From 3b60ce67203cc406e2b416321dff8f96665bf8ae Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Fri, 25 Mar 2016 22:07:16 +1300 Subject: [PATCH 01/43] fix parsing for address expressions, closes #604 --- modules/wyc/src/wyc/io/WhileyFileParser.java | 4 +--- tests/invalid/AddressExpression_Invalid_1.sysout | 5 +++++ tests/invalid/AddressExpression_Invalid_1.whiley | 10 ++++++++++ tests/invalid/AddressExpression_Invalid_2.sysout | 3 +++ tests/invalid/AddressExpression_Invalid_2.whiley | 7 +++++++ tests/valid/AddressExpression_Valid_1.whiley | 8 ++++++++ tests/valid/AddressExpression_Valid_2.whiley | 14 ++++++++++++++ tests/valid/AddressExpression_Valid_3.whiley | 8 ++++++++ tests/valid/AddressExpression_Valid_4.whiley | 14 ++++++++++++++ 9 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/invalid/AddressExpression_Invalid_1.sysout create mode 100644 tests/invalid/AddressExpression_Invalid_1.whiley create mode 100644 tests/invalid/AddressExpression_Invalid_2.sysout create mode 100644 tests/invalid/AddressExpression_Invalid_2.whiley create mode 100644 tests/valid/AddressExpression_Valid_1.whiley create mode 100644 tests/valid/AddressExpression_Valid_2.whiley create mode 100644 tests/valid/AddressExpression_Valid_3.whiley create mode 100644 tests/valid/AddressExpression_Valid_4.whiley diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java index 332d91150a..0bd3dbfe16 100644 --- a/modules/wyc/src/wyc/io/WhileyFileParser.java +++ b/modules/wyc/src/wyc/io/WhileyFileParser.java @@ -3247,10 +3247,9 @@ private Expr parseAddressExpression(WhileyFile wf, // Check whether or not parameters are supplied if (tryAndMatch(terminated, LeftBrace) != null) { // Yes, parameters are supplied! - match(LeftBrace); ArrayList parameters = new ArrayList(); boolean firstTime = true; - while (eventuallyMatch(MinusGreater) == null) { + while (eventuallyMatch(RightBrace) == null) { int p_start = index; if (!firstTime) { match(Comma); @@ -3259,7 +3258,6 @@ private Expr parseAddressExpression(WhileyFile wf, SyntacticType type = parseType(); parameters.add(type); } - match(RightBrace); return new Expr.AbstractFunctionOrMethod(id.text, parameters, sourceAttr(start, index - 1)); } else { diff --git a/tests/invalid/AddressExpression_Invalid_1.sysout b/tests/invalid/AddressExpression_Invalid_1.sysout new file mode 100644 index 0000000000..3f07e477c5 --- /dev/null +++ b/tests/invalid/AddressExpression_Invalid_1.sysout @@ -0,0 +1,5 @@ +../../tests/invalid/AddressExpression_Invalid_1.whiley:10: unable to resolve name (overloaded(...) is ambiguous + found: AddressExpression_Invalid_1:overloaded : function(int)->(int) + found: AddressExpression_Invalid_1:overloaded : function(bool)->(bool)) + fun x = &overloaded + ^^^^^^^^^^^ diff --git a/tests/invalid/AddressExpression_Invalid_1.whiley b/tests/invalid/AddressExpression_Invalid_1.whiley new file mode 100644 index 0000000000..a28be5d959 --- /dev/null +++ b/tests/invalid/AddressExpression_Invalid_1.whiley @@ -0,0 +1,10 @@ +type fun is function(int)->int + +function overloaded(int i) -> int: + return i + 1 + +function overloaded(bool b) -> bool: + return !b + +public export method test(): + fun x = &overloaded diff --git a/tests/invalid/AddressExpression_Invalid_2.sysout b/tests/invalid/AddressExpression_Invalid_2.sysout new file mode 100644 index 0000000000..86757085a3 --- /dev/null +++ b/tests/invalid/AddressExpression_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/AddressExpression_Invalid_2.whiley:7: unknown type encountered + fun x = &foo((->) + ^^ diff --git a/tests/invalid/AddressExpression_Invalid_2.whiley b/tests/invalid/AddressExpression_Invalid_2.whiley new file mode 100644 index 0000000000..72052473ae --- /dev/null +++ b/tests/invalid/AddressExpression_Invalid_2.whiley @@ -0,0 +1,7 @@ +type fun is function()->int + +function foo() -> int: + return 42 + +public export method test(): + fun x = &foo((->) diff --git a/tests/valid/AddressExpression_Valid_1.whiley b/tests/valid/AddressExpression_Valid_1.whiley new file mode 100644 index 0000000000..8198907cdc --- /dev/null +++ b/tests/valid/AddressExpression_Valid_1.whiley @@ -0,0 +1,8 @@ +type fun is function(int)->int + +function suc(int i) -> int: + return i + 1 + +public export method test(): + fun x = &suc + assume x(41) == 42 diff --git a/tests/valid/AddressExpression_Valid_2.whiley b/tests/valid/AddressExpression_Valid_2.whiley new file mode 100644 index 0000000000..65f8608726 --- /dev/null +++ b/tests/valid/AddressExpression_Valid_2.whiley @@ -0,0 +1,14 @@ +type fun1 is function(int)->int +type fun2 is function(bool)->bool + +function overloaded(int a) -> int: + return a + 1 + +function overloaded(bool a) -> bool: + return !a + +public export method test(): + fun1 x = &overloaded(int) + assume x(41) == 42 + fun2 y = &overloaded(bool) + assume y(true) == false diff --git a/tests/valid/AddressExpression_Valid_3.whiley b/tests/valid/AddressExpression_Valid_3.whiley new file mode 100644 index 0000000000..6537df6f80 --- /dev/null +++ b/tests/valid/AddressExpression_Valid_3.whiley @@ -0,0 +1,8 @@ +type fun is function(int,int)->int + +function add(int a, int b) -> int: + return a + b + +public export method test(): + fun x = &add + assume x(4, 5) == 9 diff --git a/tests/valid/AddressExpression_Valid_4.whiley b/tests/valid/AddressExpression_Valid_4.whiley new file mode 100644 index 0000000000..bc4ef8d0f6 --- /dev/null +++ b/tests/valid/AddressExpression_Valid_4.whiley @@ -0,0 +1,14 @@ +type fun1 is function(int,int)->int +type fun2 is function(bool,bool)->bool + +function overloaded(int a, int b) -> int: + return a + b + +function overloaded(bool a, bool b) -> bool: + return a && b + +public export method test(): + fun1 x = &overloaded(int,int) + assume x(4,5) == 9 + fun2 y = &overloaded(bool,bool) + assume y(true,false) == false From b10abd135a7f1cc54e4cb789a2907565fc75c267 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Fri, 25 Mar 2016 22:43:36 +1300 Subject: [PATCH 02/43] deterministic order in error message --- modules/wyc/src/wyc/builder/FlowTypeChecker.java | 14 ++++++++++---- tests/invalid/AddressExpression_Invalid_1.sysout | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/wyc/src/wyc/builder/FlowTypeChecker.java b/modules/wyc/src/wyc/builder/FlowTypeChecker.java index a6d5ceee52..572c4ad271 100644 --- a/modules/wyc/src/wyc/builder/FlowTypeChecker.java +++ b/modules/wyc/src/wyc/builder/FlowTypeChecker.java @@ -2135,11 +2135,17 @@ private Pair selectCandidateFunctionOrMethod(S candidateID = p.first(); } else if (!paramStrictSubtypes(ft, candidateType.raw())) { // this is an ambiguous error - String msg = name + parameterString(parameters) + " is ambiguous"; + StringBuilder msg = new StringBuilder(name + parameterString(parameters) + " is ambiguous"); // FIXME: should report all ambiguous matches here - msg += "\n\tfound: " + candidateID + " : " + candidateType.nominal(); - msg += "\n\tfound: " + p.first() + " : " + p.second().nominal(); - throw new ResolveError(msg); + List candidateStrings = new ArrayList(); + candidateStrings.add(candidateID + " : " + candidateType.nominal()); + candidateStrings.add(p.first() + " : " + p.second().nominal()); + Collections.sort(candidateStrings);// make error message deterministic! + for (String s : candidateStrings) { + msg.append("\n\tfound: "); + msg.append(s); + } + throw new ResolveError(msg.toString()); } } } diff --git a/tests/invalid/AddressExpression_Invalid_1.sysout b/tests/invalid/AddressExpression_Invalid_1.sysout index 3f07e477c5..c26e0232bb 100644 --- a/tests/invalid/AddressExpression_Invalid_1.sysout +++ b/tests/invalid/AddressExpression_Invalid_1.sysout @@ -1,5 +1,5 @@ ../../tests/invalid/AddressExpression_Invalid_1.whiley:10: unable to resolve name (overloaded(...) is ambiguous - found: AddressExpression_Invalid_1:overloaded : function(int)->(int) - found: AddressExpression_Invalid_1:overloaded : function(bool)->(bool)) + found: AddressExpression_Invalid_1:overloaded : function(bool)->(bool) + found: AddressExpression_Invalid_1:overloaded : function(int)->(int)) fun x = &overloaded ^^^^^^^^^^^ From 5260605a8665e7b253f4acf574f80ab46dcd42e9 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Sun, 27 Mar 2016 02:22:01 +1300 Subject: [PATCH 03/43] closes #590 --- modules/wyc/src/wyc/lang/Expr.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/wyc/src/wyc/lang/Expr.java b/modules/wyc/src/wyc/lang/Expr.java index b80ec82dad..8209281bab 100755 --- a/modules/wyc/src/wyc/lang/Expr.java +++ b/modules/wyc/src/wyc/lang/Expr.java @@ -794,6 +794,7 @@ public static class New extends SyntacticElement.Impl implements Expr,Stmt { public Nominal.Reference type; public New(Expr expr, Attribute... attributes) { + super(attributes); this.expr = expr; } From ee829b90a4630dbd8f9155fa7253fa9dbce1b541 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Sun, 27 Mar 2016 13:02:52 +1300 Subject: [PATCH 04/43] Add test cases for #608,#609,#610 These test a range of parsing related problems, such as parsing the void type, detecting duplicate functions, methods and types. They are annoying little edge cases ;) --- .../wyc/src/wyc/testing/AllInvalidTests.java | 20 ++++++++++++------- tests/invalid/ArrayAccess_Invalid_1.sysout | 3 +++ ..._1.whiley => ArrayAccess_Invalid_1.whiley} | 0 tests/invalid/ArrayAccess_Invalid_2.sysout | 3 +++ ..._2.whiley => ArrayAccess_Invalid_2.whiley} | 0 tests/invalid/ArrayAccess_Invalid_3.sysout | 4 ++++ ..._3.whiley => ArrayAccess_Invalid_3.whiley} | 0 tests/invalid/ArrayAccess_Invalid_4.sysout | 3 +++ ..._4.whiley => ArrayAccess_Invalid_4.whiley} | 0 tests/invalid/ArrayAccess_Invalid_5.sysout | 3 +++ ..._5.whiley => ArrayAccess_Invalid_5.whiley} | 0 tests/invalid/ArrayAssign_Invalid_1.sysout | 3 +++ ..._1.whiley => ArrayAssign_Invalid_1.whiley} | 0 ..._2.whiley => ArrayAssign_Invalid_2.whiley} | 0 ..._3.whiley => ArrayAssign_Invalid_3.whiley} | 0 .../invalid/ArrayConversion_Invalid_1.sysout | 4 ++++ ...hiley => ArrayConversion_Invalid_1.whiley} | 0 tests/invalid/ArrayEmpty_Invalid_1.sysout | 4 ++++ ...d_1.whiley => ArrayEmpty_Invalid_1.whiley} | 0 tests/invalid/ArrayEmpty_Invalid_2.sysout | 3 +++ ...d_2.whiley => ArrayEmpty_Invalid_2.whiley} | 0 tests/invalid/ArrayEquals_Invalid_1.sysout | 3 +++ ..._1.whiley => ArrayEquals_Invalid_1.whiley} | 0 tests/invalid/ArrayGenerator_Invalid_1.sysout | 3 +++ ...whiley => ArrayGenerator_Invalid_1.whiley} | 0 tests/invalid/ArrayGenerator_Invalid_2.sysout | 3 +++ ...whiley => ArrayGenerator_Invalid_2.whiley} | 0 tests/invalid/ArrayGenerator_Invalid_3.sysout | 3 +++ ...whiley => ArrayGenerator_Invalid_3.whiley} | 0 tests/invalid/ArrayGenerator_Invalid_4.sysout | 3 +++ ...whiley => ArrayGenerator_Invalid_4.whiley} | 0 tests/invalid/ArrayLength_Invalid_1.sysout | 3 +++ ..._1.whiley => ArrayLength_Invalid_1.whiley} | 0 tests/invalid/ArrayLength_Invalid_2.sysout | 3 +++ ..._2.whiley => ArrayLength_Invalid_2.whiley} | 0 tests/invalid/ArrayLength_Invalid_3.sysout | 3 +++ ..._3.whiley => ArrayLength_Invalid_3.whiley} | 0 ..._1.whiley => ArrayUpdate_Invalid_1.whiley} | 0 tests/invalid/Array_Invalid_1.sysout | 3 +++ ...nvalid_1.whiley => Array_Invalid_1.whiley} | 0 tests/invalid/Array_Invalid_2.sysout | 3 +++ ...nvalid_2.whiley => Array_Invalid_2.whiley} | 0 tests/invalid/Array_Invalid_3.sysout | 3 +++ ...nvalid_3.whiley => Array_Invalid_3.whiley} | 0 tests/invalid/Array_Invalid_4.sysout | 3 +++ ...nvalid_4.whiley => Array_Invalid_4.whiley} | 0 tests/invalid/Array_Invalid_5.sysout | 3 +++ ...nvalid_5.whiley => Array_Invalid_5.whiley} | 0 tests/invalid/Array_Invalid_6.sysout | 3 +++ ...nvalid_6.whiley => Array_Invalid_6.whiley} | 0 tests/invalid/Array_Invalid_7.sysout | 3 +++ ...nvalid_7.whiley => Array_Invalid_7.whiley} | 0 .../invalid/ConstrainedArray_Invalid_1.sysout | 3 +++ ...iley => ConstrainedArray_Invalid_1.whiley} | 0 .../invalid/ConstrainedArray_Invalid_2.sysout | 3 +++ ...iley => ConstrainedArray_Invalid_2.whiley} | 0 ...iley => ConstrainedArray_Invalid_3.whiley} | 0 .../invalid/ConstrainedList_Invalid_1.sysout | 3 --- .../invalid/ConstrainedList_Invalid_2.sysout | 3 --- tests/invalid/ListAccess_Invalid_1.sysout | 3 --- tests/invalid/ListAccess_Invalid_2.sysout | 3 --- tests/invalid/ListAccess_Invalid_3.sysout | 4 ---- tests/invalid/ListAccess_Invalid_4.sysout | 3 --- tests/invalid/ListAccess_Invalid_5.sysout | 3 --- tests/invalid/ListAssign_Invalid_1.sysout | 3 --- tests/invalid/ListConversion_Invalid_1.sysout | 4 ---- tests/invalid/ListEmpty_Invalid_1.sysout | 4 ---- tests/invalid/ListEmpty_Invalid_2.sysout | 3 --- tests/invalid/ListEquals_Invalid_1.sysout | 3 --- tests/invalid/ListGenerator_Invalid_1.sysout | 3 --- tests/invalid/ListGenerator_Invalid_2.sysout | 3 --- tests/invalid/ListGenerator_Invalid_3.sysout | 3 --- tests/invalid/ListGenerator_Invalid_4.sysout | 3 --- tests/invalid/ListLength_Invalid_1.sysout | 3 --- tests/invalid/ListLength_Invalid_2.sysout | 3 --- tests/invalid/ListLength_Invalid_3.sysout | 3 --- tests/invalid/List_Invalid_1.sysout | 3 --- tests/invalid/List_Invalid_2.sysout | 3 --- tests/invalid/List_Invalid_3.sysout | 3 --- tests/invalid/List_Invalid_4.sysout | 3 --- tests/invalid/List_Invalid_5.sysout | 3 --- tests/invalid/List_Invalid_6.sysout | 3 --- tests/invalid/List_Invalid_7.sysout | 3 --- tests/invalid/Parsing_Invalid_1.whiley | 7 +++++++ tests/invalid/Parsing_Invalid_10.sysout | 3 +++ tests/invalid/Parsing_Invalid_10.whiley | 2 ++ tests/invalid/Parsing_Invalid_11.sysout | 3 +++ tests/invalid/Parsing_Invalid_11.whiley | 2 ++ tests/invalid/Parsing_Invalid_12.sysout | 3 +++ tests/invalid/Parsing_Invalid_12.whiley | 8 ++++++++ tests/invalid/Parsing_Invalid_15.whiley | 2 ++ tests/invalid/Parsing_Invalid_16.sysout | 3 +++ tests/invalid/Parsing_Invalid_16.whiley | 2 ++ tests/invalid/Parsing_Invalid_2.whiley | 2 ++ tests/invalid/Parsing_Invalid_21.sysout | 3 +++ tests/invalid/Parsing_Invalid_21.whiley | 8 ++++++++ tests/invalid/Parsing_Invalid_23.sysout | 3 +++ tests/invalid/Parsing_Invalid_23.whiley | 5 +++++ tests/invalid/Parsing_Invalid_24.sysout | 3 +++ tests/invalid/Parsing_Invalid_24.whiley | 7 +++++++ tests/invalid/Parsing_Invalid_25.sysout | 3 +++ tests/invalid/Parsing_Invalid_25.whiley | 6 ++++++ tests/invalid/Parsing_Invalid_26.sysout | 3 +++ tests/invalid/Parsing_Invalid_26.whiley | 4 ++++ tests/invalid/Parsing_Invalid_27.whiley | 2 ++ tests/invalid/Parsing_Invalid_28.whiley | 3 +++ tests/invalid/Parsing_Invalid_29.sysout | 3 +++ ...lid_1.whiley => Parsing_Invalid_29.whiley} | 0 tests/invalid/Parsing_Invalid_30.sysout | 3 +++ ...lid_2.whiley => Parsing_Invalid_30.whiley} | 0 tests/invalid/Parsing_Invalid_31.whiley | 5 +++++ tests/invalid/Parsing_Invalid_4.sysout | 3 +++ tests/invalid/Parsing_Invalid_4.whiley | 4 ++++ tests/invalid/Parsing_Invalid_5.sysout | 3 +++ tests/invalid/Parsing_Invalid_5.whiley | 3 +++ tests/invalid/Parsing_Invalid_6.sysout | 3 +++ tests/invalid/Parsing_Invalid_6.whiley | 4 ++++ tests/invalid/Parsing_Invalid_8.sysout | 3 +++ tests/invalid/Parsing_Invalid_8.whiley | 2 ++ tests/invalid/Void_Invalid_3.whiley | 5 ----- 120 files changed, 217 insertions(+), 93 deletions(-) create mode 100644 tests/invalid/ArrayAccess_Invalid_1.sysout rename tests/invalid/{ListAccess_Invalid_1.whiley => ArrayAccess_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ArrayAccess_Invalid_2.sysout rename tests/invalid/{ListAccess_Invalid_2.whiley => ArrayAccess_Invalid_2.whiley} (100%) create mode 100644 tests/invalid/ArrayAccess_Invalid_3.sysout rename tests/invalid/{ListAccess_Invalid_3.whiley => ArrayAccess_Invalid_3.whiley} (100%) create mode 100644 tests/invalid/ArrayAccess_Invalid_4.sysout rename tests/invalid/{ListAccess_Invalid_4.whiley => ArrayAccess_Invalid_4.whiley} (100%) create mode 100644 tests/invalid/ArrayAccess_Invalid_5.sysout rename tests/invalid/{ListAccess_Invalid_5.whiley => ArrayAccess_Invalid_5.whiley} (100%) create mode 100644 tests/invalid/ArrayAssign_Invalid_1.sysout rename tests/invalid/{ListAssign_Invalid_1.whiley => ArrayAssign_Invalid_1.whiley} (100%) rename tests/invalid/{ListAssign_Invalid_2.whiley => ArrayAssign_Invalid_2.whiley} (100%) rename tests/invalid/{ListAssign_Invalid_3.whiley => ArrayAssign_Invalid_3.whiley} (100%) create mode 100644 tests/invalid/ArrayConversion_Invalid_1.sysout rename tests/invalid/{ListConversion_Invalid_1.whiley => ArrayConversion_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ArrayEmpty_Invalid_1.sysout rename tests/invalid/{ListEmpty_Invalid_1.whiley => ArrayEmpty_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ArrayEmpty_Invalid_2.sysout rename tests/invalid/{ListEmpty_Invalid_2.whiley => ArrayEmpty_Invalid_2.whiley} (100%) create mode 100644 tests/invalid/ArrayEquals_Invalid_1.sysout rename tests/invalid/{ListEquals_Invalid_1.whiley => ArrayEquals_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ArrayGenerator_Invalid_1.sysout rename tests/invalid/{ListGenerator_Invalid_1.whiley => ArrayGenerator_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ArrayGenerator_Invalid_2.sysout rename tests/invalid/{ListGenerator_Invalid_2.whiley => ArrayGenerator_Invalid_2.whiley} (100%) create mode 100644 tests/invalid/ArrayGenerator_Invalid_3.sysout rename tests/invalid/{ListGenerator_Invalid_3.whiley => ArrayGenerator_Invalid_3.whiley} (100%) create mode 100644 tests/invalid/ArrayGenerator_Invalid_4.sysout rename tests/invalid/{ListGenerator_Invalid_4.whiley => ArrayGenerator_Invalid_4.whiley} (100%) create mode 100644 tests/invalid/ArrayLength_Invalid_1.sysout rename tests/invalid/{ListLength_Invalid_1.whiley => ArrayLength_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ArrayLength_Invalid_2.sysout rename tests/invalid/{ListLength_Invalid_2.whiley => ArrayLength_Invalid_2.whiley} (100%) create mode 100644 tests/invalid/ArrayLength_Invalid_3.sysout rename tests/invalid/{ListLength_Invalid_3.whiley => ArrayLength_Invalid_3.whiley} (100%) rename tests/invalid/{ListUpdate_Invalid_1.whiley => ArrayUpdate_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_1.sysout rename tests/invalid/{List_Invalid_1.whiley => Array_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_2.sysout rename tests/invalid/{List_Invalid_2.whiley => Array_Invalid_2.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_3.sysout rename tests/invalid/{List_Invalid_3.whiley => Array_Invalid_3.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_4.sysout rename tests/invalid/{List_Invalid_4.whiley => Array_Invalid_4.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_5.sysout rename tests/invalid/{List_Invalid_5.whiley => Array_Invalid_5.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_6.sysout rename tests/invalid/{List_Invalid_6.whiley => Array_Invalid_6.whiley} (100%) create mode 100644 tests/invalid/Array_Invalid_7.sysout rename tests/invalid/{List_Invalid_7.whiley => Array_Invalid_7.whiley} (100%) create mode 100644 tests/invalid/ConstrainedArray_Invalid_1.sysout rename tests/invalid/{ConstrainedList_Invalid_1.whiley => ConstrainedArray_Invalid_1.whiley} (100%) create mode 100644 tests/invalid/ConstrainedArray_Invalid_2.sysout rename tests/invalid/{ConstrainedList_Invalid_2.whiley => ConstrainedArray_Invalid_2.whiley} (100%) rename tests/invalid/{ConstrainedList_Invalid_3.whiley => ConstrainedArray_Invalid_3.whiley} (100%) delete mode 100644 tests/invalid/ConstrainedList_Invalid_1.sysout delete mode 100644 tests/invalid/ConstrainedList_Invalid_2.sysout delete mode 100644 tests/invalid/ListAccess_Invalid_1.sysout delete mode 100644 tests/invalid/ListAccess_Invalid_2.sysout delete mode 100644 tests/invalid/ListAccess_Invalid_3.sysout delete mode 100644 tests/invalid/ListAccess_Invalid_4.sysout delete mode 100644 tests/invalid/ListAccess_Invalid_5.sysout delete mode 100644 tests/invalid/ListAssign_Invalid_1.sysout delete mode 100644 tests/invalid/ListConversion_Invalid_1.sysout delete mode 100644 tests/invalid/ListEmpty_Invalid_1.sysout delete mode 100644 tests/invalid/ListEmpty_Invalid_2.sysout delete mode 100644 tests/invalid/ListEquals_Invalid_1.sysout delete mode 100644 tests/invalid/ListGenerator_Invalid_1.sysout delete mode 100644 tests/invalid/ListGenerator_Invalid_2.sysout delete mode 100644 tests/invalid/ListGenerator_Invalid_3.sysout delete mode 100644 tests/invalid/ListGenerator_Invalid_4.sysout delete mode 100644 tests/invalid/ListLength_Invalid_1.sysout delete mode 100644 tests/invalid/ListLength_Invalid_2.sysout delete mode 100644 tests/invalid/ListLength_Invalid_3.sysout delete mode 100644 tests/invalid/List_Invalid_1.sysout delete mode 100644 tests/invalid/List_Invalid_2.sysout delete mode 100644 tests/invalid/List_Invalid_3.sysout delete mode 100644 tests/invalid/List_Invalid_4.sysout delete mode 100644 tests/invalid/List_Invalid_5.sysout delete mode 100644 tests/invalid/List_Invalid_6.sysout delete mode 100644 tests/invalid/List_Invalid_7.sysout create mode 100644 tests/invalid/Parsing_Invalid_1.whiley create mode 100644 tests/invalid/Parsing_Invalid_10.sysout create mode 100644 tests/invalid/Parsing_Invalid_10.whiley create mode 100644 tests/invalid/Parsing_Invalid_11.sysout create mode 100644 tests/invalid/Parsing_Invalid_11.whiley create mode 100644 tests/invalid/Parsing_Invalid_12.sysout create mode 100644 tests/invalid/Parsing_Invalid_12.whiley create mode 100644 tests/invalid/Parsing_Invalid_15.whiley create mode 100644 tests/invalid/Parsing_Invalid_16.sysout create mode 100644 tests/invalid/Parsing_Invalid_16.whiley create mode 100644 tests/invalid/Parsing_Invalid_2.whiley create mode 100644 tests/invalid/Parsing_Invalid_21.sysout create mode 100644 tests/invalid/Parsing_Invalid_21.whiley create mode 100644 tests/invalid/Parsing_Invalid_23.sysout create mode 100644 tests/invalid/Parsing_Invalid_23.whiley create mode 100644 tests/invalid/Parsing_Invalid_24.sysout create mode 100644 tests/invalid/Parsing_Invalid_24.whiley create mode 100644 tests/invalid/Parsing_Invalid_25.sysout create mode 100644 tests/invalid/Parsing_Invalid_25.whiley create mode 100644 tests/invalid/Parsing_Invalid_26.sysout create mode 100644 tests/invalid/Parsing_Invalid_26.whiley create mode 100644 tests/invalid/Parsing_Invalid_27.whiley create mode 100644 tests/invalid/Parsing_Invalid_28.whiley create mode 100644 tests/invalid/Parsing_Invalid_29.sysout rename tests/invalid/{Void_Invalid_1.whiley => Parsing_Invalid_29.whiley} (100%) create mode 100644 tests/invalid/Parsing_Invalid_30.sysout rename tests/invalid/{Void_Invalid_2.whiley => Parsing_Invalid_30.whiley} (100%) create mode 100644 tests/invalid/Parsing_Invalid_31.whiley create mode 100644 tests/invalid/Parsing_Invalid_4.sysout create mode 100644 tests/invalid/Parsing_Invalid_4.whiley create mode 100644 tests/invalid/Parsing_Invalid_5.sysout create mode 100644 tests/invalid/Parsing_Invalid_5.whiley create mode 100644 tests/invalid/Parsing_Invalid_6.sysout create mode 100644 tests/invalid/Parsing_Invalid_6.whiley create mode 100644 tests/invalid/Parsing_Invalid_8.sysout create mode 100644 tests/invalid/Parsing_Invalid_8.whiley delete mode 100644 tests/invalid/Void_Invalid_3.whiley diff --git a/modules/wyc/src/wyc/testing/AllInvalidTests.java b/modules/wyc/src/wyc/testing/AllInvalidTests.java index 252c46f974..ff9c938f37 100755 --- a/modules/wyc/src/wyc/testing/AllInvalidTests.java +++ b/modules/wyc/src/wyc/testing/AllInvalidTests.java @@ -79,8 +79,8 @@ public class AllInvalidTests { IGNORED.put("ConstrainedInt_Invalid_7", "timeout"); IGNORED.put("ConstrainedInt_Invalid_8", "timeout"); IGNORED.put("ConstrainedInt_Invalid_9", "Timeout"); - IGNORED.put("ConstrainedList_Invalid_2", "unclassified"); - IGNORED.put("ConstrainedList_Invalid_3", "unclassified"); + IGNORED.put("ConstrainedArray_Invalid_2", "unclassified"); + IGNORED.put("ConstrainedArray_Invalid_3", "unclassified"); IGNORED.put("Contractive_Invalid_1", "#425"); IGNORED.put("Contractive_Invalid_2", "unclassified"); IGNORED.put("EndOfFile_Invalid_1", "unclassified"); @@ -91,11 +91,11 @@ public class AllInvalidTests { IGNORED.put("Intersection_Invalid_1", "unclassified"); IGNORED.put("Intersection_Invalid_2", "unclassified"); IGNORED.put("Lambda_Invalid_3", "unclassified"); - IGNORED.put("ListAssign_Invalid_2", "Infinite Loop?"); - IGNORED.put("ListAssign_Invalid_3", "Infinite Loop?"); - IGNORED.put("ListEquals_Invalid_1", "unclassified"); - IGNORED.put("ListLength_Invalid_2", "Timeout"); - IGNORED.put("ListUpdate_Invalid_1", "unclassified"); + IGNORED.put("ArrayAssign_Invalid_2", "Infinite Loop?"); + IGNORED.put("ArrayAssign_Invalid_3", "Infinite Loop?"); + IGNORED.put("ArrayEquals_Invalid_1", "unclassified"); + IGNORED.put("ArrayLength_Invalid_2", "Timeout"); + IGNORED.put("ArrayUpdate_Invalid_1", "unclassified"); IGNORED.put("MethodRef_Invalid_1", "unclassified"); IGNORED.put("MethodRef_Invalid_2", "unclassified"); IGNORED.put("MethodRef_Invalid_3", "unclassified"); @@ -133,6 +133,12 @@ public class AllInvalidTests { IGNORED.put("Void_Invalid_2", "unclassified"); IGNORED.put("Void_Invalid_3", "unclassified"); IGNORED.put("While_Invalid_12", "unclassified"); + IGNORED.put("Parsing_Invalid_1", "608"); + IGNORED.put("Parsing_Invalid_2", "608"); + IGNORED.put("Parsing_Invalid_15", "609"); + IGNORED.put("Parsing_Invalid_27", "609"); + IGNORED.put("Parsing_Invalid_28", "609"); + IGNORED.put("Parsing_Invalid_31", "610"); } /** diff --git a/tests/invalid/ArrayAccess_Invalid_1.sysout b/tests/invalid/ArrayAccess_Invalid_1.sysout new file mode 100644 index 0000000000..2e5f5e3ca0 --- /dev/null +++ b/tests/invalid/ArrayAccess_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayAccess_Invalid_1.whiley:2: expected type int, found bool + return xs[y] + ^ diff --git a/tests/invalid/ListAccess_Invalid_1.whiley b/tests/invalid/ArrayAccess_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListAccess_Invalid_1.whiley rename to tests/invalid/ArrayAccess_Invalid_1.whiley diff --git a/tests/invalid/ArrayAccess_Invalid_2.sysout b/tests/invalid/ArrayAccess_Invalid_2.sysout new file mode 100644 index 0000000000..c556726bee --- /dev/null +++ b/tests/invalid/ArrayAccess_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayAccess_Invalid_2.whiley:5: index out of bounds (negative) + int z = x[-1] + ^^^^^ diff --git a/tests/invalid/ListAccess_Invalid_2.whiley b/tests/invalid/ArrayAccess_Invalid_2.whiley similarity index 100% rename from tests/invalid/ListAccess_Invalid_2.whiley rename to tests/invalid/ArrayAccess_Invalid_2.whiley diff --git a/tests/invalid/ArrayAccess_Invalid_3.sysout b/tests/invalid/ArrayAccess_Invalid_3.sysout new file mode 100644 index 0000000000..8c9b7668b3 --- /dev/null +++ b/tests/invalid/ArrayAccess_Invalid_3.sysout @@ -0,0 +1,4 @@ +../../tests/invalid/ArrayAccess_Invalid_3.whiley:11: unable to resolve name (no match for f({int data,int mode}|{int[] data,int mode}[]) + found: ArrayAccess_Invalid_3:f : function({int data,int mode}[])->({int data,int mode}[])) + return f(tups) + ^^^^^^^ diff --git a/tests/invalid/ListAccess_Invalid_3.whiley b/tests/invalid/ArrayAccess_Invalid_3.whiley similarity index 100% rename from tests/invalid/ListAccess_Invalid_3.whiley rename to tests/invalid/ArrayAccess_Invalid_3.whiley diff --git a/tests/invalid/ArrayAccess_Invalid_4.sysout b/tests/invalid/ArrayAccess_Invalid_4.sysout new file mode 100644 index 0000000000..1405993a6a --- /dev/null +++ b/tests/invalid/ArrayAccess_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayAccess_Invalid_4.whiley:3: index out of bounds (not less than length) + int y = x[0] + ^^^^ diff --git a/tests/invalid/ListAccess_Invalid_4.whiley b/tests/invalid/ArrayAccess_Invalid_4.whiley similarity index 100% rename from tests/invalid/ListAccess_Invalid_4.whiley rename to tests/invalid/ArrayAccess_Invalid_4.whiley diff --git a/tests/invalid/ArrayAccess_Invalid_5.sysout b/tests/invalid/ArrayAccess_Invalid_5.sysout new file mode 100644 index 0000000000..025fe4fad0 --- /dev/null +++ b/tests/invalid/ArrayAccess_Invalid_5.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayAccess_Invalid_5.whiley:6: index out of bounds (not less than length) + int y = x[i] + ^^^^ diff --git a/tests/invalid/ListAccess_Invalid_5.whiley b/tests/invalid/ArrayAccess_Invalid_5.whiley similarity index 100% rename from tests/invalid/ListAccess_Invalid_5.whiley rename to tests/invalid/ArrayAccess_Invalid_5.whiley diff --git a/tests/invalid/ArrayAssign_Invalid_1.sysout b/tests/invalid/ArrayAssign_Invalid_1.sysout new file mode 100644 index 0000000000..7626bdac7e --- /dev/null +++ b/tests/invalid/ArrayAssign_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayAssign_Invalid_1.whiley:6: expected type int[], found bool|int[] + xs[0] = true + ^^ diff --git a/tests/invalid/ListAssign_Invalid_1.whiley b/tests/invalid/ArrayAssign_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListAssign_Invalid_1.whiley rename to tests/invalid/ArrayAssign_Invalid_1.whiley diff --git a/tests/invalid/ListAssign_Invalid_2.whiley b/tests/invalid/ArrayAssign_Invalid_2.whiley similarity index 100% rename from tests/invalid/ListAssign_Invalid_2.whiley rename to tests/invalid/ArrayAssign_Invalid_2.whiley diff --git a/tests/invalid/ListAssign_Invalid_3.whiley b/tests/invalid/ArrayAssign_Invalid_3.whiley similarity index 100% rename from tests/invalid/ListAssign_Invalid_3.whiley rename to tests/invalid/ArrayAssign_Invalid_3.whiley diff --git a/tests/invalid/ArrayConversion_Invalid_1.sysout b/tests/invalid/ArrayConversion_Invalid_1.sysout new file mode 100644 index 0000000000..dffdeeb466 --- /dev/null +++ b/tests/invalid/ArrayConversion_Invalid_1.sysout @@ -0,0 +1,4 @@ +../../tests/invalid/ArrayConversion_Invalid_1.whiley:5: unable to resolve name (no match for f(int[]) + found: ArrayConversion_Invalid_1:f : function(byte[])->(int)) + f([1, 2, 3]) + ^^^^^^^^^^^^ diff --git a/tests/invalid/ListConversion_Invalid_1.whiley b/tests/invalid/ArrayConversion_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListConversion_Invalid_1.whiley rename to tests/invalid/ArrayConversion_Invalid_1.whiley diff --git a/tests/invalid/ArrayEmpty_Invalid_1.sysout b/tests/invalid/ArrayEmpty_Invalid_1.sysout new file mode 100644 index 0000000000..76a6bfa4d0 --- /dev/null +++ b/tests/invalid/ArrayEmpty_Invalid_1.sysout @@ -0,0 +1,4 @@ +../../tests/invalid/ArrayEmpty_Invalid_1.whiley:5: unable to resolve name (no match for f(int[]) + found: ArrayEmpty_Invalid_1:f : function(int)->(int)) + f([0;0]) + ^^^^^^^^ diff --git a/tests/invalid/ListEmpty_Invalid_1.whiley b/tests/invalid/ArrayEmpty_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListEmpty_Invalid_1.whiley rename to tests/invalid/ArrayEmpty_Invalid_1.whiley diff --git a/tests/invalid/ArrayEmpty_Invalid_2.sysout b/tests/invalid/ArrayEmpty_Invalid_2.sysout new file mode 100644 index 0000000000..b9f7c95b52 --- /dev/null +++ b/tests/invalid/ArrayEmpty_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayEmpty_Invalid_2.whiley:8: precondition not satisfied + f([0;0]) + ^^^^^^^^ diff --git a/tests/invalid/ListEmpty_Invalid_2.whiley b/tests/invalid/ArrayEmpty_Invalid_2.whiley similarity index 100% rename from tests/invalid/ListEmpty_Invalid_2.whiley rename to tests/invalid/ArrayEmpty_Invalid_2.whiley diff --git a/tests/invalid/ArrayEquals_Invalid_1.sysout b/tests/invalid/ArrayEquals_Invalid_1.sysout new file mode 100644 index 0000000000..b75bce868d --- /dev/null +++ b/tests/invalid/ArrayEquals_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayEquals_Invalid_1.whiley:8: precondition not satisfied + f([]) + ^^^^^ diff --git a/tests/invalid/ListEquals_Invalid_1.whiley b/tests/invalid/ArrayEquals_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListEquals_Invalid_1.whiley rename to tests/invalid/ArrayEquals_Invalid_1.whiley diff --git a/tests/invalid/ArrayGenerator_Invalid_1.sysout b/tests/invalid/ArrayGenerator_Invalid_1.sysout new file mode 100644 index 0000000000..fae8013d38 --- /dev/null +++ b/tests/invalid/ArrayGenerator_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayGenerator_Invalid_1.whiley:2: negative length possible + return [0; -1] + ^^^^^^^ diff --git a/tests/invalid/ListGenerator_Invalid_1.whiley b/tests/invalid/ArrayGenerator_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListGenerator_Invalid_1.whiley rename to tests/invalid/ArrayGenerator_Invalid_1.whiley diff --git a/tests/invalid/ArrayGenerator_Invalid_2.sysout b/tests/invalid/ArrayGenerator_Invalid_2.sysout new file mode 100644 index 0000000000..8c5bb83506 --- /dev/null +++ b/tests/invalid/ArrayGenerator_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayGenerator_Invalid_2.whiley:2: negative length possible + return [0; n] + ^^^^^^ diff --git a/tests/invalid/ListGenerator_Invalid_2.whiley b/tests/invalid/ArrayGenerator_Invalid_2.whiley similarity index 100% rename from tests/invalid/ListGenerator_Invalid_2.whiley rename to tests/invalid/ArrayGenerator_Invalid_2.whiley diff --git a/tests/invalid/ArrayGenerator_Invalid_3.sysout b/tests/invalid/ArrayGenerator_Invalid_3.sysout new file mode 100644 index 0000000000..c66a60911e --- /dev/null +++ b/tests/invalid/ArrayGenerator_Invalid_3.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayGenerator_Invalid_3.whiley:2: expected type int, found bool + return [0; n] + ^ diff --git a/tests/invalid/ListGenerator_Invalid_3.whiley b/tests/invalid/ArrayGenerator_Invalid_3.whiley similarity index 100% rename from tests/invalid/ListGenerator_Invalid_3.whiley rename to tests/invalid/ArrayGenerator_Invalid_3.whiley diff --git a/tests/invalid/ArrayGenerator_Invalid_4.sysout b/tests/invalid/ArrayGenerator_Invalid_4.sysout new file mode 100644 index 0000000000..8df06a3539 --- /dev/null +++ b/tests/invalid/ArrayGenerator_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayGenerator_Invalid_4.whiley:2: expected type int, found byte + return [0; n] + ^ diff --git a/tests/invalid/ListGenerator_Invalid_4.whiley b/tests/invalid/ArrayGenerator_Invalid_4.whiley similarity index 100% rename from tests/invalid/ListGenerator_Invalid_4.whiley rename to tests/invalid/ArrayGenerator_Invalid_4.whiley diff --git a/tests/invalid/ArrayLength_Invalid_1.sysout b/tests/invalid/ArrayLength_Invalid_1.sysout new file mode 100644 index 0000000000..4b42754468 --- /dev/null +++ b/tests/invalid/ArrayLength_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayLength_Invalid_1.whiley:8: assertion failed + assert |arr| == 4 + ^^^^^^^^^^ diff --git a/tests/invalid/ListLength_Invalid_1.whiley b/tests/invalid/ArrayLength_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListLength_Invalid_1.whiley rename to tests/invalid/ArrayLength_Invalid_1.whiley diff --git a/tests/invalid/ArrayLength_Invalid_2.sysout b/tests/invalid/ArrayLength_Invalid_2.sysout new file mode 100644 index 0000000000..b30c92e3a3 --- /dev/null +++ b/tests/invalid/ArrayLength_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayLength_Invalid_2.whiley:11: assertion failed + assert arr[2] != |arr| + ^^^^^^^^^^^^^^^ diff --git a/tests/invalid/ListLength_Invalid_2.whiley b/tests/invalid/ArrayLength_Invalid_2.whiley similarity index 100% rename from tests/invalid/ListLength_Invalid_2.whiley rename to tests/invalid/ArrayLength_Invalid_2.whiley diff --git a/tests/invalid/ArrayLength_Invalid_3.sysout b/tests/invalid/ArrayLength_Invalid_3.sysout new file mode 100644 index 0000000000..bcd79307b1 --- /dev/null +++ b/tests/invalid/ArrayLength_Invalid_3.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ArrayLength_Invalid_3.whiley:9: precondition not satisfied + f(-1) + ^^^^^ diff --git a/tests/invalid/ListLength_Invalid_3.whiley b/tests/invalid/ArrayLength_Invalid_3.whiley similarity index 100% rename from tests/invalid/ListLength_Invalid_3.whiley rename to tests/invalid/ArrayLength_Invalid_3.whiley diff --git a/tests/invalid/ListUpdate_Invalid_1.whiley b/tests/invalid/ArrayUpdate_Invalid_1.whiley similarity index 100% rename from tests/invalid/ListUpdate_Invalid_1.whiley rename to tests/invalid/ArrayUpdate_Invalid_1.whiley diff --git a/tests/invalid/Array_Invalid_1.sysout b/tests/invalid/Array_Invalid_1.sysout new file mode 100644 index 0000000000..6bb54546f2 --- /dev/null +++ b/tests/invalid/Array_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_1.whiley:4: variable may be uninitialised + a2[0] = x + ^^^^^ diff --git a/tests/invalid/List_Invalid_1.whiley b/tests/invalid/Array_Invalid_1.whiley similarity index 100% rename from tests/invalid/List_Invalid_1.whiley rename to tests/invalid/Array_Invalid_1.whiley diff --git a/tests/invalid/Array_Invalid_2.sysout b/tests/invalid/Array_Invalid_2.sysout new file mode 100644 index 0000000000..9cf12c9dd6 --- /dev/null +++ b/tests/invalid/Array_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_2.whiley:2: unknown variable + int x = a2[0] + ^^ diff --git a/tests/invalid/List_Invalid_2.whiley b/tests/invalid/Array_Invalid_2.whiley similarity index 100% rename from tests/invalid/List_Invalid_2.whiley rename to tests/invalid/Array_Invalid_2.whiley diff --git a/tests/invalid/Array_Invalid_3.sysout b/tests/invalid/Array_Invalid_3.sysout new file mode 100644 index 0000000000..1a5f4b66bc --- /dev/null +++ b/tests/invalid/Array_Invalid_3.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_3.whiley:3: unknown variable + int y = a2[x] + ^ diff --git a/tests/invalid/List_Invalid_3.whiley b/tests/invalid/Array_Invalid_3.whiley similarity index 100% rename from tests/invalid/List_Invalid_3.whiley rename to tests/invalid/Array_Invalid_3.whiley diff --git a/tests/invalid/Array_Invalid_4.sysout b/tests/invalid/Array_Invalid_4.sysout new file mode 100644 index 0000000000..787df64583 --- /dev/null +++ b/tests/invalid/Array_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_4.whiley:3: invalid array expression + int y = x[0] + ^ diff --git a/tests/invalid/List_Invalid_4.whiley b/tests/invalid/Array_Invalid_4.whiley similarity index 100% rename from tests/invalid/List_Invalid_4.whiley rename to tests/invalid/Array_Invalid_4.whiley diff --git a/tests/invalid/Array_Invalid_5.sysout b/tests/invalid/Array_Invalid_5.sysout new file mode 100644 index 0000000000..ecd5ae55f6 --- /dev/null +++ b/tests/invalid/Array_Invalid_5.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_5.whiley:2: expected type int[], found int + return 1 + ^ diff --git a/tests/invalid/List_Invalid_5.whiley b/tests/invalid/Array_Invalid_5.whiley similarity index 100% rename from tests/invalid/List_Invalid_5.whiley rename to tests/invalid/Array_Invalid_5.whiley diff --git a/tests/invalid/Array_Invalid_6.sysout b/tests/invalid/Array_Invalid_6.sysout new file mode 100644 index 0000000000..a8dfdc6e9a --- /dev/null +++ b/tests/invalid/Array_Invalid_6.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_6.whiley:2: expected type int, found int[] + return a + ^ diff --git a/tests/invalid/List_Invalid_6.whiley b/tests/invalid/Array_Invalid_6.whiley similarity index 100% rename from tests/invalid/List_Invalid_6.whiley rename to tests/invalid/Array_Invalid_6.whiley diff --git a/tests/invalid/Array_Invalid_7.sysout b/tests/invalid/Array_Invalid_7.sysout new file mode 100644 index 0000000000..ce0eab2bf2 --- /dev/null +++ b/tests/invalid/Array_Invalid_7.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Array_Invalid_7.whiley:3: variable may be uninitialised + r[0] = tag + ^^^^ diff --git a/tests/invalid/List_Invalid_7.whiley b/tests/invalid/Array_Invalid_7.whiley similarity index 100% rename from tests/invalid/List_Invalid_7.whiley rename to tests/invalid/Array_Invalid_7.whiley diff --git a/tests/invalid/ConstrainedArray_Invalid_1.sysout b/tests/invalid/ConstrainedArray_Invalid_1.sysout new file mode 100644 index 0000000000..12829ed9d7 --- /dev/null +++ b/tests/invalid/ConstrainedArray_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ConstrainedArray_Invalid_1.whiley:6: return type invariant not satisfied + return [x] + ^^^^^^^^^^ diff --git a/tests/invalid/ConstrainedList_Invalid_1.whiley b/tests/invalid/ConstrainedArray_Invalid_1.whiley similarity index 100% rename from tests/invalid/ConstrainedList_Invalid_1.whiley rename to tests/invalid/ConstrainedArray_Invalid_1.whiley diff --git a/tests/invalid/ConstrainedArray_Invalid_2.sysout b/tests/invalid/ConstrainedArray_Invalid_2.sysout new file mode 100644 index 0000000000..ace687c03e --- /dev/null +++ b/tests/invalid/ConstrainedArray_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/ConstrainedArray_Invalid_2.whiley:10: postcondition not satisfied + return x + ^^^^^^^^ diff --git a/tests/invalid/ConstrainedList_Invalid_2.whiley b/tests/invalid/ConstrainedArray_Invalid_2.whiley similarity index 100% rename from tests/invalid/ConstrainedList_Invalid_2.whiley rename to tests/invalid/ConstrainedArray_Invalid_2.whiley diff --git a/tests/invalid/ConstrainedList_Invalid_3.whiley b/tests/invalid/ConstrainedArray_Invalid_3.whiley similarity index 100% rename from tests/invalid/ConstrainedList_Invalid_3.whiley rename to tests/invalid/ConstrainedArray_Invalid_3.whiley diff --git a/tests/invalid/ConstrainedList_Invalid_1.sysout b/tests/invalid/ConstrainedList_Invalid_1.sysout deleted file mode 100644 index 5d824149e7..0000000000 --- a/tests/invalid/ConstrainedList_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ConstrainedList_Invalid_1.whiley:6: return type invariant not satisfied - return [x] - ^^^^^^^^^^ diff --git a/tests/invalid/ConstrainedList_Invalid_2.sysout b/tests/invalid/ConstrainedList_Invalid_2.sysout deleted file mode 100644 index cf7d63a3a9..0000000000 --- a/tests/invalid/ConstrainedList_Invalid_2.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ConstrainedList_Invalid_2.whiley:10: postcondition not satisfied - return x - ^^^^^^^^ diff --git a/tests/invalid/ListAccess_Invalid_1.sysout b/tests/invalid/ListAccess_Invalid_1.sysout deleted file mode 100644 index d23c86b4ce..0000000000 --- a/tests/invalid/ListAccess_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListAccess_Invalid_1.whiley:2: expected type int, found bool - return xs[y] - ^ diff --git a/tests/invalid/ListAccess_Invalid_2.sysout b/tests/invalid/ListAccess_Invalid_2.sysout deleted file mode 100644 index 7144eb8316..0000000000 --- a/tests/invalid/ListAccess_Invalid_2.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListAccess_Invalid_2.whiley:5: index out of bounds (negative) - int z = x[-1] - ^^^^^ diff --git a/tests/invalid/ListAccess_Invalid_3.sysout b/tests/invalid/ListAccess_Invalid_3.sysout deleted file mode 100644 index 94b02b603a..0000000000 --- a/tests/invalid/ListAccess_Invalid_3.sysout +++ /dev/null @@ -1,4 +0,0 @@ -../../tests/invalid/ListAccess_Invalid_3.whiley:11: unable to resolve name (no match for f({int data,int mode}|{int[] data,int mode}[]) - found: ListAccess_Invalid_3:f : function({int data,int mode}[])->({int data,int mode}[])) - return f(tups) - ^^^^^^^ diff --git a/tests/invalid/ListAccess_Invalid_4.sysout b/tests/invalid/ListAccess_Invalid_4.sysout deleted file mode 100644 index 923c81e683..0000000000 --- a/tests/invalid/ListAccess_Invalid_4.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListAccess_Invalid_4.whiley:3: index out of bounds (not less than length) - int y = x[0] - ^^^^ diff --git a/tests/invalid/ListAccess_Invalid_5.sysout b/tests/invalid/ListAccess_Invalid_5.sysout deleted file mode 100644 index 6b276b7e15..0000000000 --- a/tests/invalid/ListAccess_Invalid_5.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListAccess_Invalid_5.whiley:6: index out of bounds (not less than length) - int y = x[i] - ^^^^ diff --git a/tests/invalid/ListAssign_Invalid_1.sysout b/tests/invalid/ListAssign_Invalid_1.sysout deleted file mode 100644 index ebb0502a93..0000000000 --- a/tests/invalid/ListAssign_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListAssign_Invalid_1.whiley:6: expected type int[], found bool|int[] - xs[0] = true - ^^ diff --git a/tests/invalid/ListConversion_Invalid_1.sysout b/tests/invalid/ListConversion_Invalid_1.sysout deleted file mode 100644 index 7604b20d4e..0000000000 --- a/tests/invalid/ListConversion_Invalid_1.sysout +++ /dev/null @@ -1,4 +0,0 @@ -../../tests/invalid/ListConversion_Invalid_1.whiley:5: unable to resolve name (no match for f(int[]) - found: ListConversion_Invalid_1:f : function(byte[])->(int)) - f([1, 2, 3]) - ^^^^^^^^^^^^ diff --git a/tests/invalid/ListEmpty_Invalid_1.sysout b/tests/invalid/ListEmpty_Invalid_1.sysout deleted file mode 100644 index 2298461f5c..0000000000 --- a/tests/invalid/ListEmpty_Invalid_1.sysout +++ /dev/null @@ -1,4 +0,0 @@ -../../tests/invalid/ListEmpty_Invalid_1.whiley:5: unable to resolve name (no match for f(int[]) - found: ListEmpty_Invalid_1:f : function(int)->(int)) - f([0;0]) - ^^^^^^^^ diff --git a/tests/invalid/ListEmpty_Invalid_2.sysout b/tests/invalid/ListEmpty_Invalid_2.sysout deleted file mode 100644 index ddaffad26c..0000000000 --- a/tests/invalid/ListEmpty_Invalid_2.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListEmpty_Invalid_2.whiley:8: precondition not satisfied - f([0;0]) - ^^^^^^^^ diff --git a/tests/invalid/ListEquals_Invalid_1.sysout b/tests/invalid/ListEquals_Invalid_1.sysout deleted file mode 100644 index 00ade7419f..0000000000 --- a/tests/invalid/ListEquals_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListEquals_Invalid_1.whiley:8: precondition not satisfied - f([]) - ^^^^^ diff --git a/tests/invalid/ListGenerator_Invalid_1.sysout b/tests/invalid/ListGenerator_Invalid_1.sysout deleted file mode 100644 index 5dd24f9801..0000000000 --- a/tests/invalid/ListGenerator_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListGenerator_Invalid_1.whiley:2: negative length possible - return [0; -1] - ^^^^^^^ diff --git a/tests/invalid/ListGenerator_Invalid_2.sysout b/tests/invalid/ListGenerator_Invalid_2.sysout deleted file mode 100644 index 66362fe0a5..0000000000 --- a/tests/invalid/ListGenerator_Invalid_2.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListGenerator_Invalid_2.whiley:2: negative length possible - return [0; n] - ^^^^^^ diff --git a/tests/invalid/ListGenerator_Invalid_3.sysout b/tests/invalid/ListGenerator_Invalid_3.sysout deleted file mode 100644 index b8a81beefe..0000000000 --- a/tests/invalid/ListGenerator_Invalid_3.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListGenerator_Invalid_3.whiley:2: expected type int, found bool - return [0; n] - ^ diff --git a/tests/invalid/ListGenerator_Invalid_4.sysout b/tests/invalid/ListGenerator_Invalid_4.sysout deleted file mode 100644 index 05de2d1c2c..0000000000 --- a/tests/invalid/ListGenerator_Invalid_4.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListGenerator_Invalid_4.whiley:2: expected type int, found byte - return [0; n] - ^ diff --git a/tests/invalid/ListLength_Invalid_1.sysout b/tests/invalid/ListLength_Invalid_1.sysout deleted file mode 100644 index 63efc44d97..0000000000 --- a/tests/invalid/ListLength_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListLength_Invalid_1.whiley:8: assertion failed - assert |arr| == 4 - ^^^^^^^^^^ diff --git a/tests/invalid/ListLength_Invalid_2.sysout b/tests/invalid/ListLength_Invalid_2.sysout deleted file mode 100644 index b69feab2b2..0000000000 --- a/tests/invalid/ListLength_Invalid_2.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListLength_Invalid_2.whiley:11: assertion failed - assert arr[2] != |arr| - ^^^^^^^^^^^^^^^ diff --git a/tests/invalid/ListLength_Invalid_3.sysout b/tests/invalid/ListLength_Invalid_3.sysout deleted file mode 100644 index b8c26f9d62..0000000000 --- a/tests/invalid/ListLength_Invalid_3.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/ListLength_Invalid_3.whiley:9: precondition not satisfied - f(-1) - ^^^^^ diff --git a/tests/invalid/List_Invalid_1.sysout b/tests/invalid/List_Invalid_1.sysout deleted file mode 100644 index 0ccaf62460..0000000000 --- a/tests/invalid/List_Invalid_1.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_1.whiley:4: variable may be uninitialised - a2[0] = x - ^^^^^ diff --git a/tests/invalid/List_Invalid_2.sysout b/tests/invalid/List_Invalid_2.sysout deleted file mode 100644 index 8eea1d46bb..0000000000 --- a/tests/invalid/List_Invalid_2.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_2.whiley:2: unknown variable - int x = a2[0] - ^^ diff --git a/tests/invalid/List_Invalid_3.sysout b/tests/invalid/List_Invalid_3.sysout deleted file mode 100644 index a059bfc952..0000000000 --- a/tests/invalid/List_Invalid_3.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_3.whiley:3: unknown variable - int y = a2[x] - ^ diff --git a/tests/invalid/List_Invalid_4.sysout b/tests/invalid/List_Invalid_4.sysout deleted file mode 100644 index 5ba651dc2c..0000000000 --- a/tests/invalid/List_Invalid_4.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_4.whiley:3: invalid array expression - int y = x[0] - ^ diff --git a/tests/invalid/List_Invalid_5.sysout b/tests/invalid/List_Invalid_5.sysout deleted file mode 100644 index 3801c94f87..0000000000 --- a/tests/invalid/List_Invalid_5.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_5.whiley:2: expected type int[], found int - return 1 - ^ diff --git a/tests/invalid/List_Invalid_6.sysout b/tests/invalid/List_Invalid_6.sysout deleted file mode 100644 index 3861d4d221..0000000000 --- a/tests/invalid/List_Invalid_6.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_6.whiley:2: expected type int, found int[] - return a - ^ diff --git a/tests/invalid/List_Invalid_7.sysout b/tests/invalid/List_Invalid_7.sysout deleted file mode 100644 index 8b3b2bc7ac..0000000000 --- a/tests/invalid/List_Invalid_7.sysout +++ /dev/null @@ -1,3 +0,0 @@ -../../tests/invalid/List_Invalid_7.whiley:3: variable may be uninitialised - r[0] = tag - ^^^^ diff --git a/tests/invalid/Parsing_Invalid_1.whiley b/tests/invalid/Parsing_Invalid_1.whiley new file mode 100644 index 0000000000..8bb19edabf --- /dev/null +++ b/tests/invalid/Parsing_Invalid_1.whiley @@ -0,0 +1,7 @@ +function f(int x) -> (int r): + // + return x + +function f(int y) -> (int r): + // + return y diff --git a/tests/invalid/Parsing_Invalid_10.sysout b/tests/invalid/Parsing_Invalid_10.sysout new file mode 100644 index 0000000000..fa7c4e44b5 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_10.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_10.whiley:2: break outside switch or loop + break + ^^^^^ diff --git a/tests/invalid/Parsing_Invalid_10.whiley b/tests/invalid/Parsing_Invalid_10.whiley new file mode 100644 index 0000000000..f78c606550 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_10.whiley @@ -0,0 +1,2 @@ +method f(): + break diff --git a/tests/invalid/Parsing_Invalid_11.sysout b/tests/invalid/Parsing_Invalid_11.sysout new file mode 100644 index 0000000000..104ca9dd32 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_11.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_11.whiley:2: continue outside loop + continue + ^^^^^^^^ diff --git a/tests/invalid/Parsing_Invalid_11.whiley b/tests/invalid/Parsing_Invalid_11.whiley new file mode 100644 index 0000000000..6f25c2e1ae --- /dev/null +++ b/tests/invalid/Parsing_Invalid_11.whiley @@ -0,0 +1,2 @@ +method f(): + continue diff --git a/tests/invalid/Parsing_Invalid_12.sysout b/tests/invalid/Parsing_Invalid_12.sysout new file mode 100644 index 0000000000..4b8dfee6be --- /dev/null +++ b/tests/invalid/Parsing_Invalid_12.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_12.whiley:5: duplicate case label + case 1: +^^^^^^^^^^^^ diff --git a/tests/invalid/Parsing_Invalid_12.whiley b/tests/invalid/Parsing_Invalid_12.whiley new file mode 100644 index 0000000000..10ccf3fdc9 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_12.whiley @@ -0,0 +1,8 @@ +function f(int x) -> (int r): + switch(x): + case 1: + return 0 + case 1: + return 1 + // + return 0 \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_15.whiley b/tests/invalid/Parsing_Invalid_15.whiley new file mode 100644 index 0000000000..a5376a800f --- /dev/null +++ b/tests/invalid/Parsing_Invalid_15.whiley @@ -0,0 +1,2 @@ +method f(): + void x \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_16.sysout b/tests/invalid/Parsing_Invalid_16.sysout new file mode 100644 index 0000000000..cca659a039 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_16.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_16.whiley:2: duplicate tuple key + return {x: 1, x: 2, y: 3} + ^ diff --git a/tests/invalid/Parsing_Invalid_16.whiley b/tests/invalid/Parsing_Invalid_16.whiley new file mode 100644 index 0000000000..4de3fa711a --- /dev/null +++ b/tests/invalid/Parsing_Invalid_16.whiley @@ -0,0 +1,2 @@ +function f() -> {int x, int y}: + return {x: 1, x: 2, y: 3} \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_2.whiley b/tests/invalid/Parsing_Invalid_2.whiley new file mode 100644 index 0000000000..8cac9431fc --- /dev/null +++ b/tests/invalid/Parsing_Invalid_2.whiley @@ -0,0 +1,2 @@ +type nat is int +type nat is {int x, int y} diff --git a/tests/invalid/Parsing_Invalid_21.sysout b/tests/invalid/Parsing_Invalid_21.sysout new file mode 100644 index 0000000000..f4c9c5a080 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_21.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_21.whiley:8: unknown variable + return x + ^ diff --git a/tests/invalid/Parsing_Invalid_21.whiley b/tests/invalid/Parsing_Invalid_21.whiley new file mode 100644 index 0000000000..f828010a73 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_21.whiley @@ -0,0 +1,8 @@ +function f() -> (int r): + int i = 0 + // + while(i < 10): + int x = 0 + i = i + 1 + // + return x \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_23.sysout b/tests/invalid/Parsing_Invalid_23.sysout new file mode 100644 index 0000000000..9e74e287f5 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_23.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_23.whiley:5: unknown variable + return y + ^ diff --git a/tests/invalid/Parsing_Invalid_23.whiley b/tests/invalid/Parsing_Invalid_23.whiley new file mode 100644 index 0000000000..d0ec354c38 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_23.whiley @@ -0,0 +1,5 @@ +function f(int x): + if(x >= 0): + int y = x + 1 + // + return y diff --git a/tests/invalid/Parsing_Invalid_24.sysout b/tests/invalid/Parsing_Invalid_24.sysout new file mode 100644 index 0000000000..a7af3d1a5f --- /dev/null +++ b/tests/invalid/Parsing_Invalid_24.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_24.whiley:7: unknown variable + return y + ^ diff --git a/tests/invalid/Parsing_Invalid_24.whiley b/tests/invalid/Parsing_Invalid_24.whiley new file mode 100644 index 0000000000..c820d73df0 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_24.whiley @@ -0,0 +1,7 @@ +function f(int x) -> (int r): + if(x >= 0): + int y = x + 1 + else: + int y = 0 + // + return y diff --git a/tests/invalid/Parsing_Invalid_25.sysout b/tests/invalid/Parsing_Invalid_25.sysout new file mode 100644 index 0000000000..6f28b8d0e0 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_25.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_25.whiley:6: unknown variable + return x + ^ diff --git a/tests/invalid/Parsing_Invalid_25.whiley b/tests/invalid/Parsing_Invalid_25.whiley new file mode 100644 index 0000000000..e994a27c6e --- /dev/null +++ b/tests/invalid/Parsing_Invalid_25.whiley @@ -0,0 +1,6 @@ +function f(int y): + switch(y): + default: + int x = 0 + // + return x diff --git a/tests/invalid/Parsing_Invalid_26.sysout b/tests/invalid/Parsing_Invalid_26.sysout new file mode 100644 index 0000000000..5ef7c429ef --- /dev/null +++ b/tests/invalid/Parsing_Invalid_26.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_26.whiley:1: empty type encountered +type Record is { +^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Parsing_Invalid_26.whiley b/tests/invalid/Parsing_Invalid_26.whiley new file mode 100644 index 0000000000..5a94741ee8 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_26.whiley @@ -0,0 +1,4 @@ +type Record is { + int x, + void y +} \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_27.whiley b/tests/invalid/Parsing_Invalid_27.whiley new file mode 100644 index 0000000000..ef7921292c --- /dev/null +++ b/tests/invalid/Parsing_Invalid_27.whiley @@ -0,0 +1,2 @@ +function f(void[] xs) -> (int r): + return 1 \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_28.whiley b/tests/invalid/Parsing_Invalid_28.whiley new file mode 100644 index 0000000000..1c63c5bd6d --- /dev/null +++ b/tests/invalid/Parsing_Invalid_28.whiley @@ -0,0 +1,3 @@ +type Rec is { + void[] xs +} diff --git a/tests/invalid/Parsing_Invalid_29.sysout b/tests/invalid/Parsing_Invalid_29.sysout new file mode 100644 index 0000000000..c0de043d53 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_29.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_29.whiley:1: empty type encountered +function f(void z) : + ^^^^ diff --git a/tests/invalid/Void_Invalid_1.whiley b/tests/invalid/Parsing_Invalid_29.whiley similarity index 100% rename from tests/invalid/Void_Invalid_1.whiley rename to tests/invalid/Parsing_Invalid_29.whiley diff --git a/tests/invalid/Parsing_Invalid_30.sysout b/tests/invalid/Parsing_Invalid_30.sysout new file mode 100644 index 0000000000..e675814775 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_30.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_30.whiley:1: empty type encountered +function f(int x, void z) : + ^^^^ diff --git a/tests/invalid/Void_Invalid_2.whiley b/tests/invalid/Parsing_Invalid_30.whiley similarity index 100% rename from tests/invalid/Void_Invalid_2.whiley rename to tests/invalid/Parsing_Invalid_30.whiley diff --git a/tests/invalid/Parsing_Invalid_31.whiley b/tests/invalid/Parsing_Invalid_31.whiley new file mode 100644 index 0000000000..e90bd44bd4 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_31.whiley @@ -0,0 +1,5 @@ +method m() : + int b = 1 + +method main() : + int a = m() diff --git a/tests/invalid/Parsing_Invalid_4.sysout b/tests/invalid/Parsing_Invalid_4.sysout new file mode 100644 index 0000000000..fee74c3c70 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_4.whiley:3: duplicate record key + int field + ^^^^^ diff --git a/tests/invalid/Parsing_Invalid_4.whiley b/tests/invalid/Parsing_Invalid_4.whiley new file mode 100644 index 0000000000..af7b344f1b --- /dev/null +++ b/tests/invalid/Parsing_Invalid_4.whiley @@ -0,0 +1,4 @@ +type rec is { + int field, + int field +} \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_5.sysout b/tests/invalid/Parsing_Invalid_5.sysout new file mode 100644 index 0000000000..6fc18f1769 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_5.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_5.whiley:2: variable already declared + int x = x + ^^^^^ diff --git a/tests/invalid/Parsing_Invalid_5.whiley b/tests/invalid/Parsing_Invalid_5.whiley new file mode 100644 index 0000000000..77a4faff62 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_5.whiley @@ -0,0 +1,3 @@ +function f(int x) -> (int r): + int x = x + return x \ No newline at end of file diff --git a/tests/invalid/Parsing_Invalid_6.sysout b/tests/invalid/Parsing_Invalid_6.sysout new file mode 100644 index 0000000000..ba47ce4e03 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_6.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_6.whiley:3: variable already declared + int x = x + ^^^^^ diff --git a/tests/invalid/Parsing_Invalid_6.whiley b/tests/invalid/Parsing_Invalid_6.whiley new file mode 100644 index 0000000000..fd7cc6c154 --- /dev/null +++ b/tests/invalid/Parsing_Invalid_6.whiley @@ -0,0 +1,4 @@ +function f(int y) -> (int r): + int x = y + int x = x + return x diff --git a/tests/invalid/Parsing_Invalid_8.sysout b/tests/invalid/Parsing_Invalid_8.sysout new file mode 100644 index 0000000000..03fce693bf --- /dev/null +++ b/tests/invalid/Parsing_Invalid_8.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Parsing_Invalid_8.whiley:2: unknown variable + x = 1 + ^ diff --git a/tests/invalid/Parsing_Invalid_8.whiley b/tests/invalid/Parsing_Invalid_8.whiley new file mode 100644 index 0000000000..01f586e12b --- /dev/null +++ b/tests/invalid/Parsing_Invalid_8.whiley @@ -0,0 +1,2 @@ +function f() -> (int r): + x = 1 \ No newline at end of file diff --git a/tests/invalid/Void_Invalid_3.whiley b/tests/invalid/Void_Invalid_3.whiley deleted file mode 100644 index 7517071297..0000000000 --- a/tests/invalid/Void_Invalid_3.whiley +++ /dev/null @@ -1,5 +0,0 @@ -method method() : - b = 1 - -method main() : - a = method() From 84e5aae79d46dc7e0903399aa66062a119e5c8cd Mon Sep 17 00:00:00 2001 From: DavePearce Date: Mon, 1 Feb 2016 19:45:19 +1300 Subject: [PATCH 05/43] Remove AttributedCodeBlock #474 This removes the notion of AttributedCodeBlock, Attribute.Map and AbstractAttributeMap. --- modules/wyil/src/wyil/lang/Attribute.java | 68 +--- modules/wyil/src/wyil/lang/CodeBlock.java | 219 +--------- .../src/wyil/util/AbstractAttributeMap.java | 221 ---------- .../src/wyil/util/AttributedCodeBlock.java | 378 ------------------ 4 files changed, 2 insertions(+), 884 deletions(-) delete mode 100644 modules/wyil/src/wyil/util/AbstractAttributeMap.java delete mode 100644 modules/wyil/src/wyil/util/AttributedCodeBlock.java diff --git a/modules/wyil/src/wyil/lang/Attribute.java b/modules/wyil/src/wyil/lang/Attribute.java index 685488d90e..eb820591c5 100644 --- a/modules/wyil/src/wyil/lang/Attribute.java +++ b/modules/wyil/src/wyil/lang/Attribute.java @@ -14,71 +14,5 @@ * */ public interface Attribute { - - /** - *

- * An attribute map is an attribute which specifically associates meta-data - * with individual bytecodes, as opposed to other attributes which - * associated data with larger constructs (e.g. methods, functions, types, - * etc). - *

- * - *

- * The primary reason for distinguishing between attributes and attribute - * maps is that, in the latter, we want to enable the safe modification of - * bytecodes (e.g. inserting, deleting, replacing, etc). When such - * operations occur, an attribute map must be informed to ensure that the - * mapping is correctly preserved. - *

- * - * - * @author David J. Pearce - * - */ - public interface Map extends Attribute { - /** - * Returns the type of attribute that this attribute map handles. - * - * @return - */ - Class type(); - - /** - * Get the meta-data associated with a given bytecode location. Here, a - * location is a n-dimensional numeric identifier to capture bytecodes - * which may be nested inside other bytecodes. - * - * @param location - * The location to be updated. - * @return The data associated with the given location, or null if no - * such data exists. - */ - T get(CodeBlock.Index location); - - /** - * Assign or update the meta-data associated with a given bytecode - * location. Here, a location is a n-dimensional numeric identifier to - * capture bytecodes which may be nested inside other bytecodes. - * - * @param location - * The location to be updated - * @param data - * The data to assign to the given location - */ - void put(CodeBlock.Index location, T data); - - /** - * Insert meta-data associated with a given bytecode a given location. - * Here, a location is a n-dimensional numeric identifier to capture - * bytecodes which may be nested inside other bytecodes. Note that this - * will "shift down" all identifies which logically follow the given - * identify in its block, and those contained. - * - * @param location - * The location to be insert - * @param data - * The data to assign to the given location - */ - void insert(CodeBlock.Index location, T data); - } + } diff --git a/modules/wyil/src/wyil/lang/CodeBlock.java b/modules/wyil/src/wyil/lang/CodeBlock.java index ce9e3d08d9..6c3b0b482a 100644 --- a/modules/wyil/src/wyil/lang/CodeBlock.java +++ b/modules/wyil/src/wyil/lang/CodeBlock.java @@ -100,28 +100,7 @@ public Code get(Index index) { } return iterator.get(indexArray[i]); } - - /** - * Check whether a given index is contained within this block. That is, - * whether or not a bytecode exists at the given index. - * - * @param parent - * @return - */ - public boolean contains(Index index) { - int[] indexArray = index.toArray(); - CodeBlock iterator = this; - int i = 0; - while (i < indexArray.length - 1) { - int j = indexArray[i]; - if(j >= iterator.size()) { - return false; - } - iterator = (CodeBlock) iterator.get(j); - i = i + 1; - } - return indexArray[i] < iterator.size(); - } + /** * Returns a reference to the internal bytecode array. Note that this list * is not intended to be modified. @@ -194,200 +173,4 @@ public void set(int index, Code code) { public Code remove(int index) { return bytecodes.remove(index); } - - /** - * Provides a mechanism for identifying bytecodes within arbitrarily nested - * blocks. Specifically, an index is n-dimensional sequence of integer - * indices (e.g. 0.1.3) which uniquely identifies a location within a nested - * block. Bytecode indices are read left-to-right with the leftmost index - * corresponding to the index in the outermost block. - * - * @author David J. Pearce - * - */ - public static class Index { - private final Index parent; // null only for ROOT index - private final int value; - - public static final Index ROOT = null; - - /** - * Construct an index which is nested within the parent index, and whose - * initial value at the innermost level is 0. For example, "0.1.0" would - * be created from parent "0.1". - * - * @param parent - * Parent index, which maybe null if there is no parent. - */ - public Index(Index parent) { - this.parent = parent; - this.value = 0; - } - - /** - * Construct an index which is nested within the parent index, and whose - * initial value at the innermost level is determined by a given - * parameter. For example, "0.1.3" would be created from parent "0.1" - * and value 3. - * - * @param parent - * Parent index, which maybe null if there is no parent. - * @param value - * Value of innermost component - * - */ - public Index(Index parent, int value) { - this.parent = parent; - this.value = value; - } - - /** - * Return the next index in sequence. - * - * @return - */ - public Index next() { - return new Index(parent, value + 1); - } - - /** - * Return the next index in sequence. - * - * @return - */ - public Index next(int n) { - return new Index(parent, value + n); - } - - public Index parent() { - return parent; - } - - /** - * Prepend a given index onto the front of this index; - * - * @param index - * Index to be prepended. May be null, in which case this - * operation has no effect. - * @return - */ - public Index prepend(CodeBlock.Index index) { - if(index == null) { - return this; - } else { - int[] indices = toArray(); - for(int i=0;i!=indices.length;++i) { - index = new Index(index,indices[i]); - } - return index; - } - } - - /** - * Append a given index onto the end of this index; - * - * @param index - * Index to be appended. Must not be null - * @return - */ - public Index append(CodeBlock.Index index) { - int[] indices = index.toArray(); - CodeBlock.Index nIndex = this; - for(int i=0;i!=indices.length;++i) { - nIndex = new Index(nIndex,indices[i]); - } - return nIndex; - } - - /** - * Return the first index nested within this index. - * - * @return - */ - public Index firstWithin() { - return new Index(this); - } - - /** - * Return the first index nested within this index. - * - * @return - */ - public Index nestedWithin(int value) { - return new Index(this,value); - } - - /** - * Check whether this index is contained within a given parent index. In - * otherwords, that the parent index is a prefix of this index. - * - * @param parent - * @return - */ - public boolean isWithin(Index parent) { - if(parent == null) { return true; } - - int[] me = toArray(); - int[] pnt = parent.toArray(); - - if(me.length < pnt.length) { - return false; - } else { - for(int i=0;i!=pnt.length;++i) { - if(me[i] != pnt[i]) { - return false; - } - } - return true; - } - } - - public int size() { - if(parent == null) { - return 1; - } else { - return 1 + parent.size(); - } - } - - public boolean equals(Object o) { - if (o instanceof Index) { - Index i = (Index) o; - if (parent == null) { - return value == i.value && i.parent == null; - } else { - return value == i.value && parent.equals(i.parent); - } - } - return false; - } - - public int hashCode() { - if(parent == null) { - return value; - } else { - return parent.hashCode() ^ value; - } - } - - public int[] toArray() { - Index iterator = this; - int[] r = new int[size()]; - for(int i = r.length;i > 0;) { - i = i - 1; - r[i] = iterator.value; - iterator = iterator.parent; - } - return r; - } - - public String toString() { - String vstr = Integer.toString(value); - if (parent == null) { - return vstr; - } else { - return parent.toString() + "." + vstr; - } - } - } } \ No newline at end of file diff --git a/modules/wyil/src/wyil/util/AbstractAttributeMap.java b/modules/wyil/src/wyil/util/AbstractAttributeMap.java deleted file mode 100644 index 4be37f17fc..0000000000 --- a/modules/wyil/src/wyil/util/AbstractAttributeMap.java +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2014, David J. Pearce (djp@ecs.vuw.ac.nz) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package wyil.util; - -import java.util.ArrayList; - -import wyil.lang.Attribute; -import wyil.lang.CodeBlock; - -/** - * Provides a generic implementation of Attribute.Map which can be - * used as a starting point for constructing specific instances of - * Attribute.Map. The primary objective of this class is to - * associate entries with given (nested) bytecode locations in the presence of - * insertions and deletions. - * - * @author David J. Pearce - * - */ -public class AbstractAttributeMap { - private final Block root = new Block(); - - /** - * Get the attribute associated with a given bytecode location. If the - * location doesn't exist, then an exception is raised. - * - * @param location - * Location to look for attribute. Cannot be an array of size 0. - * @return - */ - public T get(CodeBlock.Index location) { - Block blk = root; - Entry e = null; - int[] components = location.toArray(); - - for(int i = 0; i!=components.length;++i) { - int loc = components[i]; - - if(loc >= blk.entries.size()) { - return null; - } - - // Second, examine the entry at the specified location. - e = blk.entries.get(loc); - - if(e instanceof Block) { - blk = (Block) e; - } else if(e == null) { - // This indicate an invalid location identifier because the - // location exists, but is not of the right kind. - return null; - } - } - - // Finally, set the attribute - return e.attribute; - } - - /** - * Associate an attribute with a given bytecode location. If the location - * doesn't exist, then it is created. However, if the location is invalid, - * then an exception will still be raised. - * - * @param location - * Location to for attribute to update. Cannot be an array of - * size 0. - * @return - */ - public void put(CodeBlock.Index location, T data) { - Block blk = root; - Entry e = null; - - int[] components = location.toArray(); - - for(int i = 0; i!=components.length;++i) { - int loc = components[i]; - - // First, make sure there is enough room for this location! - blk.ensureSize(loc+1); - - // Second, examine the entry at the specified location. - e = blk.entries.get(loc); - - if(e instanceof Block) { - blk = (Block) e; - } else if(e == null) { - // Location doesn't exist, so create it. - Block nblk = new Block(); - blk.entries.set(loc,nblk); - blk = nblk; - e = blk; - } else { - // This indicate an invalid location identifier because the - // location exists, but is not of the right kind. - throw new IllegalArgumentException("invalid entry identifier"); - } - } - - // Finally, set the attribute - e.attribute = data; - } - - /** - * Insert meta-data associated with a given bytecode a given location. - * Here, a location is a n-dimensional numeric identifier to capture - * bytecodes which may be nested inside other bytecodes. Note that this - * will "shift down" all identifies which logically follow the given - * identify in its block, and those contained. - * - * @param location - * The location to be insert - * @param data - * The data to assign to the given location - */ - public void insert(CodeBlock.Index location, T data) { - Block blk = root; - - int[] components = location.toArray(); - - for(int i = 0; i!=components.length-1;++i) { - int loc = components[i]; - - // First, make sure there is enough room for this location! - blk.ensureSize(loc+1); - - // Second, examine the entry at the specified location. - Entry e = blk.entries.get(loc); - - if(e instanceof Block) { - blk = (Block) e; - } else if(e == null) { - // Location doesn't exist, so create it. - Block nblk = new Block(); - blk.entries.set(loc,nblk); - blk = nblk; - e = blk; - } else { - // This indicate an invalid location identifier because the - // location exists, but is not of the right kind. - throw new IllegalArgumentException("invalid entry identifier"); - } - } - - int insertionPoint = components[components.length-1]; - Entry e = new Block(); - e.attribute = data; - blk.ensureSize(insertionPoint); // ensure there is enough space - blk.entries.add(insertionPoint,e); - } - - public void print() { - print(root); - } - - private static class Entry { - public T attribute; - - public Entry(T attribute) { - this.attribute = attribute; - } - } - - private static class Block extends Entry { - private final ArrayList> entries = new ArrayList>(); - - public Block() { - super(null); - } - - public Block(T attribute) { - super(attribute); - } - - public void ensureSize(int minSize) { - for(int i=entries.size();i blk) { - print("",blk); - } - - public static void print(String prefix, Block blk) { - for(int i=0;i!=blk.entries.size();++i) { - Entry e = blk.entries.get(i); - if(e == null) { - System.out.println(prefix + "." + i + " [null]"); - } else { - System.out.println(prefix + "." + i + " = " + e.attribute); - } - if(e instanceof Block) { - print(prefix + "." + i,(Block)e); - } - } - } -} diff --git a/modules/wyil/src/wyil/util/AttributedCodeBlock.java b/modules/wyil/src/wyil/util/AttributedCodeBlock.java deleted file mode 100644 index 43b6e79fff..0000000000 --- a/modules/wyil/src/wyil/util/AttributedCodeBlock.java +++ /dev/null @@ -1,378 +0,0 @@ -package wyil.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import wyil.lang.Attribute; -import wyil.lang.Code; -import wyil.lang.CodeBlock; -import wyil.lang.CodeBlock.Index; - -/** - *

- * An attributed code block represents a bytecode block where each bytecode can - * be attributed with a pre-defined set of attributes. The block maintains an - * Attribute.Map for each different kind of attribute, and ensures - * that these maps are maintained in the presence of insertions, deletions and - * updates. - *

- * - *

- * NOTE: an attributed block should never be used as part of an actual bytecode. - * This is because it does not (indeed, cannot) implement the necessary equality - * functions. - *

- * - * @author David J. Pearce - * - */ -public class AttributedCodeBlock extends CodeBlock { - - /** - * The map from attribute kinds to the intances of Attribute.Map responsible - * for storing them. - */ - private Map, Attribute.Map> attributes; - - /** - * The ID of this block. For root blocks, this is empty (i.e. null). - * However, for nested blocks, this is the ID of the enclosing bytecode - * instruction. - */ - private final CodeBlock.Index ID; - - public AttributedCodeBlock( - Attribute.Map... attributeMaps) { - this.attributes = new HashMap, wyil.lang.Attribute.Map>(); - for (Attribute.Map map : attributeMaps) { - this.attributes.put(map.type(), map); - } - this.ID = null; - } - - public AttributedCodeBlock( - Collection> attributeMaps) { - this.attributes = new HashMap, wyil.lang.Attribute.Map>(); - for (Attribute.Map map : attributeMaps) { - this.attributes.put(map.type(), map); - } - this.ID = null; - } - - public AttributedCodeBlock(Collection bytecodes, - Attribute.Map... attributeMaps) { - super(bytecodes); - this.attributes = new HashMap, wyil.lang.Attribute.Map>(); - for (Attribute.Map map : attributeMaps) { - this.attributes.put(map.type(), map); - } - this.ID = null; - } - - public AttributedCodeBlock(Collection bytecodes, - Collection> attributeMaps) { - super(bytecodes); - this.attributes = new HashMap, wyil.lang.Attribute.Map>(); - for (Attribute.Map map : attributeMaps) { - this.attributes.put(map.type(), map); - } - this.ID = null; - } - - /** - * This constructor is used when creating a subblock only. They key is that - * updates to the attributes of this block are visible to the enclosing - * block as well. - * - * @param index - * @param block - */ - public AttributedCodeBlock(CodeBlock.Index index, AttributedCodeBlock block) { - // NOTE: do not call with block.bytecodes since this is *only* used for - // creating a subblock. - super(); - this.attributes = new HashMap, wyil.lang.Attribute.Map>(); - this.attributes.putAll(block.attributes); - this.ID = index; - } - - // =================================================================== - // Get Methods - // =================================================================== - - /** - * Return the attribute of a given kind associated with a bytecode. Such an - * attribute may not exist, in which case null is returned. - * - * @param id - * @param kind - * @return - */ - public T attribute(CodeBlock.Index id, Class kind) { - Attribute.Map map = (Attribute.Map) attributes.get(kind); - if (map != null) { - return map.get(id); - } else { - // no map of this kind exists. - return null; - } - } - - public List attributes(Index index) { - ArrayList results = new ArrayList(); - for (Attribute.Map map : attributes.values()) { - Attribute attr = map.get(index); - if (attr != null) { - results.add(attr); - } - } - return results; - } - - public Collection> attributes() { - return attributes.values(); - } - - public void addAttributes(Attribute.Map map) { - attributes.put((Class) map.type(), map); - } - - /** - * Return the list of all valid bytecode indexes in this block in order of - * appearance. - * - * @return - */ - public List indices() { - ArrayList indices = new ArrayList(); - indices(ID,this,indices); - return indices; - } - - private void indices(CodeBlock.Index parent, CodeBlock block, - ArrayList indices) { - CodeBlock.Index id = new CodeBlock.Index(parent); - for (int i = 0; i != block.size(); ++i) { - indices.add(id); - // Now, check whether bytecode at the given location is itself a - // block or not. - Code code = block.get(i); - if (code instanceof CodeBlock) { - // Yes, this bytecode is itself a block. - indices(id, (CodeBlock) code, indices); - } - id = id.next(); - } - } - - public Code get(CodeBlock.Index index) { - CodeBlock.Index idx = index.prepend(ID); - return super.get(idx); - } - - /** - *

- * Construct a temporary sub-block for use in creating an attributed - * compound bytecode (e.g. a loop). In essence, the created subblock is used - * to accumulate both bytecodes for the compound, along with attributes - * which are correctly associated. - *

- *

- * NOTE: The compound bytecode is expected to be added next into this - * (i.e. the outer) AttributedCodeBlock. If other bytecodes are added - * inbetween, then attributes may not be correctly associated. - *

- * - * @return - */ - public AttributedCodeBlock createSubBlock() { - CodeBlock.Index index = new CodeBlock.Index(ID, size()); - return new AttributedCodeBlock(index, this); - } - - // =================================================================== - // Append Methods - // =================================================================== - - /** - * Append a bytecode onto the end of this block, along with any attributes - * to be associated with that bytecode. - * - * @param code - * --- bytecode to append - * @param attributes - * --- attributes associated with bytecode. - */ - public boolean add(Code code, Attribute... attributes) { - CodeBlock.Index index = new CodeBlock.Index(CodeBlock.Index.ROOT,size()); - putAll(index, attributes); - return add(code); - } - - /** - * Append a bytecode onto the end of this block, along with any attributes - * to be associated with that bytecode. - * - * @param code - * --- bytecode to append - * @param attributes - * --- attributes associated with bytecode. - */ - public boolean add(Code code, Collection attributes) { - CodeBlock.Index index = new CodeBlock.Index(CodeBlock.Index.ROOT,size()); - putAll(index, attributes); - return add(code); - } - - /** - * Add all bytecodes to this block from another include all attributes - * associated with each bytecode. - * - * @param block - */ - public void addAll(AttributedCodeBlock block) { - // FIXME: - throw new RuntimeException("implement me!"); - } - - // =================================================================== - // Insert Methods - // =================================================================== - - /** - *

- * Insert a bytecode at a given position in this block. It is assumed that - * the bytecode employs the same environment as this block. The bytecode at - * the given position (and any after it) are shifted one position down. - *

- * - * @param index - * --- position to insert at. - * @param code - * --- bytecode to insert at the given position. - * @param attributes - */ - public void add(int index, Code code, Attribute... attributes) { - CodeBlock.Index idx = new CodeBlock.Index(ID, index); - insertAll(idx, attributes); - add(index, code); - } - - /** - *

- * Insert a bytecode at a given position in this block. It is assumed that - * the bytecode employs the same environment as this block. The bytecode at - * the given position (and any after it) are shifted one position down. - *

- * - * @param index - * --- position to insert at. - * @param code - * --- bytecode to insert at the given position. - * @param attributes - */ - public void add(int index, Code code, Collection attributes) { - CodeBlock.Index idx = new CodeBlock.Index(ID, index); - insertAll(idx, attributes); - add(index, code); - } - - // =================================================================== - // Replace and Remove Methods - // =================================================================== - - /** - *

- * Replace the bytecode at a given position in this block with another. It - * is assumed that the bytecode employs the same environment as this block. - *

- * - * @param index - * --- position of bytecode to replace. - * @param code - * --- bytecode to replace with. - * @param attributes - */ - public void set(int index, Code code, Attribute... attributes) { - // TODO: actually update the attributes - set(index, code); - throw new RuntimeException("implement me!"); - } - - /** - *

- * Replace the bytecode at a given position in this block with another. It - * is assumed that the bytecode employs the same environment as this block. - *

- * - * @param index - * --- position of bytecode to replace. - * @param code - * --- bytecode to replace with. - * @param attributes - */ - public void set(int index, Code code, Collection attributes) { - // TODO: actually update the attributes - set(index, code); - throw new RuntimeException("implement me!"); - } - - // =================================================================== - // Helper Methods - // =================================================================== - private void putAll(CodeBlock.Index index, Collection attributes) { - CodeBlock.Index idx = index.prepend(ID); - // Go through and add each attribute at the given index. - for (Attribute attribute : attributes) { - Attribute.Map map = this.attributes.get(attribute - .getClass()); - // First, check whether an attribute map for this kind of attribute - // exists. - if (map != null) { - // Yes, so add it. - map.put(idx, attribute); - } - } - } - - private void putAll(CodeBlock.Index index, Attribute... attributes) { - CodeBlock.Index idx = index.prepend(ID); - // Go through and add each attribute at the given index. - for (Attribute attribute : attributes) { - Attribute.Map map = this.attributes.get(attribute - .getClass()); - // First, check whether an attribute map for this kind of attribute - // exists. - if (map != null) { - // Yes, so add it. - map.put(idx, attribute); - } - } - } - - private void insertAll(CodeBlock.Index index, Attribute... attributes) { - CodeBlock.Index idx = index.prepend(ID); - // first, make space for the given code index - for (Attribute.Map map : this.attributes.values()) { - map.insert(idx, null); - } - // second, add the attributes at that index - putAll(index, attributes); - } - - private void insertAll(CodeBlock.Index index, - Collection attributes) { - CodeBlock.Index idx = index.prepend(ID); - // first, make space for the given code index - for (Attribute.Map map : this.attributes.values()) { - map.insert(idx, null); - } - // second, add the attributes at that index - putAll(index, attributes); - } -} \ No newline at end of file From a399e9bca21c91e16228449623bad0dff86d852d Mon Sep 17 00:00:00 2001 From: DavePearce Date: Thu, 4 Feb 2016 21:04:56 +1300 Subject: [PATCH 06/43] Replace CodeBlock with CodeForest #474 The old notion of nestable code blocks is now replaced with a linear notion of blocks, which I'm referring to as a "Code Forest". It's a forest because it's a many rooted tree of blocks, although they are technically organised in a sequential fashion. It is many-rooted because we use to represent not just a single chunk of code (e.g. the body of a function or method), but multiple chunks (i.e. the body and all requires/ensures clauses). In this way, the code forest has declared set of "registers" over which contained blocks may operate. Have removed the ConstantPropagation, DeadCodeElimination and LiveVariablesAnalysis stages. These were already disabled and no longer compiled. For the sake of doing things properly, I've decided to remove them altogether. This is a temporary solution which I'll revisit at a later date when the WyIL API is more stable again. Have worked on the binary representation of code blocks, and also bytecodes. Some of this is focused on simplifying bytecodes and will be continued beyond this issue. --- .../wyc/src/wyc/builder/CodeGenerator.java | 1020 ++++++------- .../wyc/src/wyc/builder/FlowTypeChecker.java | 2 +- modules/wyc/src/wyc/util/WycBuildTask.java | 17 +- .../wyil/attributes/SourceLocationMap.java | 66 - modules/wyil/src/wyil/builders/VcBranch.java | 8 +- .../wyil/src/wyil/builders/VcGenerator.java | 68 +- .../wyil/src/wyil/checks/CoercionCheck.java | 22 +- .../wyil/checks/DefiniteAssignmentCheck.java | 63 +- modules/wyil/src/wyil/checks/ModuleCheck.java | 27 +- modules/wyil/src/wyil/io/WyilFilePrinter.java | 40 +- modules/wyil/src/wyil/io/WyilFileReader.java | 1304 ++++++++++------- modules/wyil/src/wyil/io/WyilFileWriter.java | 1083 +++++++------- modules/wyil/src/wyil/lang/Code.java | 341 +++-- modules/wyil/src/wyil/lang/CodeBlock.java | 176 --- modules/wyil/src/wyil/lang/CodeForest.java | 287 ++++ modules/wyil/src/wyil/lang/CodeUtils.java | 34 +- modules/wyil/src/wyil/lang/Codes.java | 472 ++---- modules/wyil/src/wyil/lang/WyilFile.java | 97 +- .../wyil/transforms/ConstantPropagation.java | 636 -------- .../wyil/transforms/DeadCodeElimination.java | 265 ---- .../transforms/LiveVariablesAnalysis.java | 352 ----- .../src/wyil/transforms/LoopVariants.java | 45 +- modules/wyil/src/wyil/util/Interpreter.java | 110 +- .../wyil/util/dfa/BackwardFlowAnalysis.java | 40 +- .../wyil/util/dfa/ForwardFlowAnalysis.java | 40 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 78 +- 26 files changed, 2721 insertions(+), 3972 deletions(-) delete mode 100644 modules/wyil/src/wyil/attributes/SourceLocationMap.java delete mode 100644 modules/wyil/src/wyil/lang/CodeBlock.java create mode 100644 modules/wyil/src/wyil/lang/CodeForest.java delete mode 100755 modules/wyil/src/wyil/transforms/ConstantPropagation.java delete mode 100644 modules/wyil/src/wyil/transforms/DeadCodeElimination.java delete mode 100755 modules/wyil/src/wyil/transforms/LiveVariablesAnalysis.java diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index ffc20d290f..d1f70c976b 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -42,9 +42,7 @@ import wycc.util.Triple; import wyfs.lang.Path; import wyil.attributes.VariableDeclarations; -import wyil.attributes.SourceLocationMap; import wyil.lang.*; -import wyil.util.AttributedCodeBlock; /** *

@@ -138,14 +136,12 @@ public WyilFile generate(WhileyFile wf) { } else if (d instanceof WhileyFile.Constant) { declarations.add(generate((WhileyFile.Constant) d)); } else if (d instanceof WhileyFile.FunctionOrMethod) { - declarations - .add(generate((WhileyFile.FunctionOrMethod) d)); + declarations.add(generate((WhileyFile.FunctionOrMethod) d)); } } catch (SyntaxError se) { throw se; } catch (Throwable ex) { - WhileyFile.internalFailure(ex.getMessage(), - (WhileyFile.Context) d, d, ex); + WhileyFile.internalFailure(ex.getMessage(), (WhileyFile.Context) d, d, ex); } } @@ -170,8 +166,7 @@ public WyilFile generate(WhileyFile wf) { */ private WyilFile.Constant generate(WhileyFile.Constant cd) { // TODO: this the point where were should run an evaluator ? - return new WyilFile.Constant(cd.modifiers(), cd.name(), - cd.resolvedValue); + return new WyilFile.Constant(cd.modifiers(), cd.name(), cd.resolvedValue); } // ========================================================================= @@ -187,93 +182,69 @@ private WyilFile.Constant generate(WhileyFile.Constant cd) { * @return * @throws Exception */ - private WyilFile.Type generate(WhileyFile.Type td) - throws Exception { - AttributedCodeBlock invariant = null; - - if (td.invariant.size() > 0) { - // Here, an explicit invariant is given for the type and this needs - // to be translated into bytecodes as well. - Environment environment = new Environment(); - environment.allocate(td.resolvedType.raw(),td.parameter.name()); - invariant = new AttributedCodeBlock(new SourceLocationMap()); - for(int i = 0;i!=td.invariant.size();++i) { - String lab = CodeUtils.freshLabel(); - generateCondition(lab, td.invariant.get(i), environment, invariant, td); - invariant.add(Codes.Fail()); - invariant.add(Codes.Label(lab)); - } - invariant.add(Codes.Return()); + private WyilFile.Type generate(WhileyFile.Type td) throws Exception { + CodeForest forest = new CodeForest(); + Environment environment = new Environment(); + // Allocate declared parameter + environment.allocate(td.resolvedType.raw(), td.parameter.name()); + // Generate code for each invariant condition + for (Expr invariant : td.invariant) { + int root = generateInvariantBlock(invariant, environment, forest, td); + forest.addRoot(root); } - - return new WyilFile.Type(td.modifiers(), td.name(), - td.resolvedType.nominal(), invariant); + // Add all registers used within the invariant + forest.registers().addAll(environment.asRegisters()); + // done + return new WyilFile.Type(td.modifiers(), td.name(), td.resolvedType.nominal(), forest); } // ========================================================================= // Function / Method Declarations // ========================================================================= - private WyilFile.FunctionOrMethod generate( - WhileyFile.FunctionOrMethod fd) throws Exception { - //Type.FunctionOrMethod rawFnType = fd.resolvedType().raw(); + private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throws Exception { + // Type.FunctionOrMethod rawFnType = fd.resolvedType().raw(); Type.FunctionOrMethod nominalFnType = fd.resolvedType().nominal(); // ================================================================== // Construct environments // ================================================================== - Environment environment = new Environment(); - ArrayList declarations = new ArrayList(); - addDeclaredParameters(fd.parameters,fd.resolvedType().params(), environment, declarations); - addDeclaredParameters(fd.returns,fd.resolvedType().returns(), environment, declarations); + CodeForest forest = new CodeForest(); + Environment environment = new Environment(); + ArrayList declarations = new ArrayList(); + addDeclaredParameters(fd.parameters, fd.resolvedType().params(), environment, declarations); + addDeclaredParameters(fd.returns, fd.resolvedType().returns(), environment, declarations); // Allocate all declared variables now. This ensures that all declared // variables occur before any temporary variables. buildVariableDeclarations(fd.statements, declarations, environment, fd); + // ================================================================== // Generate pre-condition // ================================================================== - - ArrayList requires = new ArrayList(); - for (Expr condition : fd.requires) { - AttributedCodeBlock precondition = new AttributedCodeBlock(new SourceLocationMap()); - String endLab = CodeUtils.freshLabel(); - generateCondition(endLab, condition, new Environment(environment), precondition, fd); - precondition.add(Codes.Fail(),attributes(condition)); - precondition.add(Codes.Label(endLab)); - precondition.add(Codes.Return()); - requires.add(precondition); + for (Expr precondition : fd.requires) { + int root = generateInvariantBlock(precondition, environment, forest, fd); + forest.addRoot(root); } - + // ================================================================== // Generate post-condition // ================================================================== - ArrayList ensures = new ArrayList(); - // This indicates one or more explicit ensures clauses are given. - // Therefore, we must translate each of these into Wyil bytecodes. - for (Expr condition : fd.ensures) { - AttributedCodeBlock postcondition = new AttributedCodeBlock(new SourceLocationMap()); - String endLab = CodeUtils.freshLabel(); - // Clone the environment at this stage to avoid updates to the - // environment within the condition affecting the external - // environment. - generateCondition(endLab, condition, new Environment(environment), - postcondition, fd); - postcondition.add(Codes.Fail(), attributes(condition)); - postcondition.add(Codes.Label(endLab)); - postcondition.add(Codes.Return()); - ensures.add(postcondition); + for (Expr postcondition : fd.ensures) { + int root = generateInvariantBlock(postcondition, environment, forest, fd); + forest.addRoot(root); } - + // ================================================================== // Generate body // ================================================================== - AttributedCodeBlock body = new AttributedCodeBlock(new SourceLocationMap()); + CodeForest.Block body = new CodeForest.Block(); + forest.addAsRoot(body); for (Stmt s : fd.statements) { - generate(s, environment, body, fd); + generate(s, environment, body, forest, fd); } - + // The following is sneaky. It guarantees that every method ends in a // return. For methods that actually need a value, this is either // removed as dead-code or remains and will cause an error. @@ -281,39 +252,60 @@ private WyilFile.FunctionOrMethod generate( WyilFile.FunctionOrMethod declaration; + // Second, add the corresponding attribute to the enclosing method. + forest.registers().addAll(createVariableDeclarations(environment, declarations)); + if (fd instanceof WhileyFile.Function) { WhileyFile.Function f = (WhileyFile.Function) fd; - declaration = new WyilFile.FunctionOrMethod(fd - .modifiers(), fd.name(), f.resolvedType.nominal(), body, requires, ensures); + declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), f.resolvedType.nominal(), forest, + fd.requires.size(), fd.ensures.size()); } else { WhileyFile.Method md = (WhileyFile.Method) fd; - declaration = new WyilFile.FunctionOrMethod(fd - .modifiers(), fd.name(), md.resolvedType.nominal(), body, requires, ensures); - } - // Second, add the corresponding attribute to the enclosing method. - declaration.attributes().add(createVariableDeclarations(environment,declarations)); + declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), md.resolvedType.nominal(), forest, + fd.requires.size(), fd.ensures.size()); + } // Done. return declaration; } + /** + * Construct a new code block in a given forest corresponding to a + * precondition, postcondition or type invariant. + * + * @param invariant + * @param environment + * @param forest + * @param context + */ + private int generateInvariantBlock(Expr invariant, Environment environment, CodeForest forest, Context context) { + CodeForest.Block precondition = new CodeForest.Block(); + int index = forest.add(precondition); + String endLab = CodeUtils.freshLabel(); + generateCondition(endLab, invariant, environment, precondition, forest, context); + precondition.add(Codes.Fail(), attributes(invariant)); + precondition.add(Codes.Label(endLab)); + precondition.add(Codes.Return()); + return index; + } + /** * Construct register declarations for this function or method. The register * declarations stores information about the names and declared types of all * registers. Technically speaking, this information is not necessary to * compile and run a Whiley program. However, it is very useful for * debugging and performing verification. - */ - private VariableDeclarations createVariableDeclarations(Environment environment, - List declarations) { + */ + private List createVariableDeclarations(Environment environment, + List declarations) { // FIXME: this is a hack. In essence, we're trying to get the types of // all intermediate registers used in code generation. To do this, we're // looking at their type having typed the entire function. - for(int i=declarations.size();i parameters, - List types, Environment environment, List declarations) { + private void addDeclaredParameters(List parameters, List types, + Environment environment, List declarations) { for (int i = 0; i != parameters.size(); ++i) { WhileyFile.Parameter parameter = parameters.get(i); // allocate parameter to register in the current block - declarations.add(new VariableDeclarations.Declaration(types.get(i).nominal(), parameter.name)); + declarations.add(new CodeForest.Register(types.get(i).nominal(), parameter.name)); // allocate parameter to register in the current block environment.allocate(types.get(i).raw(), parameter.name); } } - /** - * Add a list of parameter declarations to a given environment - * - * @param parameters --- List of parameters to add - * @param types --- List of parameter types - * @param environment --- environment to add parameters to - */ - private void addDeclaredParameter(WhileyFile.Parameter parameter, Nominal type, - Environment environment, List declarations) { - // allocate parameter to register in the current block - if(parameter != null) { - declarations.add(new VariableDeclarations.Declaration(type.nominal(), parameter.name)); - // allocate parameter to register in the current block - environment.allocate(type.raw(), parameter.name); - } - } - // ========================================================================= // Statements // ========================================================================= @@ -366,58 +341,56 @@ private void addDeclaredParameter(WhileyFile.Parameter parameter, Nominal type, * --- Statement to be translated. * @param environment * --- Mapping from variable names to to slot block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. + * @param forest + * --- Forest which encloses the given forest block * @param context * --- Enclosing context of this statement (i.e. type, constant, * function or method declaration). The context is used to aid * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt stmt, Environment environment, - AttributedCodeBlock codes, Context context) { + private void generate(Stmt stmt, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { try { if (stmt instanceof VariableDeclaration) { - generate((VariableDeclaration) stmt, environment, codes, - context); + generate((VariableDeclaration) stmt, environment, block, forest, context); } else if (stmt instanceof Assign) { - generate((Assign) stmt, environment, codes, context); + generate((Assign) stmt, environment, block, forest, context); } else if (stmt instanceof Assert) { - generate((Assert) stmt, environment, codes, context); + generate((Assert) stmt, environment, block, forest, context); } else if (stmt instanceof Assume) { - generate((Assume) stmt, environment, codes, context); + generate((Assume) stmt, environment, block, forest, context); } else if (stmt instanceof Return) { - generate((Return) stmt, environment, codes, context); + generate((Return) stmt, environment, block, forest, context); } else if (stmt instanceof Debug) { - generate((Debug) stmt, environment, codes, context); + generate((Debug) stmt, environment, block, forest, context); } else if (stmt instanceof Fail) { - generate((Fail) stmt, environment, codes, context); + generate((Fail) stmt, environment, block, forest, context); } else if (stmt instanceof IfElse) { - generate((IfElse) stmt, environment, codes, context); + generate((IfElse) stmt, environment, block, forest, context); } else if (stmt instanceof Switch) { - generate((Switch) stmt, environment, codes, context); + generate((Switch) stmt, environment, block, forest, context); } else if (stmt instanceof Break) { - generate((Break) stmt, environment, codes, context); + generate((Break) stmt, environment, block, forest, context); } else if (stmt instanceof Continue) { - generate((Continue) stmt, environment, codes, context); + generate((Continue) stmt, environment, block, forest, context); } else if (stmt instanceof While) { - generate((While) stmt, environment, codes, context); + generate((While) stmt, environment, block, forest, context); } else if (stmt instanceof DoWhile) { - generate((DoWhile) stmt, environment, codes, context); + generate((DoWhile) stmt, environment, block, forest, context); } else if (stmt instanceof Expr.FunctionOrMethodCall) { - generate((Expr.Multi) stmt, environment, - codes, context); + generate((Expr.Multi) stmt, environment, block, forest, context); } else if (stmt instanceof Expr.IndirectFunctionOrMethodCall) { - generate((Expr.Multi) stmt, - environment, codes, context); + generate((Expr.Multi) stmt, environment, block, forest, context); } else if (stmt instanceof Expr.New) { - generate((Expr.New) stmt, environment, codes, context); + generate((Expr.New) stmt, environment, block, forest, context); } else if (stmt instanceof Skip) { - generate((Skip) stmt, environment, codes, context); + generate((Skip) stmt, environment, block, forest, context); } else { // should be dead-code - WhileyFile.internalFailure("unknown statement: " - + stmt.getClass().getName(), context, stmt); + WhileyFile.internalFailure("unknown statement: " + stmt.getClass().getName(), context, stmt); } } catch (ResolveError rex) { WhileyFile.syntaxError(rex.getMessage(), context, stmt, rex); @@ -452,7 +425,7 @@ private void generate(Stmt stmt, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -460,16 +433,15 @@ private void generate(Stmt stmt, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(VariableDeclaration s, Environment environment, - AttributedCodeBlock codes, Context context) { - // First, we allocate this variable to a given slot in the environment. - int root = environment.get(s.parameter.name); + private void generate(VariableDeclaration s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { + // First, we allocate this variable to a given slot in the environment. + int root = environment.get(s.parameter.name); // Second, translate initialiser expression if it exists. if (s.expr != null) { - int operand = generate(s.expr, environment, codes, context); - codes.add(Codes.Assign(s.expr.result().raw(), root, operand), - attributes(s)); - } + int operand = generate(s.expr, environment, block, forest, context); + block.add(Codes.Assign(s.expr.result().raw(), root, operand), attributes(s)); + } } /** @@ -509,7 +481,7 @@ private void generate(VariableDeclaration s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -517,51 +489,50 @@ private void generate(VariableDeclaration s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Assign s, Environment environment, AttributedCodeBlock codes, Context context) { + private void generate(Assign s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { // First, we translate all right-hand side expressions and assign them // to temporary registers. ArrayList operands = new ArrayList(); ArrayList types = new ArrayList(); - for(int i=0;i!=s.rvals.size();++i) { + for (int i = 0; i != s.rvals.size(); ++i) { Expr e = s.rvals.get(i); // FIXME: this is a rather ugly - if(e instanceof Expr.Multi) { + if (e instanceof Expr.Multi) { // The assigned expression actually has multiple returns, // therefore extract them all. Expr.Multi me = (Expr.Multi) e; - for(Nominal t : me.returns()) { + for (Nominal t : me.returns()) { types.add(t.raw()); - } - operands.addAll(toIntegerList(generate(me, environment, codes, context))); + } + operands.addAll(toIntegerList(generate(me, environment, block, forest, context))); } else { // The assigned rval is a simple expression which returns a // single value - operands.add(generate(e, environment, codes, context)); + operands.add(generate(e, environment, block, forest, context)); types.add(e.result().raw()); - } + } } - + // Second, update each expression on left-hand side of this assignment // appropriately. Note that we can safely assume here the number of // rvals and lvals matches as this has already been checked by // FlowTypeChecker. for (int i = 0; i != s.lvals.size(); ++i) { Expr.LVal lval = s.lvals.get(i); - generateAssignment(lval, operands.get(i), types.get(i), environment, codes, context); + generateAssignment(lval, operands.get(i), types.get(i), environment, block, forest, context); } } - + public void generateAssignment(Expr.LVal lval, int operand, Type type, Environment environment, - AttributedCodeBlock codes, Context context) { + CodeForest.Block block, CodeForest forest, Context context) { if (lval instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) lval; // This is the easiest case. Having translated the right-hand side // expression, we now assign it directly to the register allocated // for variable on the left-hand side. int target = environment.get(v.var); - codes.add(Codes.Assign(type, target, operand), attributes(lval)); - } else if (lval instanceof Expr.IndexOf - || lval instanceof Expr.FieldAccess + block.add(Codes.Assign(type, target, operand), attributes(lval)); + } else if (lval instanceof Expr.IndexOf || lval instanceof Expr.FieldAccess || lval instanceof Expr.Dereference) { // This is the more complicated case, since the left-hand side // expression is recursive. However, the WyIL update bytecode comes @@ -571,11 +542,10 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme // updated. ArrayList fields = new ArrayList(); ArrayList operands = new ArrayList(); - Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, - environment, codes, context); + Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, environment, block, forest, context); int target = environment.get(lhs.var); - codes.add(Codes.Update(lhs.type.raw(), target, operands, - operand, lhs.afterType.raw(), fields), attributes(lval)); + block.add(Codes.Update(lhs.type.raw(), target, operands, operand, lhs.afterType.raw(), fields), + attributes(lval)); } else { WhileyFile.syntaxError("invalid assignment", context, lval); } @@ -601,7 +571,7 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme * by this method as it traverses the lval. * @param environment * Mapping from variable names to block registers. - * @param codes + * @param block * Code block into which this statement is to be translated. * @param context * Enclosing context of this statement (i.e. type, constant, @@ -609,33 +579,28 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme * with error reporting as it determines the enclosing file. * @return */ - private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, - ArrayList operands, Environment environment, - AttributedCodeBlock codes, Context context) { + private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, ArrayList operands, + Environment environment, CodeForest.Block block, CodeForest forest, Context context) { if (e instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) e; return v; } else if (e instanceof Expr.Dereference) { Expr.Dereference pa = (Expr.Dereference) e; - return extractLVal(pa.src, fields, operands, environment, codes, - context); + return extractLVal(pa.src, fields, operands, environment, block, forest, context); } else if (e instanceof Expr.IndexOf) { Expr.IndexOf la = (Expr.IndexOf) e; - int operand = generate(la.index, environment, codes, context); - Expr.AssignedVariable l = extractLVal(la.src, fields, operands, - environment, codes, context); + int operand = generate(la.index, environment, block, forest, context); + Expr.AssignedVariable l = extractLVal(la.src, fields, operands, environment, block, forest, context); operands.add(operand); return l; } else if (e instanceof Expr.FieldAccess) { Expr.FieldAccess ra = (Expr.FieldAccess) e; - Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, - environment, codes, context); + Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, environment, block, forest, context); fields.add(ra.name); return r; } else { - WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), - context, e); + WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), context, e); return null; // dead code } } @@ -647,7 +612,7 @@ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -655,17 +620,17 @@ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Assert s, Environment environment, - AttributedCodeBlock codes, Context context) { - + private void generate(Stmt.Assert s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { // First, create assert block body - AttributedCodeBlock body = codes.createSubBlock(); + CodeForest.Block subblock = new CodeForest.Block(); + int body = forest.add(subblock); String endLab = CodeUtils.freshLabel(); - generateCondition(endLab, s.expr, environment, body, context); - body.add(Codes.Fail(), attributes(s.expr)); - body.add(Codes.Label(endLab)); + generateCondition(endLab, s.expr, environment, subblock, forest, context); + subblock.add(Codes.Fail(), attributes(s.expr)); + subblock.add(Codes.Label(endLab)); // Second, create assert bytecode - codes.add(Codes.Assert(body.bytecodes()), attributes(s)); + block.add(Codes.Assert(body), attributes(s)); } @@ -676,7 +641,7 @@ private void generate(Stmt.Assert s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -684,16 +649,17 @@ private void generate(Stmt.Assert s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Assume s, Environment environment, - AttributedCodeBlock codes, Context context) { + private void generate(Stmt.Assume s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { // First, create assume block body - AttributedCodeBlock body = codes.createSubBlock(); + CodeForest.Block subblock = new CodeForest.Block(); + int body = forest.add(subblock); String endLab = CodeUtils.freshLabel(); - generateCondition(endLab, s.expr, environment, body, context); - body.add(Codes.Fail(), attributes(s.expr)); - body.add(Codes.Label(endLab)); - // Second, create assume bytecode - codes.add(Codes.Assume(body.bytecodes()), attributes(s)); + generateCondition(endLab, s.expr, environment, subblock, forest, context); + subblock.add(Codes.Fail(), attributes(s.expr)); + subblock.add(Codes.Label(endLab)); + // Second, create assert bytecode + block.add(Codes.Assume(body), attributes(s)); } /** @@ -721,7 +687,7 @@ private void generate(Stmt.Assume s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -729,29 +695,30 @@ private void generate(Stmt.Assume s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Return s, Environment environment, AttributedCodeBlock codes, Context context) { + private void generate(Stmt.Return s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { List returns = s.returns; // Here, we don't put the type propagated for the return expression. // Instead, we use the declared return type of this function. This // has the effect of forcing an implicit coercion between the // actual value being returned and its required type. - List returnTypes = ((WhileyFile.FunctionOrMethod) context).resolvedType().raw().returns(); - Type[] types = returnTypes.toArray(new Type[returnTypes.size()]); + List returnTypes = ((WhileyFile.FunctionOrMethod) context).resolvedType().raw().returns(); + Type[] types = returnTypes.toArray(new Type[returnTypes.size()]); int[] operands = new int[types.length]; int index = 0; for (int i = 0; i != returns.size(); ++i) { Expr e = returns.get(i); // FIXME: this is a rather ugly if (e instanceof Expr.Multi) { - int[] results = generate((Expr.Multi) e, environment, codes, context); + int[] results = generate((Expr.Multi) e, environment, block, forest, context); for (int r : results) { operands[index++] = r; } } else { - operands[index++] = generate(e, environment, codes, context); + operands[index++] = generate(e, environment, block, forest, context); } } - codes.add(Codes.Return(types,operands), attributes(s)); + block.add(Codes.Return(types, operands), attributes(s)); } /** @@ -761,7 +728,7 @@ private void generate(Stmt.Return s, Environment environment, AttributedCodeBloc * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -769,9 +736,9 @@ private void generate(Stmt.Return s, Environment environment, AttributedCodeBloc * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Skip s, Environment environment, - AttributedCodeBlock codes, Context context) { - codes.add(Codes.Nop, attributes(s)); + private void generate(Stmt.Skip s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { + block.add(Codes.Nop, attributes(s)); } /** @@ -797,7 +764,7 @@ private void generate(Stmt.Skip s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -805,10 +772,10 @@ private void generate(Stmt.Skip s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Debug s, Environment environment, - AttributedCodeBlock codes, Context context) { - int operand = generate(s.expr, environment, codes, context); - codes.add(Codes.Debug(operand), attributes(s)); + private void generate(Stmt.Debug s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { + int operand = generate(s.expr, environment, block, forest, context); + block.add(Codes.Debug(operand), attributes(s)); } /** @@ -836,9 +803,8 @@ private void generate(Stmt.Debug s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Fail s, Environment environment, - AttributedCodeBlock codes, Context context) { - codes.add(Codes.Fail(), attributes(s)); + private void generate(Stmt.Fail s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { + block.add(Codes.Fail(), attributes(s)); } /** @@ -878,7 +844,7 @@ private void generate(Stmt.Fail s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -887,27 +853,26 @@ private void generate(Stmt.Fail s, Environment environment, * @return */ private void generate(Stmt.IfElse s, Environment environment, - AttributedCodeBlock codes, Context context) { + CodeForest.Block block, CodeForest forest, Context context) { String falseLab = CodeUtils.freshLabel(); String exitLab = s.falseBranch.isEmpty() ? falseLab : CodeUtils .freshLabel(); - generateCondition(falseLab, invert(s.condition), environment, codes, - context); + generateCondition(falseLab, invert(s.condition), environment, block, forest, context); for (Stmt st : s.trueBranch) { - generate(st, environment, codes, context); + generate(st, environment, block, forest, context); } if (!s.falseBranch.isEmpty()) { - codes.add(Codes.Goto(exitLab)); - codes.add(Codes.Label(falseLab)); + block.add(Codes.Goto(exitLab)); + block.add(Codes.Label(falseLab)); for (Stmt st : s.falseBranch) { - generate(st, environment, codes, context); + generate(st, environment, block, forest, context); } } - codes.add(Codes.Label(exitLab)); + block.add(Codes.Label(exitLab)); } /** @@ -947,7 +912,7 @@ private void generate(Stmt.IfElse s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -955,14 +920,13 @@ private void generate(Stmt.IfElse s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Break s, Environment environment, - AttributedCodeBlock codes, Context context) { + private void generate(Stmt.Break s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { LoopScope scope = findEnclosingScope(LoopScope.class); if (scope == null) { - WhileyFile - .syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context, s); + WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context, s); } - codes.add(Codes.Goto(scope.breakLabel)); + block.add(Codes.Goto(scope.breakLabel)); } /** @@ -1005,7 +969,7 @@ private void generate(Stmt.Break s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -1013,14 +977,13 @@ private void generate(Stmt.Break s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Continue s, Environment environment, - AttributedCodeBlock codes, Context context) { + private void generate(Stmt.Continue s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { LoopScope scope = findEnclosingScope(LoopScope.class); if (scope == null) { - WhileyFile - .syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context, s); + WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context, s); } - codes.add(Codes.Goto(scope.continueLabel)); + block.add(Codes.Goto(scope.continueLabel)); } /** @@ -1069,7 +1032,7 @@ private void generate(Stmt.Continue s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -1077,14 +1040,14 @@ private void generate(Stmt.Continue s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Switch s, Environment environment, - AttributedCodeBlock codes, Context context) throws Exception { + private void generate(Stmt.Switch s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) throws Exception { String exitLab = CodeUtils.freshLabel(); - int operand = generate(s.expr, environment, codes, context); + int operand = generate(s.expr, environment, block, forest, context); String defaultTarget = exitLab; HashSet values = new HashSet<>(); ArrayList> cases = new ArrayList<>(); - int start = codes.size(); + int start = block.size(); for (Stmt.Case c : s.cases) { if (c.expr.isEmpty()) { @@ -1092,20 +1055,19 @@ private void generate(Stmt.Switch s, Environment environment, // must check that we have not already seen a case with an empty // match (otherwise, we'd have two default labels ;) if (defaultTarget != exitLab) { - WhileyFile.syntaxError( - errorMessage(DUPLICATE_DEFAULT_LABEL), context, c); + WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context, c); } else { defaultTarget = CodeUtils.freshLabel(); - codes.add(Codes.Label(defaultTarget), attributes(c)); + block.add(Codes.Label(defaultTarget), attributes(c)); for (Stmt st : c.stmts) { - generate(st, environment, codes, context); + generate(st, environment, block, forest, context); } - codes.add(Codes.Goto(exitLab), attributes(c)); + block.add(Codes.Goto(exitLab), attributes(c)); } } else if (defaultTarget == exitLab) { String target = CodeUtils.freshLabel(); - codes.add(Codes.Label(target), attributes(c)); + block.add(Codes.Label(target), attributes(c)); // Case statements in Whiley may have multiple matching constant // values. Therefore, we iterate each matching value and @@ -1116,30 +1078,27 @@ private void generate(Stmt.Switch s, Environment environment, // Check whether this case constant has already been used as // a case constant elsewhere. If so, then report an error. if (values.contains(constant)) { - WhileyFile.syntaxError( - errorMessage(DUPLICATE_CASE_LABEL), context, c); + WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), context, c); } cases.add(new Pair<>(constant, target)); values.add(constant); } for (Stmt st : c.stmts) { - generate(st, environment, codes, context); + generate(st, environment, block, forest, context); } - codes.add(Codes.Goto(exitLab), attributes(c)); + block.add(Codes.Goto(exitLab), attributes(c)); } else { // This represents the case where we have another non-default // case after the default case. Such code cannot be executed, // and is therefore reported as an error. - WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), context, - c); + WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), context, c); } } - codes.add(start, Codes.Switch(s.expr.result().raw(), operand, - defaultTarget, cases), attributes(s)); - codes.add(Codes.Label(exitLab), attributes(s)); + block.add(start, Codes.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); + block.add(Codes.Label(exitLab), attributes(s)); } /** @@ -1175,7 +1134,7 @@ private void generate(Stmt.Switch s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -1183,8 +1142,8 @@ private void generate(Stmt.Switch s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.While s, Environment environment, - AttributedCodeBlock codes, Context context) { + private void generate(Stmt.While s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { // A label marking where execution continues after the while // loop finishes. Used when the loop condition evaluates to false // or when a break statement is encountered. @@ -1193,38 +1152,25 @@ private void generate(Stmt.While s, Environment environment, // by the continue statement. String continueLab = CodeUtils.freshLabel(); - AttributedCodeBlock body = codes.createSubBlock(); - - if(s.invariants.size() > 0) { - // Ok, there is at least one invariant expression. Therefore, create - // an invariant bytecode. - - for (Expr e : s.invariants) { - String nextLab = CodeUtils.freshLabel(); - AttributedCodeBlock invariant = body.createSubBlock(); - generateCondition(nextLab, e, environment, invariant, context); - invariant.add(Codes.Fail(), attributes(e)); - invariant.add(Codes.Label(nextLab)); - // Terminate invariant block --- see #480 - invariant.add(Codes.Return()); - // Create the invariant block - body.add(Codes.Invariant(invariant.bytecodes()), attributes(e)); - } + CodeForest.Block bodyBlock = new CodeForest.Block(); + int body = forest.add(bodyBlock); + + for (Expr condition : s.invariants) { + int invariant = generateInvariantBlock(condition, environment, forest, context); + bodyBlock.add(Codes.Invariant(invariant), attributes(condition)); } - generateCondition(exitLab, invert(s.condition), environment, body, context); + generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); scopes.push(new LoopScope(continueLab, exitLab)); for (Stmt st : s.body) { - generate(st, environment, body, context); + generate(st, environment, bodyBlock, forest, context); } scopes.pop(); // break - body.add(Codes.Label(continueLab), attributes(s)); - - codes.add(Codes.Loop(new int[] {}, body.bytecodes()), attributes(s)); - - codes.add(Codes.Label(exitLab), attributes(s)); + bodyBlock.add(Codes.Label(continueLab), attributes(s)); + block.add(Codes.Loop(new int[] {}, body), attributes(s)); + block.add(Codes.Label(exitLab), attributes(s)); } /** @@ -1261,7 +1207,7 @@ private void generate(Stmt.While s, Environment environment, * --- Statement to be translated. * @param environment * --- Mapping from variable names to block registers. - * @param codes + * @param block * --- Code block into which this statement is to be translated. * @param context * --- Enclosing context of this statement (i.e. type, constant, @@ -1269,8 +1215,8 @@ private void generate(Stmt.While s, Environment environment, * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.DoWhile s, Environment environment, - AttributedCodeBlock codes, Context context) { + private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { // A label marking where execution continues after the do-while // loop finishes. Used when the loop condition evaluates to false // or when a break statement is encountered. @@ -1279,33 +1225,25 @@ private void generate(Stmt.DoWhile s, Environment environment, // by the continue statement. String continueLab = CodeUtils.freshLabel(); - AttributedCodeBlock body = codes.createSubBlock(); + CodeForest.Block bodyBlock = new CodeForest.Block(); + int body = forest.add(bodyBlock); + scopes.push(new LoopScope(continueLab, exitLab)); for (Stmt st : s.body) { - generate(st, environment, body, context); + generate(st, environment, bodyBlock, forest, context); } scopes.pop(); // break - if (s.invariants.size() > 0) { - // Ok, there is at least one invariant expression. Therefore, create - // an invariant bytecode. - for (Expr e : s.invariants) { - String nextLab = CodeUtils.freshLabel(); - AttributedCodeBlock invariant = body.createSubBlock(); - generateCondition(nextLab, e, environment, invariant, context); - invariant.add(Codes.Fail(), attributes(e)); - invariant.add(Codes.Label(nextLab)); - // Terminate invariant block - invariant.add(Codes.Return()); - body.add(Codes.Invariant(invariant.bytecodes()), attributes(e)); - } + for (Expr condition : s.invariants) { + int invariant = generateInvariantBlock(condition, environment, forest, context); + bodyBlock.add(Codes.Invariant(invariant), attributes(condition)); } - body.add(Codes.Label(continueLab), attributes(s)); - generateCondition(exitLab, invert(s.condition), environment, body, context); + bodyBlock.add(Codes.Label(continueLab), attributes(s)); + generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); - codes.add(Codes.Loop(new int[] {}, body.bytecodes()), attributes(s)); - codes.add(Codes.Label(exitLab), attributes(s)); + block.add(Codes.Loop(new int[] {}, body), attributes(s)); + block.add(Codes.Label(exitLab), attributes(s)); } // ========================================================================= @@ -1358,52 +1296,42 @@ private void generate(Stmt.DoWhile s, Environment environment, * one or more conditional branches. * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * @return */ - public void generateCondition(String target, Expr condition, - Environment environment, AttributedCodeBlock codes, Context context) { + public void generateCondition(String target, Expr condition, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) { try { // First, we see whether or not we can employ a special handler for // translating this condition. if (condition instanceof Expr.Constant) { - generateCondition(target, (Expr.Constant) condition, - environment, codes, context); + generateCondition(target, (Expr.Constant) condition, environment, block, forest, context); } else if (condition instanceof Expr.UnOp) { - generateCondition(target, (Expr.UnOp) condition, environment, - codes, context); + generateCondition(target, (Expr.UnOp) condition, environment, block, forest, context); } else if (condition instanceof Expr.BinOp) { - generateCondition(target, (Expr.BinOp) condition, environment, - codes, context); + generateCondition(target, (Expr.BinOp) condition, environment, block, forest, context); } else if (condition instanceof Expr.Quantifier) { - generateCondition(target, (Expr.Quantifier) condition, - environment, codes, context); - } else if (condition instanceof Expr.ConstantAccess - || condition instanceof Expr.LocalVariable - || condition instanceof Expr.AbstractInvoke - || condition instanceof Expr.AbstractIndirectInvoke - || condition instanceof Expr.FieldAccess - || condition instanceof Expr.IndexOf) { + generateCondition(target, (Expr.Quantifier) condition, environment, block, forest, context); + } else if (condition instanceof Expr.ConstantAccess || condition instanceof Expr.LocalVariable + || condition instanceof Expr.AbstractInvoke || condition instanceof Expr.AbstractIndirectInvoke + || condition instanceof Expr.FieldAccess || condition instanceof Expr.IndexOf) { // This is the default case where no special handler applies. In // this case, we simply compares the computed value against // true. In some cases, we could actually do better. For // example, !(x < 5) could be rewritten into x >= 5. - int r1 = generate(condition, environment, codes, context); + int r1 = generate(condition, environment, block, forest, context); int r2 = environment.allocate(Type.T_BOOL); - codes.add(Codes.Const(r2, Constant.V_BOOL(true)), - attributes(condition)); - codes.add(Codes.If(Type.T_BOOL, r1, r2, Codes.Comparator.EQ, - target), attributes(condition)); + block.add(Codes.Const(r2, Constant.V_BOOL(true)), attributes(condition)); + block.add(Codes.If(Type.T_BOOL, r1, r2, Codes.Comparator.EQ, target), attributes(condition)); } else { - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, - condition); + syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, condition); } } catch (SyntaxError se) { @@ -1437,16 +1365,16 @@ public void generateCondition(String target, Expr condition, * one or more conditional branches. * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * @return */ - private void generateCondition(String target, Expr.Constant c, - Environment environment, AttributedCodeBlock codes, Context context) { + private void generateCondition(String target, Expr.Constant c, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) { Constant.Bool b = (Constant.Bool) c.value; if (b.value) { - codes.add(Codes.Goto(target)); + block.add(Codes.Goto(target)); } else { // do nout } @@ -1467,38 +1395,34 @@ private void generateCondition(String target, Expr.Constant c, * one or more conditional branches. * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * @return */ - private void generateCondition(String target, Expr.BinOp v, - Environment environment, AttributedCodeBlock codes, Context context) - throws Exception { + private void generateCondition(String target, Expr.BinOp v, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) throws Exception { Expr.BOp bop = v.op; if (bop == Expr.BOp.OR) { - generateCondition(target, v.lhs, environment, codes, context); - generateCondition(target, v.rhs, environment, codes, context); + generateCondition(target, v.lhs, environment, block, forest, context); + generateCondition(target, v.rhs, environment, block, forest, context); } else if (bop == Expr.BOp.AND) { String exitLabel = CodeUtils.freshLabel(); - generateCondition(exitLabel, invert(v.lhs), environment, codes, - context); - generateCondition(target, v.rhs, environment, codes, context); - codes.add(Codes.Label(exitLabel)); + generateCondition(exitLabel, invert(v.lhs), environment, block, forest, context); + generateCondition(target, v.rhs, environment, block, forest, context); + block.add(Codes.Label(exitLabel)); } else if (bop == Expr.BOp.IS) { - generateTypeCondition(target, v, environment, codes, context); + generateTypeCondition(target, v, environment, block, forest, context); } else { Codes.Comparator cop = OP2COP(bop, v, context); - if (cop == Codes.Comparator.EQ - && v.lhs instanceof Expr.LocalVariable - && v.rhs instanceof Expr.Constant + if (cop == Codes.Comparator.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; @@ -1506,13 +1430,9 @@ private void generateCondition(String target, Expr.BinOp v, syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); } int slot = environment.get(lhs.var); - codes.add( - Codes.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), - attributes(v)); - } else if (cop == Codes.Comparator.NEQ - && v.lhs instanceof Expr.LocalVariable - && v.rhs instanceof Expr.Constant - && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { + block.add(Codes.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); + } else if (cop == Codes.Comparator.NEQ && v.lhs instanceof Expr.LocalVariable + && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. String exitLabel = CodeUtils.freshLabel(); Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; @@ -1520,15 +1440,13 @@ private void generateCondition(String target, Expr.BinOp v, syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); } int slot = environment.get(lhs.var); - codes.add(Codes.IfIs(v.srcType.raw(), slot, Type.T_NULL, - exitLabel), attributes(v)); - codes.add(Codes.Goto(target)); - codes.add(Codes.Label(exitLabel)); + block.add(Codes.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); + block.add(Codes.Goto(target)); + block.add(Codes.Label(exitLabel)); } else { - int lhs = generate(v.lhs, environment, codes, context); - int rhs = generate(v.rhs, environment, codes, context); - codes.add(Codes.If(v.srcType.raw(), lhs, rhs, cop, target), - attributes(v)); + int lhs = generate(v.lhs, environment, block, forest, context); + int rhs = generate(v.rhs, environment, block, forest, context); + block.add(Codes.If(v.srcType.raw(), lhs, rhs, cop, target), attributes(v)); } } } @@ -1552,14 +1470,13 @@ private void generateCondition(String target, Expr.BinOp v, * sequence of one or more conditional branches. * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * @return */ - private void generateTypeCondition(String target, Expr.BinOp condition, - Environment environment, AttributedCodeBlock codes, Context context) - throws Exception { + private void generateTypeCondition(String target, Expr.BinOp condition, Environment environment, + CodeForest.Block block, CodeForest forest, Context context) throws Exception { int leftOperand; if (condition.lhs instanceof Expr.LocalVariable) { @@ -1570,23 +1487,21 @@ private void generateTypeCondition(String target, Expr.BinOp condition, // the intended variable). Expr.LocalVariable lhs = (Expr.LocalVariable) condition.lhs; if (environment.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context, - condition.lhs); + syntaxError(errorMessage(UNKNOWN_VARIABLE), context, condition.lhs); } leftOperand = environment.get(lhs.var); } else { // This is the general case whether the lhs is an arbitrary variable // and, hence, retyping does not apply. Therefore, we can simply // evaluate the lhs into a temporary register as per usual. - leftOperand = generate(condition.lhs, environment, codes, context); + leftOperand = generate(condition.lhs, environment, block, forest, context); } // Note, the type checker guarantees that the rhs is a type val, so the // following cast is always safe. Expr.TypeVal rhs = (Expr.TypeVal) condition.rhs; - codes.add(Codes.IfIs(condition.srcType.raw(), leftOperand, - rhs.type.nominal(), target), attributes(condition)); + block.add(Codes.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); } /** @@ -1607,13 +1522,13 @@ private void generateTypeCondition(String target, Expr.BinOp condition, * one or more conditional branches. * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * @return */ - private void generateCondition(String target, Expr.UnOp v, - Environment environment, AttributedCodeBlock codes, Context context) { + private void generateCondition(String target, Expr.UnOp v, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) { Expr.UOp uop = v.op; switch (uop) { case NOT: @@ -1622,9 +1537,9 @@ private void generateCondition(String target, Expr.UnOp v, // through case we branch to our true destination. String label = CodeUtils.freshLabel(); - generateCondition(label, v.mhs, environment, codes, context); - codes.add(Codes.Goto(target)); - codes.add(Codes.Label(label)); + generateCondition(label, v.mhs, environment, block, forest, context); + block.add(Codes.Goto(target)); + block.add(Codes.Label(label)); return; default: // Nothing else is a valud boolean condition here. @@ -1648,67 +1563,60 @@ private void generateCondition(String target, Expr.UnOp v, * one or more conditional branches. * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * @return */ - private void generateCondition(String target, Expr.Quantifier e, - Environment environment, AttributedCodeBlock codes, Context context) { + private void generateCondition(String target, Expr.Quantifier e, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) { String exit = CodeUtils.freshLabel(); - generate(e.sources.iterator(), target, exit, e, environment, codes, - context); + generate(e.sources.iterator(), target, exit, e, environment, block, forest, context); switch (e.cop) { case NONE: - codes.add(Codes.Goto(target)); - codes.add(Codes.Label(exit)); + block.add(Codes.Goto(target)); + block.add(Codes.Label(exit)); break; case SOME: break; case ALL: - codes.add(Codes.Goto(target)); - codes.add(Codes.Label(exit)); + block.add(Codes.Goto(target)); + block.add(Codes.Label(exit)); break; } } - private void generate(Iterator> srcIterator, - String trueLabel, String falseLabel, Expr.Quantifier e, - Environment environment, AttributedCodeBlock codes, Context context) { + private void generate(Iterator> srcIterator, String trueLabel, String falseLabel, + Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { if (srcIterator.hasNext()) { // This is the inductive case (i.e. an outer loop) Triple src = srcIterator.next(); // First, determine the src slot. - int varSlot = environment.allocate(Type.T_INT, src.first()); - int startSlot = generate(src.second(), environment, codes, context); - int endSlot = generate(src.third(), environment, codes, context); + int varSlot = environment.allocate(Type.T_INT, src.first()); + int startSlot = generate(src.second(), environment, block, forest, context); + int endSlot = generate(src.third(), environment, block, forest, context); // Second, recursively generate remaining parts - AttributedCodeBlock block = codes.createSubBlock(); - generate(srcIterator, trueLabel, falseLabel, e, environment, block, - context); - + CodeForest.Block bodyBlock = new CodeForest.Block(); + int body = forest.add(bodyBlock); + generate(srcIterator, trueLabel, falseLabel, e, environment, bodyBlock, forest, context); // Finally, create the forall loop bytecode - codes.add(Codes.Quantify(startSlot, endSlot, varSlot, new int[0], - block.bytecodes()), attributes(e)); + block.add(Codes.Quantify(startSlot, endSlot, varSlot, new int[0], body), attributes(e)); } else { // This is the base case (i.e. the innermost loop) switch (e.cop) { case NONE: - generateCondition(falseLabel, e.condition, environment, codes, - context); + generateCondition(falseLabel, e.condition, environment, block, forest, context); break; case SOME: - generateCondition(trueLabel, e.condition, environment, codes, - context); + generateCondition(trueLabel, e.condition, environment, block, forest, context); break; case ALL: - generateCondition(falseLabel, invert(e.condition), environment, - codes, context); + generateCondition(falseLabel, invert(e.condition), environment, block, forest, context); break; } } @@ -1718,25 +1626,23 @@ private void generate(Iterator> srcIterator, // Multi-Expressions // ========================================================================= - - public int[] generate(Expr.Multi expression, Environment environment, - AttributedCodeBlock codes, Context context) { + public int[] generate(Expr.Multi expression, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { List returns = expression.returns(); int[] targets = new int[returns.size()]; - for(int i=0;i!=targets.length;++i) { + for (int i = 0; i != targets.length; ++i) { targets[i] = environment.allocate(returns.get(i).raw()); } try { - if(expression instanceof Expr.FunctionOrMethodCall) { - Expr.FunctionOrMethodCall fmc = (Expr.FunctionOrMethodCall) expression; - generateStmt(fmc,environment,codes,context,targets); - } else if(expression instanceof Expr.IndirectFunctionOrMethodCall) { - Expr.IndirectFunctionOrMethodCall fmc = (Expr.IndirectFunctionOrMethodCall) expression; - generateStmt(fmc,environment,codes,context,targets); + if (expression instanceof Expr.FunctionOrMethodCall) { + Expr.FunctionOrMethodCall fmc = (Expr.FunctionOrMethodCall) expression; + generateStmt(fmc, environment, block, forest, context, targets); + } else if (expression instanceof Expr.IndirectFunctionOrMethodCall) { + Expr.IndirectFunctionOrMethodCall fmc = (Expr.IndirectFunctionOrMethodCall) expression; + generateStmt(fmc, environment, block, forest, context, targets); } else { // should be dead-code - internalFailure("unknown expression: " - + expression.getClass().getName(), context, expression); + internalFailure("unknown expression: " + expression.getClass().getName(), context, expression); } } catch (ResolveError rex) { syntaxError(rex.getMessage(), context, expression, rex); @@ -1748,24 +1654,22 @@ public int[] generate(Expr.Multi expression, Environment environment, // done return targets; } - - public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment, AttributedCodeBlock codes, - Context context, int... targets) throws ResolveError { + + public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment, CodeForest.Block block, + CodeForest forest, Context context, int... targets) throws ResolveError { // - int[] operands = generate(expr.arguments, environment, codes, context); - codes.add(Codes.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); + int[] operands = generate(expr.arguments, environment, block, forest, context); + block.add(Codes.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); } - - public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, AttributedCodeBlock codes, - Context context, int... targets) throws ResolveError { + public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, CodeForest.Block block, + CodeForest forest, Context context, int... targets) throws ResolveError { // - int operand = generate(expr.src, environment, codes, context); - int[] operands = generate(expr.arguments, environment, codes, context); - codes.add(Codes.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); + int operand = generate(expr.src, environment, block, forest, context); + int[] operands = generate(expr.arguments, environment, block, forest, context); + block.add(Codes.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); } - // ========================================================================= // Expressions // ========================================================================= @@ -1779,75 +1683,55 @@ public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment env * --- Source-level expression to be translated * @param environment * --- Mapping from variable names to to slot numbers. - * @param codes + * @param block * --- List of bytecodes onto which translation should be * appended. * * @return --- the register */ - public int generate(Expr expression, Environment environment, - AttributedCodeBlock codes, Context context) { + public int generate(Expr expression, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { try { if (expression instanceof Expr.Constant) { - return generate((Expr.Constant) expression, environment, codes, - context); + return generate((Expr.Constant) expression, environment, block, forest, context); } else if (expression instanceof Expr.LocalVariable) { - return generate((Expr.LocalVariable) expression, environment, - codes, context); + return generate((Expr.LocalVariable) expression, environment, block, forest, context); } else if (expression instanceof Expr.ConstantAccess) { - return generate((Expr.ConstantAccess) expression, environment, - codes, context); + return generate((Expr.ConstantAccess) expression, environment, block, forest, context); } else if (expression instanceof Expr.ArrayInitialiser) { - return generate((Expr.ArrayInitialiser) expression, environment, codes, - context); + return generate((Expr.ArrayInitialiser) expression, environment, block, forest, context); } else if (expression instanceof Expr.ArrayGenerator) { - return generate((Expr.ArrayGenerator) expression, environment, codes, - context); + return generate((Expr.ArrayGenerator) expression, environment, block, forest, context); } else if (expression instanceof Expr.BinOp) { - return generate((Expr.BinOp) expression, environment, codes, - context); + return generate((Expr.BinOp) expression, environment, block, forest, context); } else if (expression instanceof Expr.LengthOf) { - return generate((Expr.LengthOf) expression, environment, codes, - context); + return generate((Expr.LengthOf) expression, environment, block, forest, context); } else if (expression instanceof Expr.Dereference) { - return generate((Expr.Dereference) expression, environment, - codes, context); + return generate((Expr.Dereference) expression, environment, block, forest, context); } else if (expression instanceof Expr.Cast) { - return generate((Expr.Cast) expression, environment, codes, - context); + return generate((Expr.Cast) expression, environment, block, forest, context); } else if (expression instanceof Expr.IndexOf) { - return generate((Expr.IndexOf) expression, environment, codes, - context); + return generate((Expr.IndexOf) expression, environment, block, forest, context); } else if (expression instanceof Expr.UnOp) { - return generate((Expr.UnOp) expression, environment, codes, - context); + return generate((Expr.UnOp) expression, environment, block, forest, context); } else if (expression instanceof Expr.FunctionOrMethodCall) { - return generate((Expr.FunctionOrMethodCall) expression, environment, - codes, context); + return generate((Expr.FunctionOrMethodCall) expression, environment, block, forest, context); } else if (expression instanceof Expr.IndirectFunctionCall) { - return generate((Expr.IndirectFunctionCall) expression, - environment, codes, context); + return generate((Expr.IndirectFunctionCall) expression, environment, block, forest, context); } else if (expression instanceof Expr.IndirectMethodCall) { - return generate((Expr.IndirectMethodCall) expression, - environment, codes, context); + return generate((Expr.IndirectMethodCall) expression, environment, block, forest, context); } else if (expression instanceof Expr.Quantifier) { - return generate((Expr.Quantifier) expression, environment, - codes, context); + return generate((Expr.Quantifier) expression, environment, block, forest, context); } else if (expression instanceof Expr.FieldAccess) { - return generate((Expr.FieldAccess) expression, environment, - codes, context); + return generate((Expr.FieldAccess) expression, environment, block, forest, context); } else if (expression instanceof Expr.Record) { - return generate((Expr.Record) expression, environment, codes, - context); + return generate((Expr.Record) expression, environment, block, forest, context); } else if (expression instanceof Expr.FunctionOrMethod) { - return generate((Expr.FunctionOrMethod) expression, - environment, codes, context); + return generate((Expr.FunctionOrMethod) expression, environment, block, forest, context); } else if (expression instanceof Expr.Lambda) { - return generate((Expr.Lambda) expression, environment, codes, - context); + return generate((Expr.Lambda) expression, environment, block, forest, context); } else if (expression instanceof Expr.New) { - return generate((Expr.New) expression, environment, codes, - context); + return generate((Expr.New) expression, environment, block, forest, context); } else { // should be dead-code internalFailure("unknown expression: " @@ -1865,40 +1749,39 @@ public int generate(Expr expression, Environment environment, } public int generate(Expr.FunctionOrMethodCall expr, Environment environment, - AttributedCodeBlock codes, Context context) throws ResolveError { + CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { int target = environment.allocate(expr.result().raw()); - generateStmt(expr, environment, codes, context, target); + generateStmt(expr, environment, block, forest, context, target); return target; } public int generate(Expr.IndirectFunctionOrMethodCall expr, - Environment environment, AttributedCodeBlock codes, Context context) + Environment environment, CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { int target = environment.allocate(expr.result().raw()); - generateStmt(expr, environment, codes, context, target); + generateStmt(expr, environment, block, forest, context, target); return target; } - private int generate(Expr.Constant expr, Environment environment, - AttributedCodeBlock codes, Context context) { + private int generate(Expr.Constant expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { Constant val = expr.value; int target = environment.allocate(val.type()); - codes.add(Codes.Const(target, expr.value), attributes(expr)); + block.add(Codes.Const(target, expr.value), attributes(expr)); return target; } - private int generate(Expr.FunctionOrMethod expr, Environment environment, - AttributedCodeBlock codes, Context context) { + private int generate(Expr.FunctionOrMethod expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { Type.FunctionOrMethod rawType = expr.type.raw(); Type.FunctionOrMethod nominalType = expr.type.nominal(); int target = environment.allocate(rawType); - codes.add(Codes.Lambda(nominalType, target, Collections.EMPTY_LIST, expr.nid), - attributes(expr)); + block.add(Codes.Lambda(nominalType, target, Collections.EMPTY_LIST, expr.nid), attributes(expr)); return target; } - private int generate(Expr.Lambda expr, Environment environment, - AttributedCodeBlock codes, Context context) { + private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { Type.FunctionOrMethod tfm = expr.type.raw(); List tfm_params = tfm.params(); List expr_params = expr.parameters; @@ -1906,15 +1789,15 @@ private int generate(Expr.Lambda expr, Environment environment, // Create environment for the lambda body. ArrayList operands = new ArrayList(); ArrayList paramTypes = new ArrayList(); - ArrayList declarations = new ArrayList(); - Environment benv = new Environment(); + CodeForest bodyForest = new CodeForest(); + List declarations = bodyForest.registers(); + Environment benv = new Environment(); for (int i = 0; i != tfm_params.size(); ++i) { Type type = tfm_params.get(i); String name = expr_params.get(i).name; benv.allocate(type, name); paramTypes.add(type); - operands.add(Codes.NULL_REG); - declarations.add(new VariableDeclarations.Declaration(type,name)); + declarations.add(new CodeForest.Register(type,name)); } for (Pair v : Exprs.uses(expr.body, context)) { if (benv.get(v.second()) == null) { @@ -1922,18 +1805,18 @@ private int generate(Expr.Lambda expr, Environment environment, benv.allocate(type, v.second()); paramTypes.add(type); operands.add(environment.get(v.second())); - declarations.add(new VariableDeclarations.Declaration(type,v.second())); + declarations.add(new CodeForest.Register(type,v.second())); } } - // Generate body based on current environment - AttributedCodeBlock body = new AttributedCodeBlock( - new SourceLocationMap()); + + CodeForest.Block bodyBlock = new CodeForest.Block(); + bodyForest.addAsRoot(bodyBlock); if (tfm.returns().isEmpty()) { - body.add(Codes.Return(), attributes(expr)); + bodyBlock.add(Codes.Return(), attributes(expr)); } else { - int target = generate(expr.body, benv, body, context); - body.add(Codes.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), attributes(expr)); + int target = generate(expr.body, benv, bodyBlock, bodyForest, context); + bodyBlock.add(Codes.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), attributes(expr)); } // Add type information for all temporary registers allocated @@ -1941,9 +1824,8 @@ private int generate(Expr.Lambda expr, Environment environment, // about declared variables. for(int i=declarations.size();i!=benv.size();i=i+1) { Type t = benv.type(i); - declarations.add(new VariableDeclarations.Declaration(t,null)); - } - + declarations.add(new CodeForest.Register(t,null)); + } // Create concrete type for private lambda function Type.FunctionOrMethod cfm; if (tfm instanceof Type.Function) { @@ -1958,65 +1840,62 @@ private int generate(Expr.Lambda expr, Environment environment, ArrayList modifiers = new ArrayList(); modifiers.add(Modifier.PRIVATE); WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod( - modifiers, name, cfm, body, Collections.EMPTY_LIST, - Collections.EMPTY_LIST, attributes(expr)); - lambda.attributes().add(new VariableDeclarations(declarations)); + modifiers, name, cfm, bodyForest, 0, 0, attributes(expr)); lambdas.add(lambda); Path.ID mid = context.file().module; NameID nid = new NameID(mid, name); // Finally, create the lambda int target = environment.allocate(tfm); - codes.add(Codes.Lambda(cfm, target, operands, nid), attributes(expr)); + block.add(Codes.Lambda(cfm, target, operands, nid), attributes(expr)); return target; } private int generate(Expr.ConstantAccess expr, Environment environment, - AttributedCodeBlock codes, Context context) throws ResolveError { + CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { Constant val = expr.value; int target = environment.allocate(val.type()); - codes.add(Codes.Const(target, val), attributes(expr)); + block.add(Codes.Const(target, val), attributes(expr)); return target; } - private int generate(Expr.LocalVariable expr, Environment environment, - AttributedCodeBlock codes, Context context) throws ResolveError { + private int generate(Expr.LocalVariable expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) throws ResolveError { if (environment.get(expr.var) != null) { int target = environment.get(expr.var); Type type = expr.result().raw(); return target; } else { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), context, - expr); + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), context, expr); return -1; } } private int generate(Expr.UnOp expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int operand = generate(expr.mhs, environment, codes, context); + CodeForest.Block block, CodeForest forest, Context context) { + int operand = generate(expr.mhs, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); switch (expr.op) { case NEG: - codes.add(Codes.UnaryOperator(expr.result().raw(), target, operand, + block.add(Codes.UnaryOperator(expr.result().raw(), target, operand, Codes.UnaryOperatorKind.NEG), attributes(expr)); break; case INVERT: - codes.add(Codes.Invert(expr.result().raw(), target, operand), + block.add(Codes.Invert(expr.result().raw(), target, operand), attributes(expr)); break; case NOT: String falseLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); - generateCondition(falseLabel, expr.mhs, environment, codes, context); - codes.add(Codes.Const(target, Constant.V_BOOL(true)), + generateCondition(falseLabel, expr.mhs, environment, block, forest, context); + block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(expr)); - codes.add(Codes.Goto(exitLabel)); - codes.add(Codes.Label(falseLabel)); - codes.add(Codes.Const(target, Constant.V_BOOL(false)), + block.add(Codes.Goto(exitLabel)); + block.add(Codes.Label(falseLabel)); + block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(expr)); - codes.add(Codes.Label(exitLabel)); + block.add(Codes.Label(exitLabel)); break; default: // should be dead-code @@ -2027,46 +1906,45 @@ private int generate(Expr.UnOp expr, Environment environment, return target; } - private int generate(Expr.LengthOf expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int operand = generate(expr.src, environment, codes, context); + private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { + int operand = generate(expr.src, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.LengthOf((Type.EffectiveArray) expr.srcType.raw(), - target, operand), attributes(expr)); + block.add(Codes.LengthOf((Type.EffectiveArray) expr.srcType.raw(), target, operand), attributes(expr)); return target; } private int generate(Expr.Dereference expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int operand = generate(expr.src, environment, codes, context); + CodeForest.Block block, CodeForest forest, Context context) { + int operand = generate(expr.src, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.Dereference(expr.srcType.raw(), target, operand), + block.add(Codes.Dereference(expr.srcType.raw(), target, operand), attributes(expr)); return target; } private int generate(Expr.IndexOf expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int srcOperand = generate(expr.src, environment, codes, context); - int idxOperand = generate(expr.index, environment, codes, context); + CodeForest.Block block, CodeForest forest, Context context) { + int srcOperand = generate(expr.src, environment, block, forest, context); + int idxOperand = generate(expr.index, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.IndexOf((Type.Array) expr.srcType.raw(), target, srcOperand, + block.add(Codes.IndexOf((Type.Array) expr.srcType.raw(), target, srcOperand, idxOperand), attributes(expr)); return target; } private int generate(Expr.Cast expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int operand = generate(expr.expr, environment, codes, context); + CodeForest.Block block, CodeForest forest, Context context) { + int operand = generate(expr.expr, environment, block, forest, context); Type from = expr.expr.result().raw(); Type to = expr.result().raw(); int target = environment.allocate(to); - codes.add(Codes.Convert(from, target, operand, to), attributes(expr)); + block.add(Codes.Convert(from, target, operand, to), attributes(expr)); return target; } private int generate(Expr.BinOp v, Environment environment, - AttributedCodeBlock codes, Context context) throws Exception { + CodeForest.Block block, CodeForest forest, Context context) throws Exception { // could probably use a range test for this somehow if (v.op == Expr.BOp.EQ || v.op == Expr.BOp.NEQ || v.op == Expr.BOp.LT @@ -2075,100 +1953,99 @@ private int generate(Expr.BinOp v, Environment environment, || v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { String trueLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); - generateCondition(trueLabel, v, environment, codes, context); + generateCondition(trueLabel, v, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - codes.add(Codes.Const(target, Constant.V_BOOL(false)), + block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(v)); - codes.add(Codes.Goto(exitLabel)); - codes.add(Codes.Label(trueLabel)); - codes.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(v)); - codes.add(Codes.Label(exitLabel)); + block.add(Codes.Goto(exitLabel)); + block.add(Codes.Label(trueLabel)); + block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(v)); + block.add(Codes.Label(exitLabel)); return target; } else { - int leftOperand = generate(v.lhs, environment, codes, context); - int rightOperand = generate(v.rhs, environment, codes, context); + int leftOperand = generate(v.lhs, environment, block, forest, context); + int rightOperand = generate(v.rhs, environment, block, forest, context); Type result = v.result().raw(); int target = environment.allocate(result); - - codes.add(Codes.BinaryOperator(result, target, leftOperand, - rightOperand, OP2BOP(v.op, v, context)), attributes(v)); + + block.add(Codes.BinaryOperator(result, target, leftOperand, rightOperand, OP2BOP(v.op, v, context)), + attributes(v)); return target; } } private int generate(Expr.ArrayInitialiser expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int[] operands = generate(expr.arguments, environment, codes, context); + CodeForest.Block block, CodeForest forest, Context context) { + int[] operands = generate(expr.arguments, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.NewArray((Type.Array) expr.type.raw(), target, operands), + block.add(Codes.NewArray((Type.Array) expr.type.raw(), target, operands), attributes(expr)); return target; } - private int generate(Expr.ArrayGenerator expr, Environment environment, AttributedCodeBlock codes, Context context) { - int element = generate(expr.element, environment, codes, context); - int count = generate(expr.count, environment, codes, context); + private int generate(Expr.ArrayGenerator expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { + int element = generate(expr.element, environment, block, forest, context); + int count = generate(expr.count, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.ArrayGenerator((Type.Array) expr.type.raw(), target, element, count), attributes(expr)); + block.add(Codes.ArrayGenerator((Type.Array) expr.type.raw(), target, element, count), attributes(expr)); return target; } - private int generate(Expr.Quantifier e, Environment environment, - AttributedCodeBlock codes, Context context) { + private int generate(Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { String trueLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); - generateCondition(trueLabel, e, environment, codes, context); + generateCondition(trueLabel, e, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - codes.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(e)); - codes.add(Codes.Goto(exitLabel)); - codes.add(Codes.Label(trueLabel)); - codes.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(e)); - codes.add(Codes.Label(exitLabel)); + block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(e)); + block.add(Codes.Goto(exitLabel)); + block.add(Codes.Label(trueLabel)); + block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(e)); + block.add(Codes.Label(exitLabel)); return target; } - private int generate(Expr.Record expr, Environment environment, - AttributedCodeBlock codes, Context context) { + private int generate(Expr.Record expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { ArrayList keys = new ArrayList(expr.fields.keySet()); Collections.sort(keys); int[] operands = new int[expr.fields.size()]; for (int i = 0; i != operands.length; ++i) { String key = keys.get(i); Expr arg = expr.fields.get(key); - operands[i] = generate(arg, environment, codes, context); + operands[i] = generate(arg, environment, block, forest, context); } int target = environment.allocate(expr.result().raw()); - codes.add(Codes.NewRecord((Type.Record) expr.result().raw(), target, - operands), attributes(expr)); + block.add(Codes.NewRecord((Type.Record) expr.result().raw(), target, operands), attributes(expr)); return target; } - private int generate(Expr.FieldAccess expr, Environment environment, - AttributedCodeBlock codes, Context context) { - int operand = generate(expr.src, environment, codes, context); + private int generate(Expr.FieldAccess expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { + int operand = generate(expr.src, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), - target, operand, expr.name), attributes(expr)); + block.add(Codes.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), + attributes(expr)); return target; } private int generate(Expr.New expr, Environment environment, - AttributedCodeBlock codes, Context context) throws ResolveError { - int operand = generate(expr.expr, environment, codes, context); + CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { + int operand = generate(expr.expr, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - codes.add(Codes.NewObject(expr.type.raw(), target, operand)); + block.add(Codes.NewObject(expr.type.raw(), target, operand)); return target; } - private int[] generate(List arguments, Environment environment, - AttributedCodeBlock codes, Context context) { + private int[] generate(List arguments, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int[] operands = new int[arguments.size()]; for (int i = 0; i != operands.length; ++i) { Expr arg = arguments.get(i); - operands[i] = generate(arg, environment, codes, context); + operands[i] = generate(arg, environment, block, forest, context); } return operands; } @@ -2292,7 +2169,7 @@ private static Expr invert(Expr e) { * @param declarations */ public void buildVariableDeclarations(List block, - List declarations, + List declarations, Environment environment, WhileyFile.Context context) { // for (int i = 0; i != block.size(); ++i) { @@ -2302,7 +2179,7 @@ public void buildVariableDeclarations(List block, } public void buildVariableDeclarations(Stmt stmt, - List declarations, Environment environment, + List declarations, Environment environment, WhileyFile.Context context) { if (stmt instanceof Assign || stmt instanceof Assert || stmt instanceof Assume || stmt instanceof Return @@ -2317,7 +2194,7 @@ public void buildVariableDeclarations(Stmt stmt, return; } else if (stmt instanceof VariableDeclaration) { VariableDeclaration d = (VariableDeclaration) stmt; - declarations.add(new VariableDeclarations.Declaration(d.type.nominal(),d.parameter.name)); + declarations.add(new CodeForest.Register(d.type.nominal(),d.parameter.name)); environment.allocate(d.type.raw(),d.parameter.name); } else if (stmt instanceof IfElse) { IfElse s = (IfElse) stmt; @@ -2348,7 +2225,7 @@ public void buildVariableDeclarations(Stmt stmt, * @param elem * @return */ - private static Collection attributes( + private static List attributes( SyntacticElement elem) { ArrayList attrs = new ArrayList(); Attribute.Source s = elem.attribute(Attribute.Source.class); @@ -2426,8 +2303,13 @@ public void put(int idx, String v) { var2idx.put(v, idx); } - public ArrayList asList() { - return idx2type; + public ArrayList asRegisters() { + ArrayList registers = new ArrayList(); + for(int i=0;i!=idx2type.size();++i) { + Type t = idx2type.get(i); + registers.add(new CodeForest.Register(t, get(i))); + } + return registers; } public String toString() { diff --git a/modules/wyc/src/wyc/builder/FlowTypeChecker.java b/modules/wyc/src/wyc/builder/FlowTypeChecker.java index a6d5ceee52..0cc3fb495a 100644 --- a/modules/wyc/src/wyc/builder/FlowTypeChecker.java +++ b/modules/wyc/src/wyc/builder/FlowTypeChecker.java @@ -343,7 +343,7 @@ private Environment propagate(ArrayList block, Environment environment) { * recursively propagate type information through them as well. * * - * @param block + * @param forest * Block of statements to flow-sensitively type check * @param environment * Determines the type of all variables immediately going into diff --git a/modules/wyc/src/wyc/util/WycBuildTask.java b/modules/wyc/src/wyc/util/WycBuildTask.java index bdd3c6b555..179dc7a70e 100644 --- a/modules/wyc/src/wyc/util/WycBuildTask.java +++ b/modules/wyc/src/wyc/util/WycBuildTask.java @@ -146,24 +146,14 @@ public String suffix(Content.Type t) { public static final List defaultPipeline = Collections .unmodifiableList(new ArrayList() { { - // add(new Pipeline.Template(WyilFilePrinter.class, - // Collections.EMPTY_MAP)); add(new Pipeline.Template(DefiniteAssignmentCheck.class, Collections.EMPTY_MAP)); add(new Pipeline.Template(ModuleCheck.class, Collections.EMPTY_MAP)); add(new Pipeline.Template(LoopVariants.class, Collections.EMPTY_MAP)); -// add(new Pipeline.Template(ConstantPropagation.class, -// Collections.EMPTY_MAP)); add(new Pipeline.Template(CoercionCheck.class, Collections.EMPTY_MAP)); -// add(new Pipeline.Template(DeadCodeElimination.class, -// Collections.EMPTY_MAP)); -// add(new Pipeline.Template(LiveVariablesAnalysis.class, -// Collections.EMPTY_MAP)); - // add(new Pipeline.Template(WyilFilePrinter.class, - // Collections.EMPTY_MAP)); } }); @@ -174,13 +164,10 @@ public String suffix(Content.Type t) { */ static { Pipeline.register(DefiniteAssignmentCheck.class); - Pipeline.register(LoopVariants.class); - Pipeline.register(ConstantPropagation.class); + Pipeline.register(LoopVariants.class); Pipeline.register(ModuleCheck.class); Pipeline.register(CoercionCheck.class); - Pipeline.register(WyilFilePrinter.class); - Pipeline.register(DeadCodeElimination.class); - Pipeline.register(LiveVariablesAnalysis.class); + Pipeline.register(WyilFilePrinter.class); } /** diff --git a/modules/wyil/src/wyil/attributes/SourceLocationMap.java b/modules/wyil/src/wyil/attributes/SourceLocationMap.java deleted file mode 100644 index 739eb3f786..0000000000 --- a/modules/wyil/src/wyil/attributes/SourceLocationMap.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2014, David J. Pearce (djp@ecs.vuw.ac.nz) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package wyil.attributes; - -import wyil.lang.Attribute; -import wyil.lang.CodeBlock; -import wyil.util.AbstractAttributeMap; - -/** - *

- * The source location map is used to map individual bytecodes to their - * positions within their originating source file(s). The information stored for - * each bytecode is simply the character start and end positions within the - * original source file. - *

- * - *

- * NOTE: Multiple bytecodes may map to the same or overlapping positions - * within the source file. Also, bytecodes within the same file can map to - * different source files. - *

- * - * @author David J. Pearce - * - */ -public class SourceLocationMap extends AbstractAttributeMap - implements Attribute.Map { - - @Override - public Class type() { - return SourceLocation.class; - } - - @Override - public void put(CodeBlock.Index location, SourceLocation data) { - super.put(location, data); - } - - public SourceLocation get(CodeBlock.Index location) { - SourceLocation l = super.get(location); - return l; - } -} diff --git a/modules/wyil/src/wyil/builders/VcBranch.java b/modules/wyil/src/wyil/builders/VcBranch.java index 41d39480b3..ddf31e5266 100644 --- a/modules/wyil/src/wyil/builders/VcBranch.java +++ b/modules/wyil/src/wyil/builders/VcBranch.java @@ -127,7 +127,7 @@ public enum State { /** * The bytecode index into the above block that this branch is currently at. */ - private CodeBlock.Index pc; + private CodeForest.Index pc; /** * Indicates the state of this branch. In particular, whether its active, @@ -154,7 +154,7 @@ public VcBranch(int numInputs, String[] prefixes) { this.environment = new Expr[numSlots]; this.versions = new int[numSlots]; this.constraints = null; - this.pc = new CodeBlock.Index(CodeBlock.Index.ROOT); + this.pc = new CodeForest.Index(CodeForest.Index.ROOT); this.state = State.ACTIVE; if (prefixes == null) { @@ -216,7 +216,7 @@ private VcBranch(VcBranch[] parents, Expr[] environment, * * @return */ - public CodeBlock.Index pc() { + public CodeForest.Index pc() { return pc; } @@ -285,7 +285,7 @@ public String[] prefixes() { * * @param pc */ - public void goTo(CodeBlock.Index pc) { + public void goTo(CodeForest.Index pc) { if (state != State.ACTIVE) { // Sanity check throw new IllegalArgumentException( diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java index eeb9032d1e..ad12ed4c9f 100644 --- a/modules/wyil/src/wyil/builders/VcGenerator.java +++ b/modules/wyil/src/wyil/builders/VcGenerator.java @@ -39,7 +39,7 @@ import wyil.attributes.VariableDeclarations; import wyil.builders.VcBranch.State; import wyil.lang.*; -import wyil.lang.CodeBlock.Index; +import wyil.lang.CodeForest.Index; import wyil.util.AttributedCodeBlock; import wyil.util.ErrorMessages; import wyil.util.TypeExpander; @@ -127,7 +127,7 @@ protected void transform(WyilFile.Type typeDecl, WyilFile wyilFile) { // expression. Type[] environment = new Type[] { typeDecl.type() }; List exitBranches = transform(master, - CodeBlock.Index.ROOT, true, environment, body); + CodeForest.Index.ROOT, true, environment, body); // At this point, we are guaranteed exactly one exit branch because // there is only ever one exit point from an invariant. for (VcBranch exitBranch : exitBranches) { @@ -163,13 +163,13 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // an invocation site, we can call this macro directly. String prefix = method.name() + "_requires_"; for (int i = 0; i != precondition.size(); ++i) { - buildMacroBlock(prefix + i, CodeBlock.Index.ROOT, + buildMacroBlock(prefix + i, CodeForest.Index.ROOT, precondition.get(i), fmm.params(), true); } prefix = method.name() + "_ensures_"; List postEnvironment = append(fmm.params(), fmm.returns()); for (int i = 0; i != postcondition.size(); ++i) { - buildMacroBlock(prefix + i, CodeBlock.Index.ROOT, + buildMacroBlock(prefix + i, CodeForest.Index.ROOT, postcondition.get(i), postEnvironment, true); } @@ -216,7 +216,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // active. Terminated branches are those which have reached a return // statement, whilst failed branches are those which have reached a fail // statement. - List exitBranches = transform(master, CodeBlock.Index.ROOT, + List exitBranches = transform(master, CodeForest.Index.ROOT, false, bodyEnvironment, body); // Examine all branches produced from the body. Each should be in one of @@ -324,10 +324,10 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { * * @return List of branches which reach the end of the block. */ - public List transform(VcBranch branch, CodeBlock.Index root, + public List transform(VcBranch branch, CodeForest.Index root, boolean isInvariant, Type[] environment, AttributedCodeBlock block) { // Construct the label map which is needed for conditional branches - Map labels = CodeUtils.buildLabelMap(block); + Map labels = CodeUtils.buildLabelMap(block); Pair> p = transform(root, 0, branch, false, isInvariant, environment, labels, block); // Ok, return list of exit branches @@ -380,12 +380,12 @@ public List transform(VcBranch branch, CodeBlock.Index root, * this is marked as terminated. * */ - protected Pair> transform(CodeBlock.Index parent, + protected Pair> transform(CodeForest.Index parent, int offset, VcBranch entryState, boolean breakOnInvariant, boolean isInvariant, - Type[] environment, Map labels, AttributedCodeBlock block) { + Type[] environment, Map labels, AttributedCodeBlock block) { // Move state to correct location - CodeBlock.Index start = new CodeBlock.Index(parent); - entryState.goTo(new CodeBlock.Index(parent, offset)); + CodeForest.Index start = new CodeForest.Index(parent); + entryState.goTo(new CodeForest.Index(parent, offset)); // Construct list of branches being processed. Stack worklist = new Stack(); ArrayList exitBranches = new ArrayList(); @@ -398,7 +398,7 @@ protected Pair> transform(CodeBlock.Index parent, // The program counter represents the current position of the branch // being explored. - CodeBlock.Index pc = branch.pc(); + CodeForest.Index pc = branch.pc(); // Determine whether to continue executing this branch, or whether // it has completed within this scope. @@ -544,7 +544,7 @@ private void joinAll(ArrayList branches) { for (int i = 0; i < branches.size(); ++i) { VcBranch i_branch = branches.get(i); if (i_branch != null) { - CodeBlock.Index i_pc = i_branch.pc(); + CodeForest.Index i_pc = i_branch.pc(); // Now, the goal is to identify all remaining branches which are // at the same location. These can then be all joined together // in one go. First, we count how many their are @@ -840,7 +840,7 @@ public Pair[] updateChecks(Codes.Update code, VcBranch branch) { * location information. */ protected List transform(Codes.Loop code, VcBranch branch, - Type[] environment, Map labels, + Type[] environment, Map labels, AttributedCodeBlock block) { return transformLoopHelper(code, branch, environment, labels, block) .second(); @@ -876,7 +876,7 @@ protected List transform(Codes.Loop code, VcBranch branch, */ protected List transform(Codes.Quantify code, VcBranch branch, boolean isInvariant, Type[] environment, - Map labels, AttributedCodeBlock block) { + Map labels, AttributedCodeBlock block) { // Write an arbitrary value to the index operand. This is necessary to // ensure that there is something there if it is used within the loop // body. @@ -932,7 +932,7 @@ protected List extractQuantifiers(Codes.Quantify code, VcBranch b = exitBranches.get(i); Expr body = generateAssumptions(b, root); body = new Expr.Binary(Expr.Binary.Op.AND, range, body); - CodeBlock.Index target = b.pc(); + CodeForest.Index target = b.pc(); b = root.fork(); b.assume(new Expr.Exists(pattern, body)); b.goTo(target); @@ -961,10 +961,10 @@ protected List extractQuantifiers(Codes.Quantify code, */ protected Pair> transformQuantifierHelper( Codes.Loop code, VcBranch branch, boolean isInvariant, - Type[] environment, Map labels, + Type[] environment, Map labels, AttributedCodeBlock block) { // The loopPc gives the block index of the loop bytecode. - CodeBlock.Index loopPc = branch.pc(); + CodeForest.Index loopPc = branch.pc(); // This is the easy case, as there is no loop invariant. Therefore, // we just havoc modified variables at the beginning and then allow // branches to exit the loop as normal. Branches which reach the end @@ -1001,9 +1001,9 @@ protected Pair> transformQuantifierHelper( */ protected Pair> transformLoopHelper( Codes.Loop code, VcBranch branch, Type[] environment, - Map labels, AttributedCodeBlock block) { + Map labels, AttributedCodeBlock block) { // The loopPc gives the block index of the loop bytecode. - CodeBlock.Index loopPc = branch.pc(); + CodeForest.Index loopPc = branch.pc(); int invariantOffset = getInvariantOffset(code); // First thing we need to do is determine whether or not this loop has a @@ -1021,7 +1021,7 @@ protected Pair> transformLoopHelper( numberOfInvariants = numberOfInvariants+1; } // - CodeBlock.Index firstInvariantPc = new CodeBlock.Index(loopPc, + CodeForest.Index firstInvariantPc = new CodeForest.Index(loopPc, invariantOffset); String invariantMacroPrefix = method.name() + "_loopinvariant_"; @@ -1060,7 +1060,7 @@ protected Pair> transformLoopHelper( // verification condition that asserts each invariant macro given the // current branch state. for (int i = 0; i != numberOfInvariants; ++i) { - CodeBlock.Index invariantPc = firstInvariantPc.next(i); + CodeForest.Index invariantPc = firstInvariantPc.next(i); String invariantMacroName = invariantMacroPrefix + invariantPc.toString().replace(".", "_"); Expr.Invoke invariant = buildInvariantCall(activeBranch, @@ -1077,7 +1077,7 @@ protected Pair> transformLoopHelper( // the invariant macro holds in the current branch state. havocVariables(code.modifiedOperands, activeBranch); for (int i = 0; i != numberOfInvariants; ++i) { - CodeBlock.Index invariantPc = firstInvariantPc.next(i); + CodeForest.Index invariantPc = firstInvariantPc.next(i); String invariantMacroName = invariantMacroPrefix + invariantPc.toString().replace(".", "_"); Expr.Invoke invariant = buildInvariantCall(activeBranch, invariantMacroName, @@ -1097,7 +1097,7 @@ protected Pair> transformLoopHelper( // verification condition that asserts the invariant macro given the // current branch state. for (int i = 0; i != numberOfInvariants; ++i) { - CodeBlock.Index invariantPc = firstInvariantPc.next(i); + CodeForest.Index invariantPc = firstInvariantPc.next(i); String invariantMacroName = invariantMacroPrefix + invariantPc.toString().replace(".", "_"); Expr.Invoke invariant = buildInvariantCall(activeBranch, @@ -1135,8 +1135,8 @@ protected Pair> transformLoopHelper( */ protected Pair> transformLoopWithoutInvariant( Codes.Loop code, VcBranch branch, Type[] environment, - Map labels, AttributedCodeBlock block) { - CodeBlock.Index loopPc = branch.pc(); + Map labels, AttributedCodeBlock block) { + CodeForest.Index loopPc = branch.pc(); // This is the easy case, as there is no loop invariant. Therefore, // we just havoc modified variables at the beginning and then allow // branches to exit the loop as normal. Branches which reach the end @@ -1174,7 +1174,7 @@ protected Pair> transformLoopWithoutInvariant( * @param block * The enclosing code block */ - private void buildInvariantMacro(CodeBlock.Index invariantPC, + private void buildInvariantMacro(CodeForest.Index invariantPC, boolean[] variables, Type[] environment, AttributedCodeBlock block) { // FIXME: we don't need to include all variables, only those which are // "active". @@ -1281,7 +1281,7 @@ public void havocVariables(int[] variables, VcBranch branch) { * The list of branches currently being managed. */ protected List transform(Codes.If code, VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, AttributedCodeBlock block) { // First, clone and register the true branch VcBranch trueBranch = branch.fork(); VcBranch falseBranch = branch.fork(); @@ -1318,7 +1318,7 @@ protected List transform(Codes.If code, VcBranch branch, * The list of branches currently being managed. */ protected List transform(Codes.IfIs code, VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, AttributedCodeBlock block) { ArrayList exitBranches = new ArrayList(); // In this case, both branches are reachable. // First, clone and register the branch @@ -1359,7 +1359,7 @@ protected List transform(Codes.IfIs code, VcBranch branch, * The list of branches currently being managed. */ protected List transform(Codes.Switch code, VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, AttributedCodeBlock block) { ArrayList exitBranches = new ArrayList(); VcBranch defaultBranch = branch.fork(); @@ -1426,14 +1426,14 @@ protected List transform(Codes.Switch code, VcBranch branch, */ protected Pair> transform( Codes.AssertOrAssume code, boolean isAssert, VcBranch branch, - Type[] environment, Map labels, + Type[] environment, Map labels, AttributedCodeBlock block) { int start = wyalFile.declarations().size(); // First, transform the given branch through the assert or assume block. // This will produce one or more exit branches, some of which may have // reached failed states and need to be turned into verification // conditions (for asserts only). - CodeBlock.Index pc = branch.pc(); + CodeForest.Index pc = branch.pc(); Pair> p = transform(pc, 0, branch, false, true, environment, labels, block); List exitBranches = p.second(); @@ -1481,7 +1481,7 @@ protected Pair> transform( * The list of branches currently being managed. */ protected void transform(Codes.Goto code, final VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, AttributedCodeBlock block) { branch.goTo(labels.get(code.target)); } @@ -1989,7 +1989,7 @@ protected List findPostcondition(NameID name, Type.Function * the inputs of the block being translated. * @return */ - protected void buildMacroBlock(String name, CodeBlock.Index root, + protected void buildMacroBlock(String name, CodeForest.Index root, AttributedCodeBlock block, List types, boolean isInvariant) { int start = wyalFile.declarations().size(); diff --git a/modules/wyil/src/wyil/checks/CoercionCheck.java b/modules/wyil/src/wyil/checks/CoercionCheck.java index 5904db6924..cf659225b7 100755 --- a/modules/wyil/src/wyil/checks/CoercionCheck.java +++ b/modules/wyil/src/wyil/checks/CoercionCheck.java @@ -33,7 +33,6 @@ import wycc.util.Pair; import wyil.attributes.SourceLocation; import wyil.lang.*; -import wyil.util.AttributedCodeBlock; /** *

@@ -88,20 +87,22 @@ public void apply(WyilFile module) { } public void check(WyilFile.FunctionOrMethod method) { - check(null, method.body(), method.body(), method); + CodeForest forest = method.code(); + for(int i=0;i!=forest.numBlocks();++i) { + check(i, forest, method); + } } - protected void check(CodeBlock.Index index, CodeBlock block, AttributedCodeBlock root, WyilFile.FunctionOrMethod method) { + protected void check(int blockID, CodeForest forest, WyilFile.FunctionOrMethod method) { // Examine all entries in this block looking for a conversion bytecode + CodeForest.Block block = forest.get(blockID); for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); + CodeForest.Entry e = block.get(i); + Code code = e.code(); if (code instanceof Codes.Convert) { Codes.Convert conv = (Codes.Convert) code; - check(conv.type(0), conv.result, new HashSet>(), - root.attribute(new CodeBlock.Index(index, i), SourceLocation.class)); - } else if (code instanceof CodeBlock) { - check(new CodeBlock.Index(index, i), (CodeBlock) code, root, method); - } + check(conv.type(0), conv.result(), new HashSet>(), e.attribute(SourceLocation.class)); + } } } @@ -115,8 +116,7 @@ protected void check(CodeBlock.Index index, CodeBlock block, AttributedCodeBlock * @param visited - the set of pairs already checked. * @param location - source location attribute (if applicable). */ - protected void check(Type from, Type to, HashSet> visited, - SourceLocation location) { + protected void check(Type from, Type to, HashSet> visited, SourceLocation location) { Pair p = new Pair(from,to); if(visited.contains(p)) { return; // already checked this pair diff --git a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java index 1eeb1b87d9..497d4854eb 100755 --- a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java +++ b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java @@ -36,7 +36,7 @@ import wyil.attributes.SourceLocation; import wyil.lang.*; import static wycc.lang.SyntaxError.*; -import static wyil.lang.CodeBlock.*; +import static wyil.lang.CodeForest.*; import static wyil.util.ErrorMessages.*; /** @@ -102,14 +102,13 @@ public HashSet initialStore() { } @Override - public HashSet propagate(CodeBlock.Index index, Code code, - HashSet in) { + public HashSet propagate(CodeForest.Index index, Code code, HashSet in) { checkUses(index, code, in); int[] defs = defs(code); - + if (defs.length >= 0) { - for(int def : defs) { + for (int def : defs) { in = new HashSet(in); in.add(def); } @@ -119,36 +118,35 @@ public HashSet propagate(CodeBlock.Index index, Code code, } @Override - public Pair, HashSet> propagate(CodeBlock.Index index, + public Pair, HashSet> propagate(CodeForest.Index index, Codes.If igoto, HashSet in) { if (!in.contains(igoto.operand(0)) || !in.contains(igoto.operand(1))) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, - rootBlock.attribute(index, SourceLocation.class)); + forest.get(index).attribute(SourceLocation.class)); } return new Pair(in, in); } @Override - public Pair, HashSet> propagate(CodeBlock.Index index, - Codes.IfIs iftype, HashSet in) { + public Pair, HashSet> propagate(CodeForest.Index index, Codes.IfIs iftype, + HashSet in) { if (!in.contains(iftype.operand(0))) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, - rootBlock.attribute(index,SourceLocation.class)); + forest.get(index).attribute(SourceLocation.class)); } - return new Pair(in,in); + return new Pair(in, in); } @Override - public List> propagate(CodeBlock.Index index, Codes.Switch sw, - HashSet in) { + public List> propagate(CodeForest.Index index, Codes.Switch sw, HashSet in) { if (!in.contains(sw.operand(0))) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, - rootBlock.attribute(index,SourceLocation.class)); + forest.get(index).attribute(SourceLocation.class)); } ArrayList> stores = new ArrayList(); @@ -159,23 +157,20 @@ public List> propagate(CodeBlock.Index index, Codes.Switch sw, } @Override - public HashSet propagate(CodeBlock.Index index, Codes.Loop loop, - HashSet in) { + public HashSet propagate(CodeForest.Index index, Codes.Loop loop, HashSet in) { if (loop instanceof Codes.Quantify) { Codes.Quantify fall = (Codes.Quantify) loop; - if (!in.contains(fall.startOperand) || !in.contains(fall.endOperand)) { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), - filename, - rootBlock.attribute(index, SourceLocation.class)); - } + if (!in.contains(fall.startOperand()) || !in.contains(fall.endOperand())) { + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, + forest.get(index).attribute(SourceLocation.class)); + } in = new HashSet(in); - in.add(fall.indexOperand); + in.add(fall.indexOperand()); } - CodeBlock blk = loop; - HashSet r = propagate(index, blk, in); + HashSet r = propagate(loop.block(), in); return join(in, r); } @@ -190,20 +185,20 @@ protected HashSet join(HashSet s1, HashSet s2) { return r; } - public void checkUses(CodeBlock.Index index, Code code, HashSet in) { - if(code instanceof Code.AbstractBytecode) { + public void checkUses(CodeForest.Index index, Code code, HashSet in) { + if (code instanceof Code.AbstractBytecode) { Code.AbstractBytecode a = (Code.AbstractBytecode) code; - for(int operand : a.operands()) { - if(operand != Codes.NULL_REG && !in.contains(operand)) { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), - filename, rootBlock.attribute(index,SourceLocation.class)); + for (int operand : a.operands()) { + if (operand != Codes.NULL_REG && !in.contains(operand)) { + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, + forest.get(index).attribute(SourceLocation.class)); } - } - if(code instanceof Codes.Update && !in.contains(a.target(0))) { + } + if (code instanceof Codes.Update && !in.contains(a.target(0))) { // In this case, we are assigning to an index or field. // Therefore, the target register must already be defined. - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), - filename, rootBlock.attribute(index,SourceLocation.class)); + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, + forest.get(index).attribute(SourceLocation.class)); } return; } else { diff --git a/modules/wyil/src/wyil/checks/ModuleCheck.java b/modules/wyil/src/wyil/checks/ModuleCheck.java index 509ae99fce..973ef42bc7 100755 --- a/modules/wyil/src/wyil/checks/ModuleCheck.java +++ b/modules/wyil/src/wyil/checks/ModuleCheck.java @@ -33,9 +33,8 @@ import wycc.util.Pair; import wyil.attributes.SourceLocation; import wyil.lang.*; -import wyil.lang.CodeBlock.Index; +import wyil.lang.CodeForest.Index; import wyil.lang.Codes.*; -import wyil.util.AttributedCodeBlock; import static wyil.util.ErrorMessages.*; /** @@ -126,26 +125,26 @@ public boolean catchException(Type type) { * @param c */ protected void checkFunctionPure(WyilFile.FunctionOrMethod c) { - AttributedCodeBlock block = c.body(); - checkFunctionPure(null,block,block); + checkFunctionPure(c.body(),c.code()); } - protected void checkFunctionPure(CodeBlock.Index parent, CodeBlock block, AttributedCodeBlock root) { + protected void checkFunctionPure(int blockID, CodeForest forest) { + CodeForest.Block block = forest.get(blockID); for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); - CodeBlock.Index index = new CodeBlock.Index(parent,i); + CodeForest.Entry e = block.get(i); + Code code = e.first(); if(code instanceof Codes.Invoke && ((Codes.Invoke)code).type(0) instanceof Type.Method) { // internal message send - syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, root.attribute(index, SourceLocation.class)); + syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if (code instanceof Codes.IndirectInvoke && ((Codes.IndirectInvoke)code).type(0) instanceof Type.Method) { - syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, root.attribute(index, SourceLocation.class)); + syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if(code instanceof Codes.NewObject) { - syntaxError(errorMessage(ALLOCATION_NOT_PERMITTED_IN_FUNCTION), filename, root.attribute(index, SourceLocation.class)); + syntaxError(errorMessage(ALLOCATION_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if(code instanceof Codes.Dereference){ - syntaxError(errorMessage(REFERENCE_ACCESS_NOT_PERMITTED_IN_FUNCTION), filename, root.attribute(index, SourceLocation.class)); - } else if(code instanceof CodeBlock) { - // Recursively check the block - checkFunctionPure(index,(CodeBlock) code, root); + syntaxError(errorMessage(REFERENCE_ACCESS_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); + } else if(code instanceof Code.AbstractCompoundBytecode) { + Code.AbstractCompoundBytecode a = (Code.AbstractCompoundBytecode) code; + checkFunctionPure(a.block(), forest); } } } diff --git a/modules/wyil/src/wyil/io/WyilFilePrinter.java b/modules/wyil/src/wyil/io/WyilFilePrinter.java index bb44070f05..e58b1a53ed 100755 --- a/modules/wyil/src/wyil/io/WyilFilePrinter.java +++ b/modules/wyil/src/wyil/io/WyilFilePrinter.java @@ -34,7 +34,6 @@ import wyil.lang.*; import wyil.lang.Type; import wyil.lang.WyilFile.*; -import wyil.util.AttributedCodeBlock; /** * Writes WYIL bytecodes in a textual from to a given file. @@ -125,9 +124,7 @@ public void apply(WyilFile module) throws IOException { String t_str; t_str = t.toString(); writeModifiers(td.modifiers(),out); - out.println("type " + td.name() + " : " + t_str); - out.println("invariant:"); - write(0,td.invariant(),out); + out.println("type " + td.name() + " : " + t_str); out.println(); } @@ -139,6 +136,8 @@ public void apply(WyilFile module) throws IOException { } private void write(FunctionOrMethod method, PrintWriter out) { + CodeForest forest = method.code(); + // writeModifiers(method.modifiers(), out); Type.FunctionOrMethod ft = method.type(); if (ft instanceof Type.Function) { @@ -153,20 +152,20 @@ private void write(FunctionOrMethod method, PrintWriter out) { writeParameters(ft.returns(),out); } out.println(":"); - - for (AttributedCodeBlock precondition : method.precondition()) { + // + for (int precondition : method.preconditions()) { out.println("requires:"); - write(0, precondition, out); + write(0, precondition, forest, out); } - for (AttributedCodeBlock postcondition : method.postcondition()) { + for (int postcondition : method.postconditions()) { out.println("ensures:"); - write(0, postcondition, out); + write(0, postcondition, forest, out); } - + if (method.body() != null) { out.println("body: "); - write(0, method.body(), out); + write(0, method.body(), forest, out); } } @@ -181,19 +180,19 @@ private void writeParameters(List parameters, PrintWriter out) { out.print(")"); } - private void write(int indent, CodeBlock blk, PrintWriter out) { - if(blk == null) { return; } - for(int i=0;i!=blk.size();++i) { - Code code = blk.get(i); + private void write(int indent, int blockID, CodeForest forest, PrintWriter out) { + CodeForest.Block block = forest.get(blockID); + for(int i=0;i!=block.size();++i) { + Code code = block.get(i).code(); if(code instanceof Codes.Label) { - write(indent-1,code,out); + write(indent-1,code,forest,out); } else { - write(indent,code,out); + write(indent,code,forest,out); } } } - private void write(int indent, Code c, PrintWriter out) { + private void write(int indent, Code c, CodeForest forest, PrintWriter out) { String line = "null"; tabIndent(indent+1,out); @@ -218,8 +217,9 @@ private void write(int indent, Code c, PrintWriter out) { // } out.println(); - if(c instanceof CodeBlock) { - write(indent+1,(CodeBlock)c,out); + if(c instanceof Code.AbstractCompoundBytecode) { + Code.AbstractCompoundBytecode cc = (Code.AbstractCompoundBytecode) c; + write(indent+1,cc.block(),forest,out); } } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 3d151a2bec..4a5849df86 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -37,7 +37,11 @@ import wyfs.lang.Path; import wyfs.util.Trie; import wyil.lang.*; -import wyil.util.AttributedCodeBlock; +import wyil.lang.Code.Schema; +import wyil.lang.Code.Extras; +import wyil.lang.Code.Operands; +import wyil.lang.Code.Targets; +import wyil.lang.Code.Types; /** * Read a binary WYIL file from a byte stream and convert into the corresponding @@ -47,8 +51,7 @@ * */ public final class WyilFileReader { - private static final char[] magic = { 'W', 'Y', 'I', 'L', 'F', 'I', 'L', - 'E' }; + private static final char[] magic = { 'W', 'Y', 'I', 'L', 'F', 'I', 'L', 'E' }; private final BinaryInputStream input; private String[] stringPool; @@ -109,6 +112,23 @@ public WyilFile read() throws IOException { return readModule(); } + /** + * Read the list of strings which constitute the string pool. Each entry is + * formated like so: + * + *

+	 * +-----------------+
+	 * | uv : len        |
+	 * +-----------------+
+	 * | u8[len] : bytes |
+	 * +-----------------+
+	 * 
+ * + * The encoding for each string item is UTF-8. + * + * @param count + * @throws IOException + */ private void readStringPool(int count) throws IOException { final String[] myStringPool = new String[count]; @@ -126,6 +146,25 @@ private void readStringPool(int count) throws IOException { stringPool = myStringPool; } + /** + * Read the list of paths which constitute the path pool. Each entry is + * formated like so: + * + *
+	 * +-----------------+
+	 * | uv : parent     |
+	 * +-----------------+
+	 * | uv : stringIdx  |
+	 * +-----------------+
+	 * 
+ * + * Each entry is a child of some parent entry, with index zero being + * automatically designated the "root". The stringIdx + * corresponds to an index in the string pool. + * + * @param count + * @throws IOException + */ private void readPathPool(int count) throws IOException { final Path.ID[] myPathPool = new Path.ID[count]; myPathPool[0] = Trie.ROOT; @@ -141,6 +180,24 @@ private void readPathPool(int count) throws IOException { pathPool = myPathPool; } + /** + * Read the list of names which constitute the name pool. Each entry is + * formated like so: + * + *
+	 * +-----------------+
+	 * | uv : pathIdx    |
+	 * +-----------------+
+	 * | uv : stringIdx  |
+	 * +-----------------+
+	 * 
+ * + * Each entry consists of a path component and a name component, both of + * which index the path and string pools (respectively). + * + * @param count + * @throws IOException + */ private void readNamePool(int count) throws IOException { final NameID[] myNamePool = new NameID[count]; @@ -156,6 +213,26 @@ private void readNamePool(int count) throws IOException { namePool = myNamePool; } + /** + * Read the list of constants which constitute the constant pool. Each entry + * is formated like so: + * + *
+	 * +-----------------+
+	 * | uv : code       |
+	 * +-----------------+
+	 * | ... payload ... |
+	 * +-----------------+
+	 * 
+ * + * Here, the size of the payload is determined by the constant code. In some + * cases, there is no payload (e.g. for the constant NULL). In other case, + * there can be numerous bytes contained in the payload (e.g. for an Integer + * constant). + * + * @param count + * @throws IOException + */ private void readConstantPool(int count) throws IOException { final Constant[] myConstantPool = new Constant[count]; @@ -186,7 +263,7 @@ private void readConstantPool(int count) throws IOException { constant = Constant.V_INTEGER(bi); break; } - case WyilFileWriter.CONSTANT_List: { + case WyilFileWriter.CONSTANT_Array: { int len = input.read_uv(); ArrayList values = new ArrayList(); for (int j = 0; j != len; ++j) { @@ -218,8 +295,7 @@ private void readConstantPool(int count) throws IOException { break; } default: - throw new RuntimeException( - "Unknown constant encountered in WhileyDefine: " + code); + throw new RuntimeException("Unknown constant encountered in WhileyDefine: " + code); } myConstantPool[i] = constant; } @@ -227,6 +303,18 @@ private void readConstantPool(int count) throws IOException { constantPool = myConstantPool; } + /** + *

+ * Read the list of types which constitute the type pool. Each entry is + * currently formated using the binary representation of automata. + *

+ * + * NOTE: Eventually, automata need to be properly integrated with the + * WyIL file format to avoid duplication. + * + * @param count + * @throws IOException + */ private void readTypePool(int count) throws IOException { final Type[] myTypePool = new Type[count]; Type.BinaryReader bin = new Type.BinaryReader(input); @@ -238,13 +326,41 @@ private void readTypePool(int count) throws IOException { typePool = myTypePool; } + /** + * Read a module contained within a given WyIL file. The format is: + * + *
+	 * +-----------------+
+	 * | uv : kind       |
+	 * +-----------------+
+	 * | uv : size       |
+	 * +-----------------+
+	 * ~~~~~~~~ u8 ~~~~~~~
+	 * +-----------------+
+	 * | uv : pathIDX    |
+	 * +-----------------+
+	 * | uv : nModifiers |
+	 * +-----------------+
+	 * | uv : nBlocks    |
+	 * +-----------------+
+	 * ~~~~~~~~ u8 ~~~~~~~
+	 * +-----------------+
+	 * | Block[nBlocks]  |
+	 * +-----------------+
+	 * 
+ * + * Here, the pathIDX gives the path identifiers for the module + * in question. + * + * @throws IOException + */ private WyilFile readModule() throws IOException { int kind = input.read_uv(); // block identifier int size = input.read_uv(); input.pad_u8(); int pathIdx = input.read_uv(); - int modifiers = input.read_uv(); // unused + int numModifiers = input.read_uv(); // unused int numBlocks = input.read_uv(); input.pad_u8(); @@ -257,99 +373,157 @@ private WyilFile readModule() throws IOException { return new WyilFile(pathPool[pathIdx], "unknown.whiley", declarations); } + private WyilFile.Block readModuleBlock() throws IOException { + WyilFile.Block block; int kind = input.read_uv(); int size = input.read_uv(); input.pad_u8(); switch (kind) { case WyilFileWriter.BLOCK_Constant: - return readConstantBlock(); + block = readConstantBlock(); + break; case WyilFileWriter.BLOCK_Type: - return readTypeBlock(); + block = readTypeBlock(); + break; case WyilFileWriter.BLOCK_Function: - return readFunctionBlock(); case WyilFileWriter.BLOCK_Method: - return readMethodBlock(); + block = readFunctionOrMethodBlock(); + break; default: - throw new RuntimeException("unknown module block encountered (" - + kind + ")"); + throw new RuntimeException("unknown module block encountered (" + kind + ")"); } + + input.pad_u8(); + + return block; } + /** + * Read a BLOCK_Constant, that is a WyIL module block representing a + * constant declaration. The format is: + * + *
+	 * +-----------------+
+	 * | uv : nameIdx    |
+	 * +-----------------+
+	 * | uv : Modifiers  |
+	 * +-----------------+
+	 * | uv : constIdx   |
+	 * +-----------------+
+	 * ~~~~~~~ u8 ~~~~~~~~
+	 * 
+ * + * The nameIdx is an index into the stringPool + * representing the declaration's name, whilst constIdx is an + * index into the constantPool representing the constant value + * itself. + * + * @throws IOException + */ private WyilFile.Constant readConstantBlock() throws IOException { int nameIdx = input.read_uv(); - // System.out.println("=== CONSTANT " + stringPool.get(nameIdx)); int modifiers = input.read_uv(); int constantIdx = input.read_uv(); - int nBlocks = input.read_uv(); // unused - input.pad_u8(); - return new WyilFile.Constant(generateModifiers(modifiers), - stringPool[nameIdx], constantPool[constantIdx]); + return new WyilFile.Constant(generateModifiers(modifiers), stringPool[nameIdx], constantPool[constantIdx]); } + /** + * Read a BLOCK_Type, that is a WyIL module block representing a type + * declaration. The format is: + * + *
+	 * +------------------------+
+	 * | uv : nameIdx           |
+	 * +------------------------+
+	 * | uv : Modifiers         |
+	 * +------------------------+
+	 * | uv : typeIdx           |
+	 * +------------------------+
+	 * ~~~~~~~~~~ u8 ~~~~~~~~~~~~
+	 * +------------------------+
+	 * | CodeForest : invariant |
+	 * +------------------------+
+	 * 
+ * + * The nameIdx is an index into the stringPool + * representing the declaration's name, whilst typeIdx is an + * index into the typePool representing the type itself. + * Finally, invariant gives the type's invariant as zero or + * more bytecode blocks. + * + * @throws IOException + */ private WyilFile.Type readTypeBlock() throws IOException { int nameIdx = input.read_uv(); - // System.out.println("=== TYPE " + stringPool.get(nameIdx)); int modifiers = input.read_uv(); int typeIdx = input.read_uv(); - int nBlocks = input.read_uv(); - - input.pad_u8(); - AttributedCodeBlock invariant = null; - for (int i = 0; i != nBlocks; ++i) { - int kind = input.read_uv(); - int size = input.read_uv(); - input.pad_u8(); - switch (kind) { - case WyilFileWriter.BLOCK_Constraint: - invariant = readAttributedCodeBlock(1); - break; - default: - throw new RuntimeException("Unknown type block encountered"); - } - } - - return new WyilFile.Type(generateModifiers(modifiers), - stringPool[nameIdx], typePool[typeIdx], invariant); + CodeForest forest = readCodeForestBlock(); + + return new WyilFile.Type(generateModifiers(modifiers), stringPool[nameIdx], typePool[typeIdx], forest); } - private WyilFile.FunctionOrMethod readFunctionBlock() - throws IOException { - int nameIdx = input.read_uv(); - int modifiers = input.read_uv(); - int typeIdx = input.read_uv(); - - input.pad_u8(); - - Type.Function type = (Type.Function) typePool[typeIdx]; - - Triple, List, AttributedCodeBlock> bodies = readFunctionOrMethodBlocks(type); - - return new WyilFile.FunctionOrMethod(generateModifiers(modifiers), - stringPool[nameIdx], type, bodies.third(), bodies.first(), - bodies.second()); - } - - private WyilFile.FunctionOrMethod readMethodBlock() throws IOException { + /** + * Read a BLOCK_Function or BLOCK_Method, that is a WyIL module block + * representing a function or method declaration. The format is: + * + *
+	 * +------------------------+
+	 * | uv : nameIdx           |
+	 * +------------------------+
+	 * | uv : Modifiers         |
+	 * +------------------------+
+	 * | uv : typeIdx           |
+	 * +------------------------+
+	 * | uv : nRequires         |
+	 * +------------------------+
+	 * | uv : nEnsures          |
+	 * +------------------------+
+	 * ~~~~~~~~~~ u8 ~~~~~~~~~~~~
+	 * +------------------------+
+	 * | CodeForest : code      |
+	 * +------------------------+
+	 * 
+ * + * The nameIdx is an index into the stringPool + * representing the declaration's name, whilst typeIdx is an + * index into the typePool representing the function or method + * type itself. Finally, code provides all code associated with + * the function or method which includes any preconditions, postconditions + * and the body itself. Here, nRequires identifiers the number + * of roots which correspond to the precondition, whilst + * nEnsures the number of roots corresponding to the + * postcondition. Any root after this comprise the body. + * + * @throws IOException + */ + private WyilFile.FunctionOrMethod readFunctionOrMethodBlock() throws IOException { int nameIdx = input.read_uv(); - // System.out.println("=== METHOD " + stringPool[nameIdx]); int modifiers = input.read_uv(); int typeIdx = input.read_uv(); + int nRequires = input.read_uv(); + int nEnsures = input.read_uv(); input.pad_u8(); - Type.Method type = (Type.Method) typePool[typeIdx]; + Type.FunctionOrMethod type = (Type.FunctionOrMethod) typePool[typeIdx]; - Triple, List, AttributedCodeBlock> bodies = readFunctionOrMethodBlocks(type); + CodeForest forest = readCodeForestBlock(); - return new WyilFile.FunctionOrMethod(generateModifiers(modifiers), - stringPool[nameIdx], type, bodies.third(), bodies.first(), - bodies.second()); + return new WyilFile.FunctionOrMethod(generateModifiers(modifiers), stringPool[nameIdx], type, forest, + nRequires, nEnsures); } - + + /** + * Convert an bit pattern representing various modifiers into instances of + * Modifier. + * + * @param modifiers + * @return + */ private Collection generateModifiers(int modifiers) { ArrayList mods = new ArrayList(); @@ -377,59 +551,116 @@ private Collection generateModifiers(int modifiers) { return mods; } - - private Triple, List, AttributedCodeBlock> readFunctionOrMethodBlocks( - Type.FunctionOrMethod type) throws IOException { - ArrayList requires = new ArrayList<>(); - ArrayList ensures = new ArrayList<>(); - AttributedCodeBlock body = null; - int numInputs = type.params().size(); - int nBlocks = input.read_uv(); - + + /** + *

+ * Read a code forest from the input stream. The format is: + *

+ * + *
+	 * +--------------------+
+	 * | uv: nRegs          |
+	 * +--------------------+
+	 * | uv: nBlocks        |
+	 * +--------------------+
+	 * | uv: nRoots         |
+	 * +--------------------+
+	 * | uv: nAttrs         |
+	 * +--------------------+
+	 * | Register[nRegs]    |
+	 * +--------------------+
+	 * | uv[nRoots]         |
+	 * +--------------------+
+	 * | CodeBlock[nBlocks] |
+	 * +--------------------+
+	 * | Attribute[nAttrs]  |
+	 * +--------------------+
+	 * 
+ * + *

+ * Each bytecode has a given offset which is calculated from the start of + * all blocks. For example, assume block 1 has ten actual bytecodes (i.e. + * which are not labels); then, a bytecode at index 2 in block 2 has offset + * 12. + *

+ * + * @param forest + * The forest being written to the stream + * @param labels + * The set of pre-calculated label offsets + * @param output + * @throws IOException + */ + private CodeForest readCodeForestBlock() throws IOException { input.pad_u8(); - - for (int i = 0; i != nBlocks; ++i) { - int kind = input.read_uv(); - int size = input.read_uv(); - input.pad_u8(); - - switch (kind) { - case WyilFileWriter.BLOCK_Precondition: - requires.add(readAttributedCodeBlock(numInputs)); - break; - case WyilFileWriter.BLOCK_Postcondition: - ensures.add(readAttributedCodeBlock(numInputs + 1)); - break; - case WyilFileWriter.BLOCK_Body: - body = readAttributedCodeBlock(numInputs); - break; - default: - throw new RuntimeException("Unknown case block encountered"); - } + int kind = input.read_uv(); // unused + int size = input.read_uv(); // unused + input.pad_u8(); + + int nRegs = input.read_uv(); + int nBlocks = input.read_uv(); + int nRoots = input.read_uv(); + int nAttrs = input.read_uv(); + + CodeForest forest = new CodeForest(); + + for(int i=0;i!=nRegs;++i) { + CodeForest.Register register = readCodeRegister(); + forest.registers().add(register); } - - return new Triple(requires, ensures, body); - } - - private AttributedCodeBlock readAttributedCodeBlock(int numInputs) - throws IOException { - int nCodes = input.read_uv(); + + for(int i=0;i!=nRoots;++i) { + int root = input.read_uv(); + forest.addRoot(root); + } + HashMap labels = new HashMap(); - ArrayList bytecodes = readCodeBlock(0, nCodes, labels); - insertLabels(0,bytecodes,labels); - - input.pad_u8(); // necessary - - return new AttributedCodeBlock(bytecodes); + + int offset = 0; + for(int i=0;i!=nBlocks;++i) { + CodeForest.Block block = readCodeBlock(offset,labels); + forest.add(block); + offset += block.size(); + } + + insertLabels(forest, labels); + + return forest; } - + + /** + * Read a given code register from the input stream. The format + * of reach register entry is: + * + *
+	 * +-------------------+
+	 * | uv : nAttrs       |
+	 * +-------------------+
+	 * | uv : typeIdx      |
+	 * +-------------------+
+	 * | Attribute[nAttrs] |
+	 * +-------------------+
+	 * 
+ * + * @param register + * Register to be written out + * @param output + * @throws IOException + */ + private CodeForest.Register readCodeRegister() throws IOException { + int nAttrs = input.read_uv(); + int typeIdx = input.read_uv(); + // TODO: read any attributes given + return new CodeForest.Register(typePool[typeIdx], "unknown"); + } + /** * This function is responsible for inserting labels into the correct - * positions within arbitrarily nested code blocks. Every label is given an - * offset within the flat bytecode array, as stored on disk. The challenge - * here is that, having loaded it from disk, the bytecode array is nested - * rather than flat. This is because certain bytecodes contain blocks of - * nested bytecodes (e.g. loop bytecode). Therefore, we need to track the + * positions within all blocks contained in a forest. Every label is given + * an offset within the flat bytecode array, as stored on disk. The + * challenge here is that, having loaded it from disk, the bytecode array is + * nested rather than flat. This is because certain bytecodes contain blocks + * of nested bytecodes (e.g. loop bytecode). Therefore, we need to track the * offset within the flat bytecode array against that of the now nested * bytecode structure. * @@ -441,472 +672,174 @@ private AttributedCodeBlock readAttributedCodeBlock(int numInputs) * The map of offsets to labels being inserted. * @return */ - private int insertLabels(int offset, ArrayList bytecodes, - HashMap labels) { - for (int i = 0; i != bytecodes.size(); ++i) { - Code bytecode = bytecodes.get(i); - - // First, check whether there is a label to insert - Codes.Label label = labels.get(offset++); - if (label != null) { bytecodes.add(i++, label); } - - // Second, check whether we have a nested block which needs to be - // explored. - if (bytecode instanceof Code.Compound) { - Code.Compound block = (Code.Compound) bytecode; - // At this point, we must clone the given bytecode - ArrayList blkBytecodes = new ArrayList(block.bytecodes()); - offset = 1 + insertLabels(offset, blkBytecodes, labels); - bytecodes.set(i,updateBytecodes(block,blkBytecodes)); + private int insertLabels(CodeForest forest, HashMap labels) { + int offset = 0; + for (int i = 0; i != forest.numBlocks(); ++i) { + CodeForest.Block block = forest.get(i); + for (int j = 0; j != block.size(); ++j) { + // First, check whether there is a label to insert + Codes.Label label = labels.get(offset++); + if (label != null) { + block.add(++j, label); + } } } - - // Finally, check whether or not there is a label at the end of this - // block. - Codes.Label label = labels.get(offset); - if (label != null) { bytecodes.add(bytecodes.size(), label); } - // Done return offset; } - - /** - * This method reconstructs a given compound bytecode with a new list of - * bytecodes representing its body. - * - * @param compound - * The compound bytecode being updated. - * @param bytecodes - * The list of new bytecodes representing its body. This list may - * be identical to its current body. - * @return - */ - private Code.Compound updateBytecodes(Code.Compound compound, ArrayList bytecodes) { - if (compound instanceof Codes.Quantify) { - Codes.Quantify l = (Codes.Quantify) compound; - return Codes.Quantify(l.startOperand, l.endOperand, l.indexOperand, - l.modifiedOperands, bytecodes); - } else if(compound instanceof Codes.Loop) { - Codes.Loop l = (Codes.Loop) compound; - return Codes.Loop(l.modifiedOperands, bytecodes); - } else if(compound instanceof Codes.Invariant) { - return Codes.Invariant(bytecodes); - } else if(compound instanceof Codes.Assert) { - return Codes.Assert(bytecodes); - } else if(compound instanceof Codes.Assume) { - return Codes.Assume(bytecodes); - } else { - throw new IllegalArgumentException("Unknown compound bytecode encountered: " + compound.getClass().getName()); - } - } - + /** - * Read all bytecodes between two given offsets. - * + *

+ * Read a block of bytecodes whilst adding newly discovered labels to a + * given set of label offsets. The format is: + *

+ * + *
+	 * +-------------------+
+	 * | uv : nCodes       |
+	 * +-------------------+
+	 * | uv : nAttrs       |
+	 * +-------------------+
+	 * | Bytecode[nCodes]  |
+	 * +-------------------+
+	 * | Attribute[nAttrs] |
+	 * +-------------------+
+	 * 
+ * + *

+ * The block is associated with a given offset value, which indicates the + * offset of the first bytecode in the block to be used when calculating + * branch offsets. + *

+ * * @param offset - * Starting offset to read from - * @param count - * Count of bytecodes to read * @param labels * @return + * @throws IOException */ - public ArrayList readCodeBlock(int offset, int count, - HashMap labels) throws IOException { - ArrayList bytecodes = new ArrayList(); - for (int i = 0; i < count; ++i) { - Code code = readCode(offset, labels); - bytecodes.add(code); - offset = offset + sizeof(code); - } - return bytecodes; - } + public CodeForest.Block readCodeBlock(int offset, HashMap labels) + throws IOException { + int nCodes = input.read_uv(); + int nAttrs = input.read_uv(); - /** - * Return the "size" of this bytecode. That is, number of bytecodes in the - * binary format it represents. In most cases, each bytecode in the binary - * format corresponds to exactly one in the object representation. However, - * in the case of compound bytecodes (e.g. loop, forall, etc) then it - * represents one plus the number contained within the block itself. - * - * @param code - * @return - */ - public static int sizeof(Code code) { - if(code instanceof Codes.Label) { - // Observe, this case is not possible in WyilFileReader, but is - // possible in WyilFileWriter (where this function is also called - // from). - return 0; - } else if (code instanceof Code.Unit) { - return 1; - } else { - Code.Compound compound = (Code.Compound) code; - // The size of a compound includes 1 for the header bytecode, and 1 - // for the virtual bytecode appearing at the block end. - int size = 2; - for (int i = 0; i != compound.size(); ++i) { - size += sizeof(compound.get(i)); - } - return size; + ArrayList bytecodes = new ArrayList(); + for (int i = 0; i < nCodes; ++i) { + Code code = readBytecode(i + offset, labels); + bytecodes.add(new CodeForest.Entry(code)); } + // TODO: read any attributes given + return new CodeForest.Block(bytecodes); } - private Code readCode(int offset, HashMap labels) - throws IOException { + private Code readBytecode(int offset, HashMap labels) throws IOException { int opcode = input.read_u8(); - boolean wideBase = false; - boolean wideRest = false; - - // deal with wide bytecodes first - switch (opcode) { - case Code.OPCODE_wide: - opcode = input.read_u8(); - wideBase = true; - break; - case Code.OPCODE_widerest: - opcode = input.read_u8(); - wideRest = true; - break; - case Code.OPCODE_widewide: - opcode = input.read_u8(); - wideBase = true; - wideRest = true; - break; - } - - int fmt = (opcode & Code.FMT_MASK); - - switch (fmt) { - case Code.FMT_NARYOP: - return readNaryOp(opcode, wideBase, wideRest, offset, labels); - case Code.FMT_EMPTY: - case Code.FMT_UNARYOP: - case Code.FMT_BINARYOP: - case Code.FMT_UNARYASSIGN: - case Code.FMT_BINARYASSIGN: - case Code.FMT_NARYASSIGN: - return readNaryAssign(opcode, wideBase, wideRest, offset, labels); - case Code.FMT_OTHER: - return readOther(opcode, wideBase, wideRest, offset, labels); - default: - throw new RuntimeException("unknown opcode encountered (" + opcode - + ")"); - } + Code.Schema schema = schemas[opcode]; + + // First, read and validate all targets, operands and types + int nTargets = input.read_uv(); + int nOperands = input.read_uv(); + int nTypes = input.read_uv(); + int nAttts = input.read_uv(); + int[] targets = readRegisters(nTargets); + int[] operands = readRegisters(nOperands); + Type[] types = readTypes(nTypes); + // Second, read all extras + Object[] extras = readExtras(schema,labels); + + // Finally, create the bytecode + return schema.construct(opcode,targets,operands,types,extras); } - - private Code readNaryOp(int opcode, boolean wideBase, boolean wideRest, - int offset, HashMap labels) + + /** + * Read the list of extra components defined by a given bytecode schema. + * Each extra is interpreted in a slightly different fashion. + * + * @param schema + * @param labels + * @return + * @throws IOException + */ + private Object[] readExtras(Code.Schema schema, HashMap labels) throws IOException { - int nOperands = readBase(wideBase); - int[] operands = new int[nOperands]; - for (int i = 0; i != nOperands; ++i) { - operands[i] = readBase(wideBase); - } - - // Special case which doesn't have a type. - if (opcode == Code.OPCODE_loop) { - int count = readRest(wideRest); - ArrayList bytecodes = readCodeBlock(offset + 1, count, labels); - return Codes.Loop(operands, bytecodes); - } else if(opcode == Code.OPCODE_quantify) { - int count = readRest(wideRest); - int indexOperand = operands[0]; - int startOperand = operands[1]; - int endOperand = operands[2]; - operands = Arrays.copyOfRange(operands, 3, operands.length); - ArrayList bytecodes = readCodeBlock(offset + 1, count, labels); - return Codes.Quantify(startOperand, endOperand, indexOperand, - operands, bytecodes); - } - - throw new RuntimeException("unknown opcode encountered (" + opcode - + ")"); - } - - private Code readNaryAssign(int opcode, boolean wideBase, boolean wideRest, int offset, - HashMap labels) throws IOException { - int nTargets = readBase(wideBase); - int nOperands = readBase(wideBase); - int nTypes = readBase(wideBase); - int[] targets = new int[nTargets]; - int[] operands = new int[nOperands]; - Type[] types = new Type[nTypes]; - for (int i = 0; i != nTargets; ++i) { - targets[i] = readBase(wideBase); - } - for (int i = 0; i != nOperands; ++i) { - operands[i] = readBase(wideBase); - } - for (int i = 0; i != nTypes; ++i) { - int typeIndex = readBase(wideBase); - types[i] = typePool[typeIndex]; - } - switch (opcode) { - case Code.OPCODE_return: - return Codes.Return(types, operands); - case Code.OPCODE_indirectinvokefn: - case Code.OPCODE_indirectinvokemd: { - if (!(types[0] instanceof Type.FunctionOrMethod)) { - throw new RuntimeException("expected function or method type"); - } - int operand = operands[0]; - operands = Arrays.copyOfRange(operands, 1, operands.length); - return Codes.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, - operand, operands); - } - case Code.OPCODE_invokefn: - case Code.OPCODE_invokemd: { - if (!(types[0] instanceof Type.FunctionOrMethod)) { - throw new RuntimeException("expected function or method type"); - } - int nameIdx = readRest(wideRest); - NameID nid = namePool[nameIdx]; - return Codes.Invoke((Type.FunctionOrMethod) types[0], targets, operands, - nid); - } - case Code.OPCODE_lambdafn: - case Code.OPCODE_lambdamd: { - if (!(types[0] instanceof Type.FunctionOrMethod)) { - throw new RuntimeException("expected function or method type"); - } else if(targets.length != 1) { - throw new RuntimeException("expected exactly one target"); + Extras[] extras = schema.extras(); + Object[] results = new Object[extras.length]; + for(int i=0;i!=extras.length;++i) { + switch(extras[i]) { + case CONSTANT: { + int constIdx = input.read_uv(); + results[i] = constantPool[constIdx]; + break; + } + case STRING: { + int stringIdx = input.read_uv(); + results[i] = stringPool[stringIdx]; + break; } - int nameIdx = readRest(wideRest); - NameID nid = namePool[nameIdx]; - for(int i=0;i!=operands.length;++i) { - operands[i] -= 1; - } - return Codes.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, - nid); - } - case Code.OPCODE_newrecord: { - if (!(types[0] instanceof Type.Record)) { - throw new RuntimeException("expected record type"); - } else if(targets.length != 1) { - throw new RuntimeException("expected exactly one target"); - } - return Codes.NewRecord((Type.Record) types[0], targets[0], operands); - } - case Code.OPCODE_newlist: { - if (!(types[0] instanceof Type.Array)) { - throw new RuntimeException("expected array type"); - } else if(targets.length != 1) { - throw new RuntimeException("expected exactly one target"); - } - return Codes.NewArray((Type.Array) types[0], targets[0], operands); - } - // Unary assignables - case Code.OPCODE_convert: { - int i = readRest(wideRest); - Type t = typePool[i]; - return Codes.Convert(types[0], targets[0], operands[0], t); - } - case Code.OPCODE_assign: - return Codes.Assign(types[0], targets[0], operands[0]); - case Code.OPCODE_dereference: { - if (!(types[0] instanceof Type.Reference)) { - throw new RuntimeException("expected reference type"); - } - return Codes.Dereference((Type.Reference) types[0], targets[0], operands[0]); - } - case Code.OPCODE_fieldload: { - if (!(types[0] instanceof Type.EffectiveRecord)) { - throw new RuntimeException("expected record type"); - } - int i = readRest(wideRest); - String field = stringPool[i]; - return Codes.FieldLoad((Type.EffectiveRecord) types[0], targets[0], - operands[0], field); - } - case Code.OPCODE_invert: - return Codes.Invert(types[0], targets[0], operands[0]); - case Code.OPCODE_newobject: { - if (!(types[0] instanceof Type.Reference)) { - throw new RuntimeException("expected reference type"); + case NAME: { + int nameIdx = input.read_uv(); + results[i] = namePool[nameIdx]; + break; + } + case BLOCK: { + int blockID = input.read_uv(); + results[i] = blockID; + break; } - return Codes.NewObject((Type.Reference) types[0], targets[0], operands[0]); - } - case Code.OPCODE_lengthof: { - if (!(types[0] instanceof Type.EffectiveArray)) { - throw new RuntimeException("expected collection type"); + case TARGET: { + int target = input.read_uv(); + Codes.Label l = findLabel(target, labels); + results[i] = l.label; + break; } - return Codes.LengthOf((Type.EffectiveArray) types[0], targets[0], operands[0]); - } - case Code.OPCODE_move: - return Codes.Move(types[0], targets[0], operands[0]); - case Code.OPCODE_neg: - return Codes.UnaryOperator(types[0], targets[0], operands[0], Codes.UnaryOperatorKind.NEG); - case Code.OPCODE_not: { - if (!(types[0] instanceof Type.Bool)) { - throw new RuntimeException("expected bool type"); - } - return Codes.Not(targets[0], operands[0]); - } - // Binary Assignables - case Code.OPCODE_indexof: { - if (!(types[0] instanceof Type.EffectiveArray)) { - throw new RuntimeException("expecting indexible type"); + case STRING_ARRAY: { + int nStrings = input.read_uv(); + String[] strings = new String[nStrings]; + for(int j=0;j!=nStrings;++j) { + int stringIdx = input.read_uv(); + strings[j] = stringPool[stringIdx]; + } + results[i] = strings; + break; } - return Codes.IndexOf((Type.EffectiveArray) types[0], targets[0], operands[0], operands[1]); - } - case Code.OPCODE_listgen: { - if (!(types[0] instanceof Type.Array)) { - throw new RuntimeException("expecting list type"); + case SWITCH_ARRAY: { + int nPairs = input.read_uv(); + Pair[] pairs = new Pair[nPairs]; + for(int j=0;j!=nPairs;++j) { + int constIdx = input.read_uv(); + int target = input.read_uv(); + String label = findLabel(target,labels).label; + pairs[j] = new Pair(constantPool[constIdx],label); + } + results[i] = pairs; + break; } - return Codes.ArrayGenerator((Type.Array) types[0], targets[0], operands[0], operands[1]); - } - case Code.OPCODE_add: - case Code.OPCODE_sub: - case Code.OPCODE_mul: - case Code.OPCODE_div: - case Code.OPCODE_rem: - case Code.OPCODE_bitwiseor: - case Code.OPCODE_bitwisexor: - case Code.OPCODE_bitwiseand: - case Code.OPCODE_lshr: - case Code.OPCODE_rshr: { - Codes.BinaryOperatorKind kind = Codes.BinaryOperatorKind.values()[opcode - Code.OPCODE_add]; - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], kind); - } - case Code.OPCODE_const: { - int idx = readRest(wideRest); - Constant c = constantPool[idx]; - return Codes.Const(targets[0], c); - } - // Unary operations - case Code.OPCODE_debug: - return Codes.Debug(operands[0]); - case Code.OPCODE_ifis: { - int resultIdx = readRest(wideRest); - Type result = typePool[resultIdx]; - int target = readTarget(wideRest, offset); - Codes.Label l = findLabel(target, labels); - return Codes.IfIs(types[0], operands[0], result, l.label); - } - case Code.OPCODE_switch: { - ArrayList> cases = new ArrayList>(); - int target = readTarget(wideRest, offset); - Codes.Label defaultLabel = findLabel(target, labels); - int nCases = readRest(wideRest); - for (int i = 0; i != nCases; ++i) { - int constIdx = readRest(wideRest); - Constant constant = constantPool[constIdx]; - target = readTarget(wideRest, offset); - Codes.Label l = findLabel(target, labels); - cases.add(new Pair(constant, l.label)); + default: + throw new RuntimeException("unknown bytecode extra encountered: " + extras[i]); } - return Codes.Switch(types[0], operands[0], defaultLabel.label, cases); - } - // Binary operators - case Code.OPCODE_ifeq: - case Code.OPCODE_ifne: - case Code.OPCODE_iflt: - case Code.OPCODE_ifle: - case Code.OPCODE_ifgt: - case Code.OPCODE_ifge: - case Code.OPCODE_ifel: - case Code.OPCODE_ifss: - case Code.OPCODE_ifse: { - int target = readTarget(wideRest, offset); - Codes.Label l = findLabel(target, labels); - Codes.Comparator cop = Codes.Comparator.values()[opcode - Code.OPCODE_ifeq]; - return Codes.If(types[0], operands[0], operands[1], cop, l.label); - } - // Empty bytecodes - case Code.OPCODE_fail: { - return Codes.Fail(); - } - case Code.OPCODE_goto: { - int target = readTarget(wideRest, offset); - Codes.Label lab = findLabel(target, labels); - return Codes.Goto(lab.label); } - case Code.OPCODE_nop: - return Codes.Nop; - } - throw new RuntimeException("unknown opcode encountered (" + opcode - + ")"); + return results; } - - private Code readOther(int opcode, boolean wideBase, boolean wideRest, - int offset, HashMap labels) - throws IOException { - switch (opcode) { - case Code.OPCODE_update: { - int nTargets = readBase(wideBase); - int nOperands = readBase(wideBase); - int nTypes = readBase(wideBase); - int[] targets = new int[nTargets]; - int[] operands = new int[nOperands-1]; - Type[] types = new Type[nTypes]; - for (int i = 0; i != targets.length; ++i) { - targets[i] = readBase(wideBase); - } - for (int i = 0; i != operands.length; ++i) { - operands[i] = readBase(wideBase); - } - int operand = readBase(wideBase); - for (int i = 0; i != types.length; ++i) { - types[i] = typePool[readBase(wideBase)]; - } - Type afterType = typePool[readRest(wideRest)]; - int nFields = readRest(wideRest); - ArrayList fields = new ArrayList(); - for (int i = 0; i != nFields; ++i) { - String field = stringPool[readRest(wideRest)]; - fields.add(field); - } - return Codes.Update(types[0], targets[0], operands, operand, - afterType, fields); - } - case Code.OPCODE_assertblock: { - int count = readRest(wideRest); - ArrayList bytecodes = readCodeBlock(offset + 1, count, labels); - return Codes.Assert(bytecodes); - } - case Code.OPCODE_assumeblock: { - int count = readRest(wideRest); - ArrayList bytecodes = readCodeBlock(offset + 1, count, labels); - return Codes.Assume(bytecodes); - } - case Code.OPCODE_invariantblock: { - int count = readRest(wideRest); - ArrayList bytecodes = readCodeBlock(offset + 1, count, labels); - return Codes.Invariant(bytecodes); - } + + private int[] readRegisters(int nRegisters) throws IOException { + int[] bs = new int[nRegisters]; + for (int i = 0; i != nRegisters; ++i) { + bs[i] = input.read_uv(); } - throw new RuntimeException("unknown opcode encountered (" + opcode - + ")"); + return bs; } - - private int readBase(boolean wide) throws IOException { - if (wide) { - return input.read_uv(); - } else { - return input.read_un(4); - } - } - - private int readRest(boolean wide) throws IOException { - if (wide) { - return input.read_uv(); - } else { - return input.read_u8(); - } - } - - private int readTarget(boolean wide, int offset) throws IOException { - if (wide) { - return input.read_uv(); - } else { - return (input.read_u8() + offset) - 128; + + private Type[] readTypes(int nTypes) throws IOException { + Type[] types = new Type[nTypes]; + for (int i = 0; i != nTypes; ++i) { + int typeIndex = input.read_uv(); + types[i] = typePool[typeIndex]; } + return types; } - + private static int labelCount = 0; - private static Codes.Label findLabel(int target, - HashMap labels) { + private static Codes.Label findLabel(int target, HashMap labels) { Codes.Label label = labels.get(target); if (label == null) { label = Codes.Label("label" + labelCount++); @@ -914,4 +847,285 @@ private static Codes.Label findLabel(int target, } return label; } + + /** + * ================================================================== + * Individual Bytecode Schemas + * ================================================================== + */ + + private static final Schema[] schemas = new Schema[255]; + + static { + // + schemas[Code.OPCODE_nop] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO) { + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Nop; + } + }; + schemas[Code.OPCODE_goto] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Goto((String) extras[0]); + } + }; + schemas[Code.OPCODE_fail] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Fail(); + } + }; + schemas[Code.OPCODE_assert] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Assert((Integer) extras[0]); + } + }; + schemas[Code.OPCODE_assume] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Assume((Integer) extras[0]); + } + }; + schemas[Code.OPCODE_invariant] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Invariant((Integer) extras[0]); + } + }; + + // ========================================================================= + // Unary Operators. + // ========================================================================= + schemas[Code.OPCODE_debug] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Debug(operands[0]); + } + }; + schemas[Code.OPCODE_return] = new Schema(Targets.ZERO, Operands.MANY, Types.MANY){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Return(types,operands); + } + }; + schemas[Code.OPCODE_switch] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE, Extras.TARGET, Extras.SWITCH_ARRAY){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + String defaultTarget = (String) extras[0]; + Pair[] cases = (Pair[]) extras[1]; + return Codes.Switch(types[0], operands[0], defaultTarget, Arrays.asList(cases)); + } + }; + schemas[Code.OPCODE_ifis] = new Schema(Targets.ZERO, Operands.ONE, Types.TWO, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.IfIs(types[0], operands[0], types[1], (String)extras[0]); + } + }; + + // ========================================================================= + // Unary Assignables + // ========================================================================= + schemas[Code.OPCODE_assign] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Assign(types[0], targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_move] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Move(types[0], targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.NewObject((Type.Reference) types[0], targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Dereference((Type.Reference) types[0], targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_invert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Invert(types[0], targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_lengthof] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.LengthOf((Type.EffectiveArray) types[0], targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.UnaryOperator(types[0], targets[0], operands[0], Codes.UnaryOperatorKind.NEG); + } + }; + schemas[Code.OPCODE_not] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Not(targets[0], operands[0]); + } + }; + schemas[Code.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); + } + }; + schemas[Code.OPCODE_convert] = new Schema(Targets.ONE, Operands.ONE, Types.TWO){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Convert(types[0], targets[0], operands[0], types[1]); + } + }; + schemas[Code.OPCODE_const] = new Schema(Targets.ONE, Operands.ZERO, Types.ZERO, Extras.CONSTANT){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Const(targets[0], (Constant) extras[0]); + } + }; + + // ========================================================================= + // Binary Operators + // ========================================================================= + schemas[Code.OPCODE_ifeq] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.EQ, (String) extras[0]); + } + }; + schemas[Code.OPCODE_ifne] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.NEQ, (String) extras[0]); + } + }; + schemas[Code.OPCODE_iflt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.LT, (String) extras[0]); + } + }; + schemas[Code.OPCODE_ifle] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.LTEQ, (String) extras[0]); + } + }; + schemas[Code.OPCODE_ifgt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.GT, (String) extras[0]); + } + }; + schemas[Code.OPCODE_ifge] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.GTEQ, (String) extras[0]); + } + }; + + // ========================================================================= + // Binary Assignables + // ========================================================================= + schemas[Code.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.ADD); + } + }; + schemas[Code.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.SUB); + } + }; + schemas[Code.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.MUL); + } + }; + schemas[Code.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.DIV); + } + }; + schemas[Code.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.REM); + } + }; + schemas[Code.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.BITWISEOR); + } + }; + schemas[Code.OPCODE_bitwisexor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.BITWISEXOR); + } + }; + schemas[Code.OPCODE_bitwiseand] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.BITWISEAND); + } + }; + schemas[Code.OPCODE_lshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.LEFTSHIFT); + } + }; + schemas[Code.OPCODE_rshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.RIGHTSHIFT); + } + }; + schemas[Code.OPCODE_indexof] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.IndexOf((Type.EffectiveArray)types[0],targets[0],operands[0],operands[1]); + } + }; + schemas[Code.OPCODE_listgen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.ArrayGenerator((Type.Array) types[0], targets[0], operands[0], operands[1]); + } + }; + + // ========================================================================= + // Nary Assignables + // ========================================================================= + schemas[Code.OPCODE_newlist] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.NewArray((Type.Array) types[0], targets[0], operands); + } + }; + schemas[Code.OPCODE_newrecord] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.NewRecord((Type.Record) types[0], targets[0], operands); + } + }; + schemas[Code.OPCODE_invoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Invoke((Type.FunctionOrMethod) types[0], targets, operands, (NameID) extras[0]); + } + }; + schemas[Code.OPCODE_indirectinvoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + int src = operands[0]; + operands = Arrays.copyOfRange(operands,1,operands.length); + return Codes.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, src, operands); + } + }; + schemas[Code.OPCODE_lambda] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, (NameID) extras[0]); + } + }; + schemas[Code.OPCODE_loop] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Loop(targets, (Integer) extras[0]); + } + }; + schemas[Code.OPCODE_quantify] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Quantify(operands[0],operands[1],operands[2], targets, (Integer) extras[0]); + } + }; + schemas[Code.OPCODE_update] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.STRING_ARRAY){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + ArrayList fields = new ArrayList(); + String[] strings = (String[]) extras[0]; + for(int i=0;i!=strings.length;++i) { fields.add(strings[i]); } + int operand = operands[operands.length-1]; + operands = Arrays.copyOf(operands, operands.length-1); + return Codes.Update(types[0], targets[0], operands, operand, types[1], fields); + } + }; + schemas[Code.OPCODE_void] = new Schema(Targets.MANY, Operands.ZERO, Types.ONE){ + public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Void(types[0], targets); + } + }; + } } diff --git a/modules/wyil/src/wyil/io/WyilFileWriter.java b/modules/wyil/src/wyil/io/WyilFileWriter.java index cd5596e8a6..3d87b889af 100644 --- a/modules/wyil/src/wyil/io/WyilFileWriter.java +++ b/modules/wyil/src/wyil/io/WyilFileWriter.java @@ -34,7 +34,6 @@ import wyfs.io.BinaryOutputStream; import wyfs.lang.Path; import wyil.lang.*; -import wyil.util.AttributedCodeBlock; import wyautl.util.BigRational; /** @@ -69,16 +68,16 @@ public final class WyilFileWriter { private final BinaryOutputStream out; - private final ArrayList stringPool = new ArrayList(); - private final HashMap stringCache = new HashMap(); + private final ArrayList stringPool = new ArrayList(); + private final HashMap stringCache = new HashMap(); private final ArrayList pathPool = new ArrayList(); - private final HashMap pathCache = new HashMap(); + private final HashMap pathCache = new HashMap(); private final ArrayList namePool = new ArrayList(); - private final HashMap nameCache = new HashMap(); + private final HashMap nameCache = new HashMap(); private final ArrayList constantPool = new ArrayList(); - private final HashMap constantCache = new HashMap(); + private final HashMap constantCache = new HashMap(); private final ArrayList typePool = new ArrayList(); - private final HashMap typeCache = new HashMap(); + private final HashMap typeCache = new HashMap(); public WyilFileWriter(OutputStream output) { this.out = new BinaryOutputStream(output); @@ -103,48 +102,41 @@ public void write(WyilFile module) throws IOException { buildPools(module); // third, write head block - writeBlock(BLOCK_Header,module,out); + writeBlock(BLOCK_Header, module, out); // fourth, write module block(s) - writeBlock(BLOCK_Module,module,out); + writeBlock(BLOCK_Module, module, out); out.flush(); } - private void writeBlock(int kind, Object data, BinaryOutputStream output) - throws IOException { - - output.pad_u8(); // pad out to next byte boundary + private void writeBlock(int kind, Object data, BinaryOutputStream output) throws IOException { // determine bytes for block byte[] bytes = null; - switch(kind) { - case BLOCK_Header: - bytes = generateHeaderBlock((WyilFile) data); - break; - case BLOCK_Module: - bytes = generateModuleBlock((WyilFile) data); - break; - case BLOCK_Type: - bytes = generateTypeBlock((WyilFile.Type) data); - break; - case BLOCK_Constant: - bytes = generateConstantBlock((WyilFile.Constant) data); - break; - case BLOCK_Function: - bytes = generateFunctionOrMethodBlock((WyilFile.FunctionOrMethod) data); - break; - case BLOCK_Method: - bytes = generateFunctionOrMethodBlock((WyilFile.FunctionOrMethod) data); - break; - case BLOCK_Body: - case BLOCK_Precondition: - case BLOCK_Postcondition: - case BLOCK_Constraint: - bytes = generateAttributedCodeBlock((AttributedCodeBlock) data); - break; + switch (kind) { + case BLOCK_Header: + bytes = generateHeaderBlock((WyilFile) data); + break; + case BLOCK_Module: + bytes = generateModuleBlock((WyilFile) data); + break; + case BLOCK_Type: + bytes = generateTypeBlock((WyilFile.Type) data); + break; + case BLOCK_Constant: + bytes = generateConstantBlock((WyilFile.Constant) data); + break; + case BLOCK_Function: + case BLOCK_Method: + bytes = generateFunctionOrMethodBlock((WyilFile.FunctionOrMethod) data); + break; + case BLOCK_CodeForest: + bytes = generateCodeForest((CodeForest) data); + break; } + output.pad_u8(); // pad out to next byte boundary output.write_uv(kind); output.write_uv(bytes.length); output.pad_u8(); // pad out to next byte boundary @@ -159,8 +151,7 @@ private void writeBlock(int kind, Object data, BinaryOutputStream output) * * @throws IOException */ - private byte[] generateHeaderBlock(WyilFile module) - throws IOException { + private byte[] generateHeaderBlock(WyilFile module) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); BinaryOutputStream output = new BinaryOutputStream(bytes); @@ -190,12 +181,22 @@ private byte[] generateHeaderBlock(WyilFile module) } /** - * Write the list of strings making up the string pool in UTF8. + * Write the list of strings making up the string pool. Each entry is + * formated like so: + * + *
+	 * +-----------------+
+	 * | uv : len        |
+	 * +-----------------+
+	 * | u8[len] : bytes |
+	 * +-----------------+
+	 * 
+ * + * The encoding for each string item is UTF8. * * @throws IOException */ private void writeStringPool(BinaryOutputStream output) throws IOException { - //System.out.println("Writing " + stringPool.size() + " string item(s)."); for (String s : stringPool) { try { byte[] bytes = s.getBytes("UTF-8"); @@ -207,70 +208,114 @@ private void writeStringPool(BinaryOutputStream output) throws IOException { } } + /** + * Write the list of paths which constitute the path pool. Each entry is + * formated like so: + * + *
+	 * +-----------------+
+	 * | uv : parent     |
+	 * +-----------------+
+	 * | uv : stringIdx  |
+	 * +-----------------+
+	 * 
+ * + * Each entry is a child of some parent entry, with index zero being + * automatically designated the "root". The stringIdx + * corresponds to an index in the string pool. + */ private void writePathPool(BinaryOutputStream output) throws IOException { - for(int i=1;i + * +-----------------+ + * | uv : pathIdx | + * +-----------------+ + * | uv : stringIdx | + * +-----------------+ + * + * + * Each entry consists of a path component and a name component, both of + * which index the path and string pools (respectively). + * + * @param count + * @throws IOException + */ private void writeNamePool(BinaryOutputStream output) throws IOException { - //System.out.println("Writing " + stringPool.size() + " name item(s)."); for (NAME_Item p : namePool) { - //output.write_uv(p.kind.kind()); output.write_uv(p.pathIndex); output.write_uv(p.nameIndex); } } + /** + * Write the list of constants which constitute the constant pool. Each entry + * is formated like so: + * + *
+	 * +-----------------+
+	 * | uv : code       |
+	 * +-----------------+
+	 * | ... payload ... |
+	 * +-----------------+
+	 * 
+ * + * Here, the size of the payload is determined by the constant code. In some + * cases, there is no payload (e.g. for the constant NULL). In other case, + * there can be numerous bytes contained in the payload (e.g. for an Integer + * constant). + * + * @param count + * @throws IOException + */ private void writeConstantPool(BinaryOutputStream output) throws IOException { - //System.out.println("Writing " + stringPool.size() + " constant item(s)."); - for (Constant val : constantPool) { - if(val instanceof Constant.Null) { + if (val instanceof Constant.Null) { output.write_uv(CONSTANT_Null); - - } else if(val instanceof Constant.Bool) { + } else if (val instanceof Constant.Bool) { Constant.Bool b = (Constant.Bool) val; output.write_uv(b.value ? CONSTANT_True : CONSTANT_False); - - } else if(val instanceof Constant.Byte) { + } else if (val instanceof Constant.Byte) { Constant.Byte b = (Constant.Byte) val; output.write_uv(CONSTANT_Byte); output.write_u8(b.value); - - } else if(val instanceof Constant.Integer) { + } else if (val instanceof Constant.Integer) { Constant.Integer i = (Constant.Integer) val; BigInteger num = i.value; byte[] numbytes = num.toByteArray(); output.write_uv(CONSTANT_Int); output.write_uv(numbytes.length); output.write(numbytes); - } else if(val instanceof Constant.Array) { + } else if (val instanceof Constant.Array) { Constant.Array s = (Constant.Array) val; - output.write_uv(CONSTANT_List); + output.write_uv(CONSTANT_Array); output.write_uv(s.values.size()); - for(Constant v : s.values) { + for (Constant v : s.values) { int index = constantCache.get(v); output.write_uv(index); } - - } else if(val instanceof Constant.Record) { + } else if (val instanceof Constant.Record) { Constant.Record r = (Constant.Record) val; output.write_uv(CONSTANT_Record); output.write_uv(r.values.size()); - for(java.util.Map.Entry v : r.values.entrySet()) { + for (java.util.Map.Entry v : r.values.entrySet()) { output.write_uv(stringCache.get(v.getKey())); int index = constantCache.get(v.getValue()); output.write_uv(index); } - - } else if(val instanceof Constant.Lambda) { + } else if (val instanceof Constant.Lambda) { Constant.Lambda fm = (Constant.Lambda) val; Type.FunctionOrMethod t = fm.type(); - output.write_uv(t instanceof Type.Function ? CONSTANT_Function - : CONSTANT_Method); + output.write_uv(t instanceof Type.Function ? CONSTANT_Function : CONSTANT_Method); output.write_uv(typeCache.get(t)); output.write_uv(nameCache.get(fm.name)); } else { @@ -280,7 +325,6 @@ private void writeConstantPool(BinaryOutputStream output) throws IOException { } private void writeTypePool(BinaryOutputStream output) throws IOException { - //System.out.println("Writing " + stringPool.size() + " type item(s)."); Type.BinaryWriter bout = new Type.BinaryWriter(output); for (Type t : typePool) { bout.write(t); @@ -291,28 +335,27 @@ private byte[] generateModuleBlock(WyilFile module) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); BinaryOutputStream output = new BinaryOutputStream(bytes); - output.write_uv(pathCache.get(module.id())); // FIXME: BROKEN! + output.write_uv(pathCache.get(module.id())); output.write_uv(MODIFIER_Public); // for now output.write_uv(module.blocks().size()); - for(WyilFile.Block d : module.blocks()) { - writeModuleBlock(d,output); + for (WyilFile.Block d : module.blocks()) { + writeModuleBlock(d, output); } - output.close(); + output.close(); return bytes.toByteArray(); } - private void writeModuleBlock(WyilFile.Block d, - BinaryOutputStream output) throws IOException { - if(d instanceof WyilFile.Constant) { - writeBlock(BLOCK_Constant, d ,output); - } else if(d instanceof WyilFile.Type) { + private void writeModuleBlock(WyilFile.Block d, BinaryOutputStream output) throws IOException { + if (d instanceof WyilFile.Constant) { + writeBlock(BLOCK_Constant, d, output); + } else if (d instanceof WyilFile.Type) { writeBlock(BLOCK_Type, d, output); - } else if(d instanceof WyilFile.FunctionOrMethod) { + } else if (d instanceof WyilFile.FunctionOrMethod) { WyilFile.FunctionOrMethod md = (WyilFile.FunctionOrMethod) d; - if(md.type() instanceof Type.Function) { + if (md.type() instanceof Type.Function) { writeBlock(BLOCK_Function, d, output); } else { writeBlock(BLOCK_Method, d, output); @@ -320,6 +363,28 @@ private void writeModuleBlock(WyilFile.Block d, } } + /** + * Construct a BLOCK_Constant, that is a WyIL module block representing a + * constant declaration. The format is: + * + *
+	 * +-----------------+
+	 * | uv : nameIdx    |
+	 * +-----------------+
+	 * | uv : Modifiers  |
+	 * +-----------------+
+	 * | uv : constIdx   |
+	 * +-----------------+
+	 * ~~~~~~~ u8 ~~~~~~~~
+	 * 
+ * + * The nameIdx is an index into the stringPool + * representing the declaration's name, whilst constIdx is an + * index into the constantPool representing the constant value + * itself. + * + * @throws IOException + */ private byte[] generateConstantBlock(WyilFile.Constant cd) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); BinaryOutputStream output = new BinaryOutputStream(bytes); @@ -327,13 +392,38 @@ private byte[] generateConstantBlock(WyilFile.Constant cd) throws IOException { output.write_uv(stringCache.get(cd.name())); output.write_uv(generateModifiers(cd.modifiers())); output.write_uv(constantCache.get(cd.constant())); - output.write_uv(0); // no sub-blocks - // TODO: write annotations + // TODO: write attributes output.close(); return bytes.toByteArray(); } + /** + * Construct a BLOCK_Type, that is a WyIL module block representing a type + * declaration. The format is: + * + *
+	 * +------------------------+
+	 * | uv : nameIdx           |
+	 * +------------------------+
+	 * | uv : Modifiers         |
+	 * +------------------------+
+	 * | uv : typeIdx           |
+	 * +------------------------+
+	 * ~~~~~~~~~~ u8 ~~~~~~~~~~~~
+	 * +------------------------+
+	 * | CodeForest : invariant |
+	 * +------------------------+
+	 * 
+ * + * The nameIdx is an index into the stringPool + * representing the declaration's name, whilst typeIdx is an + * index into the typePool representing the type itself. + * Finally, invariant gives the type's invariant as zero or + * more bytecode blocks. + * + * @throws IOException + */ private byte[] generateTypeBlock(WyilFile.Type td) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); BinaryOutputStream output = new BinaryOutputStream(bytes); @@ -341,19 +431,45 @@ private byte[] generateTypeBlock(WyilFile.Type td) throws IOException { output.write_uv(stringCache.get(td.name())); output.write_uv(generateModifiers(td.modifiers())); output.write_uv(typeCache.get(td.type())); - CodeBlock invariant = td.invariant(); - - if(invariant != null) { - output.write_uv(1); - writeBlock(BLOCK_Constraint,td.invariant(),output); - } else { - output.write_uv(0); - } - + writeBlock(BLOCK_CodeForest, td.invariant(), output); output.close(); return bytes.toByteArray(); } + /** + * Construct a BLOCK_Function or BLOCK_Method, that is a WyIL module block + * representing a function or method declaration. The format is: + * + *
+	 * +------------------------+
+	 * | uv : nameIdx           |
+	 * +------------------------+
+	 * | uv : Modifiers         |
+	 * +------------------------+
+	 * | uv : typeIdx           |
+	 * +------------------------+
+	 * | uv : nRequires         |
+	 * +------------------------+
+	 * | uv : nEnsures          |
+	 * +------------------------+
+	 * ~~~~~~~~~~ u8 ~~~~~~~~~~~~
+	 * +------------------------+
+	 * | CodeForest : code      |
+	 * +------------------------+
+	 * 
+ * + * The nameIdx is an index into the stringPool + * representing the declaration's name, whilst typeIdx is an + * index into the typePool representing the function or method + * type itself. Finally, code provides all code associated with + * the function or method which includes any preconditions, postconditions + * and the body itself. Here, nRequires identifiers the number + * of roots which correspond to the precondition, whilst + * nEnsures the number of roots corresponding to the + * postcondition. Any root after this comprise the body. + * + * @throws IOException + */ private byte[] generateFunctionOrMethodBlock(WyilFile.FunctionOrMethod md) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); BinaryOutputStream output = new BinaryOutputStream(bytes); @@ -362,213 +478,305 @@ private byte[] generateFunctionOrMethodBlock(WyilFile.FunctionOrMethod md) throw output.write_uv(generateModifiers(md.modifiers())); output.write_uv(typeCache.get(md.type())); - output.pad_u8(); // pad out to next byte boundary - - int bodyCount = md.body() == null ? 0 : 1; + output.write_uv(md.preconditions().length); + output.write_uv(md.postconditions().length); - output.write_uv(md.precondition().size() + md.postcondition().size() + bodyCount); + output.pad_u8(); // pad out to next byte boundary - for(CodeBlock requires : md.precondition()) { - writeBlock(BLOCK_Precondition,requires,output); - } - for(CodeBlock ensures : md.postcondition()) { - writeBlock(BLOCK_Postcondition,ensures,output); - } - if(md.body() != null) { - writeBlock(BLOCK_Body,md.body(),output); - } - // TODO: write annotations + writeBlock(BLOCK_CodeForest, md.code(), output); output.close(); return bytes.toByteArray(); } - private byte[] generateAttributedCodeBlock(AttributedCodeBlock block) throws IOException { + /** + *

+ * Construct a code forest using a given set of pre-calculated label + * offsets. The format is: + *

+ * + *
+	 * +--------------------+
+	 * | uv: nRegs          |
+	 * +--------------------+
+	 * | uv: nBlocks        |
+	 * +--------------------+
+	 * | uv: nRoots         |
+	 * +--------------------+
+	 * | uv: nAttrs         |
+	 * +--------------------+
+	 * | Register[nRegs]    |
+	 * +--------------------+
+	 * | uv[nRoots]         |
+	 * +--------------------+
+	 * | CodeBlock[nBlocks] |
+	 * +--------------------+
+	 * | Attribute[nAttrs]  |
+	 * +--------------------+
+	 * 
+ * + *

+ * Each bytecode has a given offset which is calculated from the start of + * all blocks. For example, assume block 1 has ten actual bytecodes (i.e. + * which are not labels); then, a bytecode at index 2 in block 2 has offset + * 12. + *

+ *

+ * The mapping of label names to their bytecode offsets is needed when + * writing branching bytecodes (e.g. goto). Each label object is associated + * with the bytecode following it in the block. + *

+ * + * @param forest + * The forest being written to the stream + * @param labels + * The set of pre-calculated label offsets + * @throws IOException + */ + private byte[] generateCodeForest(CodeForest forest) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); BinaryOutputStream output = new BinaryOutputStream(bytes); - HashMap labels = new HashMap(); - buildLabelsMap(0,block,labels); - writeCodeBlock(true,block,0,labels,output); + HashMap labels = buildLabelsMap(forest); + output.write_uv(forest.numRegisters()); + output.write_uv(forest.numBlocks()); + output.write_uv(forest.numRoots()); + output.write_uv(0); // currently no attributes + + List registers = forest.registers(); + for (int i = 0; i != registers.size(); ++i) { + writeCodeRegister(registers.get(i), output); + } + + // FIXME: in principle we can get rid of the following by reorgansing + // the forest so that root blocks come first. + for (int i = 0; i != forest.numRoots(); ++i) { + output.write_uv(forest.getRoot(i)); + } + + int offset = 0; + for (int i = 0; i != forest.numBlocks(); ++i) { + CodeForest.Block block = forest.get(i); + offset = writeCodeBlock(block, offset, labels, output); + } output.close(); return bytes.toByteArray(); } /** - * Construct a mapping of labels to their WyIL code offsets within the - * resulting block. + *

+ * Construct a mapping of labels to the bytecode offsets they represent + * within the code forest. Labels do not correspond to "real" bytecodes + * which are written out. Rather they are associated with the offset of + * the bytecode immediately following them in a given block. + *

+ *

+ * Each bytecode has a given offset which is calculated from the start of + * all blocks. For example, assume block 1 has ten actual bytecodes (i.e. + * which are not labels); then, a bytecode at index 2 in block 2 has offset + * 12. + *

+ *

+ * The purpose of the label mapping is to enable branch offsets to be + * calculated when writing them out. + *

* - * @param offset - * Offset of first code in the given block. - * @param block - * Block of bytecodes to go through + * @param forest + * Code forest to construct label mapping from * @param labels * Lab map being constructed * @return */ - private int buildLabelsMap(int offset, CodeBlock block, HashMap labels) { - for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); - if (code instanceof Codes.Label) { - Codes.Label l = (Codes.Label) code; - labels.put(l.label, offset); - } else if (code instanceof CodeBlock) { - // Must add 1 here for the virtual bytecode at the end of the - // block. - offset = 1 + buildLabelsMap(offset + 1, (CodeBlock) code, labels); - } else { - offset = offset + 1; + private HashMap buildLabelsMap(CodeForest forest) { + HashMap labels = new HashMap(); + int offset = 0; + for (int i = 0; i != forest.numBlocks(); ++i) { + CodeForest.Block block = forest.get(i); + for (int j = 0; j != block.size(); ++j) { + Code code = block.get(j).code(); + if (code instanceof Codes.Label) { + Codes.Label l = (Codes.Label) code; + labels.put(l.label, offset-1); + } else { + offset = offset + 1; + } } } - return offset; + return labels; } - private void writeCodeBlock(boolean wide, CodeBlock block, int offset, - HashMap labels, BinaryOutputStream output) - throws IOException { - + /** + * Write details of a given code register to the output stream. The format + * of reach register entry is: + * + *
+	 * +-------------------+
+	 * | uv : nAttrs       |
+	 * +-------------------+
+	 * | uv : typeIdx      |
+	 * +-------------------+
+	 * | Attribute[nAttrs] |
+	 * +-------------------+
+	 * 
+ * + * @param register + * Register to be written out + * @param output + * @throws + */ + private void writeCodeRegister(CodeForest.Register register, BinaryOutputStream output) throws IOException { + // TODO: write out register attributes (including name) + output.write_uv(0); + // Write out the type index + output.write_uv(typeCache.get(register.type())); + } + + + /** + *

+ * Write out a block of bytecodes using a given set of pre-calculated label + * offsets. The format is: + *

+ * + *
+	 * +-------------------+
+	 * | uv : nCodes       |
+	 * +-------------------+
+	 * | uv : nAttrs       |
+	 * +-------------------+
+	 * | Bytecode[nCodes]  |
+	 * +-------------------+
+	 * | Attribute[nAttrs] |
+	 * +-------------------+
+	 * 
+ * + *

+ * The block is associated with a given offset value, which indicates the + * offset of the first bytecode in the block to be used when calculating + * branch offsets. + *

+ * + * @param block + * @param offset + * @param labels + * @param output + * @return + * @throws IOException + */ + private int writeCodeBlock(CodeForest.Block block, int offset, HashMap labels, + BinaryOutputStream output) throws IOException { // First, determine how many labels there are in this block (since // labels are not real bytecodes) - int nlabels = 0; - for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); - if (code instanceof Codes.Label) { - nlabels++; - } - } + int nlabels = countLabels(block); + // Second, write the count of bytecodes - writeRest(wide,block.size() - nlabels,output); + output.write_uv(block.size() - nlabels); + // Third, write the count of attributes + // TODO: write out attributes + output.write_uv(0); - // Third, write the actual bytecodes! + // Finally, write the actual bytecodes! for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); + Code code = block.get(i).code(); if (code instanceof Codes.Label) { // Skip over labels because these are not written to disk and // have no "offset" } else { - writeCode(code, offset, labels, output); - offset += WyilFileReader.sizeof(code); + writeBytecode((Code.AbstractBytecode) code, offset, labels, output); + offset = offset + 1; } } + + // TODO: write attributes + return offset; } - private void writeCode(Code code, int offset, - HashMap labels, BinaryOutputStream output) + /** + *

+ * Write out a given bytecode a given set of pre-calculated label offsets, + * whose format is currently given as follows: + *

+ * + *
+	 * +-------------------+
+	 * | u8 : opcode       |
+	 * +-------------------+
+	 * | uv : nTargets     |
+	 * +-------------------+
+	 * | uv : nOperands    |
+	 * +-------------------+
+	 * | uv : nTypes       | 
+	 * +-------------------+
+	 * | uv : nAttrs       | 
+	 * +-------------------+
+	 * | uv[nTargets]      |
+	 * +-------------------+
+	 * | uv[nOperands]     |
+	 * +-------------------+ 
+	 * | uv[nTypes]        |
+	 * +-------------------+
+	 * | Attribute[nAttrs] | 
+	 * +-------------------+
+	 * | uv[] rest         |
+	 * +-------------------+
+	 * 
+ * + *

+ * NOTE: The intention is to support a range of different bytecode + * formats in order to optimise the common cases. For example, when there + * are no targets, no operands, no types, etc. Furthermore, when the size of + * items can be reduced from uv to u4, etc. + *

+ */ + private void writeBytecode(Code.AbstractBytecode code, int offset, HashMap labels, BinaryOutputStream output) throws IOException { - - // first determine whether we need some kind of wide instruction. - int width = calculateWidth(code, offset, labels); - - switch (width) { - case Code.OPCODE_wide : - output.write_u8(width); - writeBase(true, code, output); - writeRest(false, code, offset, labels, output); - break; - case Code.OPCODE_widerest : - output.write_u8(width); - writeBase(false, code, output); - writeRest(true, code, offset, labels, output); - break; - case Code.OPCODE_widewide : - output.write_u8(width); - writeBase(true, code, output); - writeRest(true, code, offset, labels, output); - break; - default : - writeBase(false, code, output); - writeRest(false, code, offset, labels, output); - } + writeCommon(code, output); + writeRest(code, offset, labels, output); } /** - * Write the "base" part of a bytecode instruction. This includes only the - * opcode itself and the operands (if any). + * Write the "common" part of a bytecode instruction. This includes the + * opcode, target register, operand registers, types and attributes. * - * @param wide - * --- indicates whether we should be writing the base in "wide" - * format. That is, using the unlimited representation of - * integers. In the alternative "short" representation, all - * operands are written using exactly 4 unsigned bits. This means - * we can encode registers 0-15, which covers the majority of - * cases. The wide format is then used for cases when we have to - * write a register operand whose index is > 15. * @param code * --- The bytecode to be written. - * @param offset - * --- The current offset of this bytecode in the bytecode array - * being generated. This offset is measured in complete - * bytecodes, not in e.g. bytes. Therefore, the first bytecode - * has offset zero, the second bytecode has offset 1, etc. The - * offset is required * @param output * --- The binary stream to write this bytecode to. * @throws IOException */ - private void writeBase(boolean wide, Code code, - BinaryOutputStream output) throws IOException { - - // second, deal with standard instruction formats + private void writeCommon(Code.AbstractBytecode code, BinaryOutputStream output) throws IOException { output.write_u8(code.opcode()); - - if(code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode a = (Code.AbstractBytecode) code; - int[] targets = a.targets(); - int[] operands = a.operands(); - Type[] types = a.types(); - if(code instanceof Codes.Lambda) { - // FIXME: This is something of a hack, but the reason is that - // lambda operands can be NULL_REG. - for(int i=0;i!=operands.length;++i) { - operands[i] ++; - } - } - writeBase(wide,targets.length,output); - writeBase(wide,operands.length,output); - writeBase(wide,types.length,output); - for(int i=0;i!=targets.length;++i) { - writeBase(wide,targets[i],output); - } - for(int i=0;i!=operands.length;++i) { - writeBase(wide,operands[i],output); - } - for(int i=0;i!=types.length;++i) { - writeBase(wide,typeCache.get(types[i]),output); - } - } else if(code instanceof Codes.Quantify) { - Codes.Quantify l = (Codes.Quantify) code; - int[] operands = l.modifiedOperands; - writeBase(wide,operands.length + 3,output); - writeBase(wide,l.indexOperand,output); - writeBase(wide,l.startOperand,output); - writeBase(wide,l.endOperand,output); - for(int i=0;i!=operands.length;++i) { - writeBase(wide,operands[i],output); - } - } else if(code instanceof Codes.Loop) { - Codes.Loop l = (Codes.Loop) code; - int[] operands = l.modifiedOperands; - writeBase(wide,operands.length,output); - for(int i=0;i!=operands.length;++i) { - writeBase(wide,operands[i],output); - } + int[] targets = code.targets(); + int[] operands = code.operands(); + Type[] types = code.types(); + output.write_uv(targets.length); + output.write_uv(operands.length); + output.write_uv(types.length); + output.write_uv(0); // attributes + // Write target registers + for (int i = 0; i != targets.length; ++i) { + output.write_uv(targets[i]); + } + // Write operand registers + for (int i = 0; i != operands.length; ++i) { + output.write_uv(operands[i]); } + // Write type indices + for (int i = 0; i != types.length; ++i) { + output.write_uv(typeCache.get(types[i])); + } + // TODO: write attributes } /** - * Write the "rest" of a bytecode instruction. This includes any additional - * information, such as the type and/or other pool items required for the - * bytecode. + * Write the "rest" of a bytecode instruction. This includes additional + * information as specified for the given opcode. For compound bytecodes, + * this includes the block identifier(s) of the nested block(s) in question. + * For branching bytecodes, it will include the branch destination as a + * relative offset. Other bytecodes include indices which identify constants + * in one of the pools. * - * @param wide - * --- indicates whether we should be writing the rest in "wide" - * format. That is, using the unlimited representation of - * integers. In the alternative "short" representation, all pool - * indices are written using exactly 8 unsigned bits. This means - * we can encode pool indices 0-255, which covers a large number - * of cases. The wide format is then used for cases when we have - * to a pool index > 255. * @param code * --- The bytecode to be written. * @param offset @@ -588,253 +796,88 @@ private void writeBase(boolean wide, Code code, * written. * @throws IOException */ - private void writeRest(boolean wide, Code code, int offset, - HashMap labels, BinaryOutputStream output) throws IOException { + private void writeRest(Code code, int offset, HashMap labels, + BinaryOutputStream output) throws IOException { // now deal with non-uniform instructions // First, deal with special cases - if(code instanceof Codes.AssertOrAssume) { - Codes.AssertOrAssume l = (Codes.AssertOrAssume) code; - writeCodeBlock(wide,l,offset+1,labels,output); - } else if(code instanceof Codes.Const) { + if(code instanceof Code.AbstractCompoundBytecode) { + // Assert / Assume / Loop / Quantify + Code.AbstractCompoundBytecode cb = (Code.AbstractCompoundBytecode) code; + output.write_uv(cb.block()); + } else if(code instanceof Code.AbstractBranchingBytecode) { + Code.AbstractBranchingBytecode bb = (Code.AbstractBranchingBytecode) code; + int destination = labels.get(bb.destination()); + output.write_uv(destination); + } else if (code instanceof Codes.Const) { Codes.Const c = (Codes.Const) code; - writeRest(wide,constantCache.get(c.constant),output); - } else if(code instanceof Codes.Convert) { - Codes.Convert c = (Codes.Convert) code; - writeRest(wide,typeCache.get(c.result),output); - } else if(code instanceof Codes.FieldLoad) { + output.write_uv(constantCache.get(c.constant)); + } else if (code instanceof Codes.FieldLoad) { Codes.FieldLoad c = (Codes.FieldLoad) code; - writeRest(wide,stringCache.get(c.field),output); - } else if(code instanceof Codes.Quantify) { - Codes.Quantify f = (Codes.Quantify) code; - writeCodeBlock(wide,f,offset+1,labels,output); - } else if(code instanceof Codes.IfIs) { - Codes.IfIs c = (Codes.IfIs) code; - int target = labels.get(c.target); - writeRest(wide,typeCache.get(c.rightOperand),output); - writeTarget(wide,offset,target,output); - } else if(code instanceof Codes.If) { - Codes.If c = (Codes.If) code; - int target = labels.get(c.target); - writeTarget(wide,offset,target,output); - } else if(code instanceof Codes.Goto) { - Codes.Goto c = (Codes.Goto) code; - int target = labels.get(c.target); - writeTarget(wide,offset,target,output); - } else if(code instanceof Codes.Invoke) { + output.write_uv(stringCache.get(c.field)); + } else if (code instanceof Codes.Invoke) { Codes.Invoke c = (Codes.Invoke) code; - writeRest(wide,nameCache.get(c.name),output); - } else if(code instanceof Codes.Lambda) { + output.write_uv(nameCache.get(c.name)); + } else if (code instanceof Codes.Lambda) { Codes.Lambda c = (Codes.Lambda) code; - writeRest(wide,nameCache.get(c.name),output); - } else if(code instanceof Codes.Loop) { - Codes.Loop l = (Codes.Loop) code; - writeCodeBlock(wide,l,offset+1,labels,output); - } else if(code instanceof Codes.Update) { + output.write_uv(nameCache.get(c.name)); + } else if (code instanceof Codes.Update) { Codes.Update c = (Codes.Update) code; List fields = c.fields; - writeRest(wide,typeCache.get(c.afterType),output); - writeRest(wide,fields.size(),output); + output.write_uv(fields.size()); for (int i = 0; i != fields.size(); ++i) { - writeRest(wide, stringCache.get(fields.get(i)), output); + output.write_uv(stringCache.get(fields.get(i))); } - } else if(code instanceof Codes.Switch) { + } else if (code instanceof Codes.Switch) { Codes.Switch c = (Codes.Switch) code; - List> branches = c.branches; + List> branches = c.branches; int target = labels.get(c.defaultTarget); - writeTarget(wide,offset,target,output); - writeRest(wide,branches.size(),output); - for(Pair b : branches) { - writeRest(wide,constantCache.get(b.first()),output); - target = labels.get(b.second()); - writeTarget(wide,offset,target,output); - } - } - } - - private void writeBase(boolean wide, int value, BinaryOutputStream output) throws IOException { - if(wide) { - output.write_uv(value); - } else { - if(value >= 16) { - throw new IllegalArgumentException("invalid base value"); - } - output.write_un(value,4); - } - } - - private void writeRest(boolean wide, int value, BinaryOutputStream output) throws IOException { - if(wide) { - output.write_uv(value); - } else { - if(value >= 256) { - throw new IllegalArgumentException("invalid base value"); - } - output.write_u8(value); - } - } - - private void writeTarget(boolean wide, int offset, int target, - BinaryOutputStream output) throws IOException { - if (wide) { output.write_uv(target); - } else { - target = (target - offset) + 128; - output.write_u8(target); - } - } - - /** - * Calculate the "width" of a given bytecode. That is, whether or not either - * of the base or remainder components need to be encoded using the "wide" - * format. The wide format allows for unlimited precision, but occupies more - * space. The alternative "short" format uses fixed precision, but cannot - * encode all possible register operands and/or pool indices. - * - * @param code - * --- The bytecode whose width is to be determined. - * @param offset - * --- The current offset of this bytecode in the bytecode array - * being generated. This offset is measured in complete - * bytecodes, not in e.g. bytes. Therefore, the first bytecode - * has offset zero, the second bytecode has offset 1, etc. The - * offset is required for calculating jump targets for branching - * instructions (e.g. goto). Since jump targets (in short form) - * are encoded as a relative offset, we need to know our current - * offset to compute the relative target. - * @param labels - * --- A map from label to offset. This is required to determine - * the (relative) jump offset for a branching instruction. - * @return - */ - private int calculateWidth(Code code, int offset, HashMap labels) { - int maxBase = 0; - int maxRest = 0; - - if(code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode a = (Code.AbstractBytecode) code; - Type[] types = a.types(); - int[] targets = a.targets(); - int[] operands = a.operands(); - for(int i=0;i!=types.length;++i) { - maxBase = Math.max(maxBase,typeCache.get(types[i])); - } - for(int i=0;i!=targets.length;++i) { - maxBase = Math.max(maxBase,targets[i]); - } - for(int i=0;i!=operands.length;++i) { - maxBase = Math.max(maxBase,operands[i]); - } - } - - // now, deal with non-uniform opcodes - if(code instanceof Codes.AssertOrAssume) { - Codes.AssertOrAssume aa = (Codes.AssertOrAssume) code; - maxRest = aa.size(); - } else if(code instanceof Codes.Const) { - Codes.Const c = (Codes.Const) code; - maxRest = Math.max(maxRest,constantCache.get(c.constant)); - } else if(code instanceof Codes.Convert) { - Codes.Convert c = (Codes.Convert) code; - maxRest = Math.max(maxRest,typeCache.get(c.result)); - } else if(code instanceof Codes.FieldLoad) { - Codes.FieldLoad c = (Codes.FieldLoad) code; - maxRest = Math.max(maxRest,stringCache.get(c.field)); - } else if(code instanceof Codes.Quantify) { - Codes.Quantify f = (Codes.Quantify) code; - int[] operands = f.modifiedOperands; - maxBase = Math.max(f.startOperand, f.endOperand); - maxBase = Math.max(f.indexOperand, maxBase); - for(int i=0;i!=operands.length;++i) { - maxBase = Math.max(maxBase, operands[i]); - } - maxRest = Math.max(maxRest,f.size()); - } else if(code instanceof Codes.IfIs) { - Codes.IfIs c = (Codes.IfIs) code; - maxRest = Math.max(maxRest,typeCache.get(c.rightOperand)); - maxRest = Math.max(maxRest,targetWidth(c.target, offset, labels)); - } else if(code instanceof Codes.If) { - Codes.If c = (Codes.If) code; - maxRest = Math.max(maxRest,targetWidth(c.target, offset, labels)); - } else if(code instanceof Codes.Goto) { - Codes.Goto c = (Codes.Goto) code; - maxRest = Math.max(maxRest,targetWidth(c.target, offset, labels)); - } else if(code instanceof Codes.Invoke) { - Codes.Invoke c = (Codes.Invoke) code; - maxRest = Math.max(maxRest,nameCache.get(c.name)); - } else if(code instanceof Codes.Lambda) { - Codes.Lambda c = (Codes.Lambda) code; - maxRest = Math.max(maxRest,nameCache.get(c.name)); - } else if(code instanceof Codes.Loop) { - Codes.Loop l = (Codes.Loop) code; - int[] operands = l.modifiedOperands; - maxBase = 0; - for(int i=0;i!=operands.length;++i) { - maxBase = Math.max(maxBase, operands[i]); - } - maxRest = Math.max(maxRest,l.size()); - } else if(code instanceof Codes.Update) { - Codes.Update c = (Codes.Update) code; - maxRest = Math.max(maxRest,typeCache.get(c.afterType)); - ArrayList fields = c.fields; - for(int i=0;i!=fields.size();++i) { - String field = fields.get(i); - maxRest = Math.max(maxRest,stringCache.get(field)); - } - } else if(code instanceof Codes.Switch) { - Codes.Switch c = (Codes.Switch) code; - List> branches = c.branches; - maxRest = Math.max(maxRest,targetWidth(c.defaultTarget, offset, labels)); - maxRest = Math.max(maxRest,branches.size()); - for(Pair b : branches) { - maxRest = Math.max(maxRest,constantCache.get(b.first())); - maxRest = Math.max(maxRest,targetWidth(b.second(), offset, labels)); - } - } - - if(maxBase < 16) { - if(maxRest < 256) { - return 0; // standard - } else { - return Code.OPCODE_widerest; - } - } else { - if(maxRest < 256) { - return Code.OPCODE_wide; - } else { - return Code.OPCODE_widewide; + output.write_uv(branches.size()); + for (Pair b : branches) { + output.write_uv(constantCache.get(b.first())); + target = labels.get(b.second()); + output.write_uv(target); } } } - private int targetWidth(String label, int offset, - HashMap labels) { - int target = labels.get(label) - offset; - target = target + 128; - if(target < 0) { - // won't fit in a single byte - return 256; - } else { - return target; - } - } - private int generateModifiers(Collection modifiers) { int mods = 0; - for(Modifier m : modifiers) { - if(m == Modifier.PUBLIC) { + for (Modifier m : modifiers) { + if (m == Modifier.PUBLIC) { mods |= MODIFIER_Public; - } else if(m == Modifier.PRIVATE) { + } else if (m == Modifier.PRIVATE) { mods |= MODIFIER_Private; - } else if(m == Modifier.NATIVE) { + } else if (m == Modifier.NATIVE) { mods |= MODIFIER_Native; - } else if(m == Modifier.EXPORT) { + } else if (m == Modifier.EXPORT) { mods |= MODIFIER_Export; } } return mods; } + /** + * Count the number of label bytecodes in a given block. This is needed to + * help calculate the number of "real" bytecodes in that block, since labels + * are not real bytecodes. Rather they are just markers acting as branch + * targets. + * + * @param block + * @return + */ + private int countLabels(CodeForest.Block block) { + int nlabels = 0; + for (int i = 0; i != block.size(); ++i) { + Code code = block.get(i).code(); + if (code instanceof Codes.Label) { + nlabels++; + } + } + return nlabels; + } + private void buildPools(WyilFile module) { stringPool.clear(); stringCache.clear(); @@ -843,7 +886,7 @@ private void buildPools(WyilFile module) { pathCache.clear(); // preload the path root pathPool.add(null); - pathCache.put(wyfs.util.Trie.ROOT,0); + pathCache.put(wyfs.util.Trie.ROOT, 0); namePool.clear(); nameCache.clear(); @@ -855,18 +898,18 @@ private void buildPools(WyilFile module) { typeCache.clear(); addPathItem(module.id()); - for(WyilFile.Block d : module.blocks()) { + for (WyilFile.Block d : module.blocks()) { buildPools(d); } } private void buildPools(WyilFile.Block declaration) { - if(declaration instanceof WyilFile.Type) { - buildPools((WyilFile.Type)declaration); - } else if(declaration instanceof WyilFile.Constant) { - buildPools((WyilFile.Constant)declaration); - } else if(declaration instanceof WyilFile.FunctionOrMethod) { - buildPools((WyilFile.FunctionOrMethod)declaration); + if (declaration instanceof WyilFile.Type) { + buildPools((WyilFile.Type) declaration); + } else if (declaration instanceof WyilFile.Constant) { + buildPools((WyilFile.Constant) declaration); + } else if (declaration instanceof WyilFile.FunctionOrMethod) { + buildPools((WyilFile.FunctionOrMethod) declaration); } } @@ -884,71 +927,64 @@ private void buildPools(WyilFile.Constant declaration) { private void buildPools(WyilFile.FunctionOrMethod declaration) { addStringItem(declaration.name()); addTypeItem(declaration.type()); - buildPools(declaration.precondition()); - buildPools(declaration.body()); - buildPools(declaration.postcondition()); + buildPools(declaration.code()); } - private void buildPools(List blocks) { - for(CodeBlock block : blocks) { - buildPools(block); + private void buildPools(CodeForest forest) { + for(int i=0;i!=forest.numRegisters();++i) { + buildPools(forest.getRegister(i)); + } + for(int i=0;i!=forest.numBlocks();++i) { + buildPools(forest.get(i)); } } - private void buildPools(CodeBlock block) { - if (block == null) { - return; - } + private void buildPools(CodeForest.Register reg) { + addTypeItem(reg.type()); + } + + private void buildPools(CodeForest.Block block) { for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); - buildPools(code); - if (code instanceof CodeBlock) { - buildPools((CodeBlock) code); - } + CodeForest.Entry entry = block.get(i); + buildPools(entry.code()); + // TODO: handle entry attributes } } private void buildPools(Code code) { // First, deal with special cases - if(code instanceof Codes.Const) { + if (code instanceof Codes.Const) { Codes.Const c = (Codes.Const) code; addConstantItem(c.constant); - } else if(code instanceof Codes.Convert) { - Codes.Convert c = (Codes.Convert) code; - addTypeItem(c.result); - } else if(code instanceof Codes.FieldLoad) { + } else if (code instanceof Codes.FieldLoad) { Codes.FieldLoad c = (Codes.FieldLoad) code; addStringItem(c.field); - } else if(code instanceof Codes.IfIs) { - Codes.IfIs c = (Codes.IfIs) code; - addTypeItem(c.rightOperand); - } else if(code instanceof Codes.Invoke) { + }else if (code instanceof Codes.Invoke) { Codes.Invoke c = (Codes.Invoke) code; addNameItem(c.name); - } else if(code instanceof Codes.Lambda) { + } else if (code instanceof Codes.Lambda) { Codes.Lambda c = (Codes.Lambda) code; addNameItem(c.name); - } else if(code instanceof Codes.Update) { + } else if (code instanceof Codes.Update) { Codes.Update c = (Codes.Update) code; - addTypeItem(c.afterType); - for(Codes.LVal l : c) { - if(l instanceof Codes.RecordLVal) { + for (Codes.LVal l : c) { + if (l instanceof Codes.RecordLVal) { Codes.RecordLVal lv = (Codes.RecordLVal) l; addStringItem(lv.field); } } - } else if(code instanceof Codes.Switch) { + } else if (code instanceof Codes.Switch) { Codes.Switch s = (Codes.Switch) code; - for(Pair b : s.branches) { + for (Pair b : s.branches) { addConstantItem(b.first()); } } // Second, deal with standard cases - if(code instanceof Code.AbstractBytecode) { + if (code instanceof Code.AbstractBytecode) { Code.AbstractBytecode a = (Code.AbstractBytecode) code; - for(Type type : a.types()) { + for (Type type : a.types()) { addTypeItem(type); } } @@ -956,11 +992,10 @@ private void buildPools(Code code) { private int addNameItem(NameID name) { Integer index = nameCache.get(name); - if(index == null) { + if (index == null) { int i = namePool.size(); nameCache.put(name, i); - namePool.add(new NAME_Item(addPathItem(name.module()), - addStringItem(name.name()))); + namePool.add(new NAME_Item(addPathItem(name.module()), addStringItem(name.name()))); return i; } else { return index; @@ -969,7 +1004,7 @@ private int addNameItem(NameID name) { private int addStringItem(String string) { Integer index = stringCache.get(string); - if(index == null) { + if (index == null) { int i = stringPool.size(); stringCache.put(string, i); stringPool.add(string); @@ -981,10 +1016,10 @@ private int addStringItem(String string) { private int addPathItem(Path.ID pid) { Integer index = pathCache.get(pid); - if(index == null) { + if (index == null) { int parent = addPathItem(pid.parent()); int i = pathPool.size(); - pathPool.add(new PATH_Item(parent,addStringItem(pid.last()))); + pathPool.add(new PATH_Item(parent, addStringItem(pid.last()))); pathCache.put(pid, i); return i; } else { @@ -999,7 +1034,7 @@ private int addTypeItem(Type t) { // types individually ... because that's sooooo inefficient! Integer index = typeCache.get(t); - if(index == null) { + if (index == null) { int i = typePool.size(); typeCache.put(t, i); typePool.add(t); @@ -1012,7 +1047,7 @@ private int addTypeItem(Type t) { private int addConstantItem(Constant v) { Integer index = constantCache.get(v); - if(index == null) { + if (index == null) { // All subitems must have lower indices than the containing item. // So, we must add subitems first before attempting to allocate a // place for this value. @@ -1028,18 +1063,18 @@ private int addConstantItem(Constant v) { } private void addConstantSubitems(Constant v) { - if(v instanceof Constant.Array) { + if (v instanceof Constant.Array) { Constant.Array l = (Constant.Array) v; for (Constant e : l.values) { addConstantItem(e); } - } else if(v instanceof Constant.Record) { + } else if (v instanceof Constant.Record) { Constant.Record r = (Constant.Record) v; - for (Map.Entry e : r.values.entrySet()) { + for (Map.Entry e : r.values.entrySet()) { addStringItem(e.getKey()); addConstantItem(e.getValue()); } - } else if(v instanceof Constant.Lambda){ + } else if (v instanceof Constant.Lambda) { Constant.Lambda fm = (Constant.Lambda) v; addTypeItem(fm.type()); addNameItem(fm.name); @@ -1071,12 +1106,7 @@ public PATH_Item(int parentIndex, int stringIndex) { } private enum NAME_Kind { - PACKAGE(0), - MODULE(1), - CONSTANT(2), - TYPE(3), - FUNCTION(4), - METHOD(5); + PACKAGE(0), MODULE(1), CONSTANT(2), TYPE(3), FUNCTION(4), METHOD(5); private final int kind; @@ -1112,8 +1142,8 @@ private class NAME_Item { */ public final int nameIndex; - public NAME_Item(/*NAME_Kind kind, */int pathIndex, int nameIndex) { - //this.kind = kind; + public NAME_Item(/* NAME_Kind kind, */int pathIndex, int nameIndex) { + // this.kind = kind; this.pathIndex = pathIndex; this.nameIndex = nameIndex; } @@ -1133,10 +1163,7 @@ public NAME_Item(/*NAME_Kind kind, */int pathIndex, int nameIndex) { public final static int BLOCK_Function = 12; public final static int BLOCK_Method = 13; // ... (anticipating some others here) - public final static int BLOCK_Body = 20; - public final static int BLOCK_Precondition = 21; - public final static int BLOCK_Postcondition = 22; - public final static int BLOCK_Constraint = 23; + public final static int BLOCK_CodeForest = 20; // ... (anticipating some others here) // ========================================================================= @@ -1148,12 +1175,12 @@ public NAME_Item(/*NAME_Kind kind, */int pathIndex, int nameIndex) { public final static int CONSTANT_False = 2; public final static int CONSTANT_Byte = 3; public final static int CONSTANT_Int = 5; - public final static int CONSTANT_Real = 6; -// public final static int CONSTANT_Set = 7; - public final static int CONSTANT_List = 9; + // public final static int CONSTANT_Real = 6; + // public final static int CONSTANT_Set = 7; + public final static int CONSTANT_Array = 9; public final static int CONSTANT_Record = 10; -// public final static int CONSTANT_Tuple = 11; -// public final static int CONSTANT_Map = 12; + // public final static int CONSTANT_Tuple = 11; + // public final static int CONSTANT_Map = 12; public final static int CONSTANT_Function = 13; public final static int CONSTANT_Method = 14; @@ -1171,5 +1198,5 @@ public NAME_Item(/*NAME_Kind kind, */int pathIndex, int nameIndex) { public final static int MODIFIER_MANGLE_MASK = 3 << 4; public final static int MODIFIER_Native = 1 << 4; public final static int MODIFIER_Export = 2 << 4; - //public final static int MODIFIER_Total = 3 << 4; + // public final static int MODIFIER_Total = 3 << 4; } diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index 20f5f35e5d..aa0e206f27 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -28,6 +28,7 @@ import java.util.*; import wycc.lang.SyntacticElement; + import static wyil.lang.CodeUtils.*; /** @@ -138,64 +139,6 @@ public interface Code { */ public abstract int opcode(); - // =============================================================== - // Subtypes - // =============================================================== - - /** - * A Unit bytecode is one which does not contain other bytecodes. - * - * @author David J. Pearce - * - */ - public static abstract class Unit implements Code { - - @Override - public void registers(java.util.Set register) { - // default implementation does nothing - } - - @Override - public abstract Code.Unit remap(Map binding); - } - - /** - * A compound bytecode represents a bytecode that contains sequence of zero - * or more bytecodes. For example, the loop bytecode contains its loop body. - * - * @author David J. Pearce - * - */ - public static abstract class Compound extends CodeBlock implements Code { - - public Compound(Code... bytecodes) { - super(bytecodes); - } - - public Compound(Collection bytecodes) { - super(bytecodes); - } - - @Override - public void registers(java.util.Set register) { - for(int i=0;i!=size();++i) { - get(i).registers(register); - } - } - - @Override - public Code.Compound remap(Map binding) { - Code.Compound block = clone(); - for(int i=0;i!=size();++i) { - Code code = get(i); - block.set(i,code.remap(binding)); - } - return block; - } - - public abstract Code.Compound clone(); - } - // ===============================================================C // Abstract Bytecodes // =============================================================== @@ -210,7 +153,7 @@ public Code.Compound remap(Map binding) { * @param * --- the type associated with this bytecode. */ - public static abstract class AbstractBytecode extends Code.Unit { + public static abstract class AbstractBytecode implements Code { protected final Type[] types; private final int[] targets; protected final int[] operands; @@ -228,7 +171,7 @@ public AbstractBytecode(Type[] types, int[] targets, int... operands) { } @Override - public final void registers(java.util.Set registers) { + public void registers(java.util.Set registers) { for (int i = 0; i != targets().length; ++i) { registers.add(targets()[i]); } @@ -238,7 +181,7 @@ public final void registers(java.util.Set registers) { } @Override - public final Code.Unit remap(Map binding) { + public Code remap(Map binding) { int[] nTargets = remapOperands(binding, targets()); int[] nOperands = remapOperands(binding, operands()); if (nTargets != targets() || nOperands != operands()) { @@ -247,7 +190,7 @@ public final Code.Unit remap(Map binding) { return this; } - protected abstract Code.Unit clone(int[] nTargets, int[] nOperands); + protected abstract Code clone(int[] nTargets, int[] nOperands); @Override public int hashCode() { @@ -309,130 +252,204 @@ public int operand(int i) { } } - // ========================================================================= - // Opcodes - // ========================================================================= + /** + * A compound bytecode represents a bytecode that contains sequence of zero + * or more bytecodes. For example, the loop bytecode contains its loop body. + * The nested block of bytecodes is represented as a block identifier in the + * enclosing forest. + * + * @author David J. Pearce + * + */ + public static abstract class AbstractCompoundBytecode extends AbstractBytecode { + protected final int block; - public static final int FMT_SHIFT = 5; - public static final int FMT_MASK = 7 << FMT_SHIFT; + public AbstractCompoundBytecode(int block, Type[] types, int[] targets, int... operands) { + super(types, targets, operands); + this.block = block; + } - public static final int FMT_EMPTY = 0 << FMT_SHIFT; - public static final int FMT_UNARYOP = 1 << FMT_SHIFT; - public static final int FMT_UNARYASSIGN = 2 << FMT_SHIFT; - public static final int FMT_BINARYOP = 3 << FMT_SHIFT; - public static final int FMT_BINARYASSIGN = 4 << FMT_SHIFT; - public static final int FMT_NARYOP = 5 << FMT_SHIFT; - public static final int FMT_NARYASSIGN = 6 << FMT_SHIFT; - public static final int FMT_OTHER = 7 << FMT_SHIFT; + public int block() { + return block; + } + + public int hashCode() { + return super.hashCode() ^ block; + } + + public boolean equals(Object o) { + if(o instanceof AbstractCompoundBytecode) { + AbstractCompoundBytecode abc = (AbstractCompoundBytecode) o; + return block == abc.block && super.equals(o); + } + return false; + } + } + + /** + * A compound bytecode represents a bytecode that contains sequence of zero + * or more bytecodes. For example, the loop bytecode contains its loop body. + * The nested block of bytecodes is represented as a block identifier in the + * enclosing forest. + * + * @author David J. Pearce + * + */ + public static abstract class AbstractBranchingBytecode extends AbstractBytecode { + protected final String destination; + + public AbstractBranchingBytecode(String destination, Type[] types, int[] targets, int... operands) { + super(types, targets, operands); + this.destination = destination; + } + + public String destination() { + return destination; + } + + public int hashCode() { + return super.hashCode() ^ destination.hashCode(); + } + + public boolean equals(Object o) { + if(o instanceof AbstractBranchingBytecode) { + AbstractBranchingBytecode abc = (AbstractBranchingBytecode) o; + return destination.equals(abc.destination) && super.equals(o); + } + return false; + } + } // ========================================================================= // Empty Bytecodes // ========================================================================= - public static final int OPCODE_nop = 0 + FMT_EMPTY; - public static final int OPCODE_goto = 3 + FMT_EMPTY; // +INT - public static final int OPCODE_fail = 4 + FMT_EMPTY; + public static final int OPCODE_nop = 0; + public static final int OPCODE_goto = 1; + public static final int OPCODE_fail = 2; + public static final int OPCODE_assert = 4; + public static final int OPCODE_assume = 5; + public static final int OPCODE_invariant = 6; // ========================================================================= - // Unary Operators + // Unary Operators. // ========================================================================= - public static final int OPCODE_debug = 0 + FMT_UNARYOP; - public static final int OPCODE_return = 1 + FMT_NARYASSIGN; - public static final int OPCODE_ifis = 3 + FMT_UNARYOP; // +TYPEIDX - public static final int OPCODE_switch = 4 + FMT_UNARYOP; // +OTHER - public static final int OPCODE_throw = 5 + FMT_UNARYOP; + public static final int UNARY_OPERATOR = 7; + + public static final int OPCODE_debug = UNARY_OPERATOR+0; + public static final int OPCODE_return = UNARY_OPERATOR+1; + public static final int OPCODE_ifis = UNARY_OPERATOR+2; + public static final int OPCODE_switch = UNARY_OPERATOR+3; + public static final int OPCODE_throw = UNARY_OPERATOR+4; // ========================================================================= // Unary Assignables // ========================================================================= - public static final int OPCODE_assign = 0 + FMT_UNARYASSIGN; - public static final int OPCODE_dereference = 1 + FMT_UNARYASSIGN; - public static final int OPCODE_invert = 2 + FMT_UNARYASSIGN; - public static final int OPCODE_lengthof = 3 + FMT_UNARYASSIGN; - public static final int OPCODE_move = 4 + FMT_UNARYASSIGN; - public static final int OPCODE_newobject = 5 + FMT_UNARYASSIGN; - public static final int OPCODE_neg = 6 + FMT_UNARYASSIGN; -// public static final int OPCODE_numerator = 7 + FMT_UNARYASSIGN; -// public static final int OPCODE_denominator = 8 + FMT_UNARYASSIGN; - public static final int OPCODE_not = 9 + FMT_UNARYASSIGN; -// public static final int OPCODE_tupleload = 10 + FMT_UNARYASSIGN; - public static final int OPCODE_fieldload = 11 + FMT_UNARYASSIGN; // +STRINGIDX - public static final int OPCODE_convert = 12 + FMT_UNARYASSIGN; // +TYPEIDX - public static final int OPCODE_const = 14 + FMT_UNARYASSIGN; // +CONSTIDX + public static final int UNARY_ASSIGNABLE = UNARY_OPERATOR+5; + + public static final int OPCODE_assign = UNARY_ASSIGNABLE+0; + public static final int OPCODE_dereference = UNARY_ASSIGNABLE+1; + public static final int OPCODE_invert = UNARY_ASSIGNABLE+2; + public static final int OPCODE_lengthof = UNARY_ASSIGNABLE+3; + public static final int OPCODE_move = UNARY_ASSIGNABLE+4; + public static final int OPCODE_newobject = UNARY_ASSIGNABLE+5; + public static final int OPCODE_neg = UNARY_ASSIGNABLE+6; + public static final int OPCODE_not = UNARY_ASSIGNABLE+7; + public static final int OPCODE_fieldload = UNARY_ASSIGNABLE+8; + public static final int OPCODE_convert = UNARY_ASSIGNABLE+9; + public static final int OPCODE_const = UNARY_ASSIGNABLE+10; // ========================================================================= // Binary Operators // ========================================================================= - public static final int OPCODE_ifeq = 0 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifne = 1 + FMT_BINARYOP; // +INT - public static final int OPCODE_iflt = 2 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifle = 3 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifgt = 4 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifge = 5 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifel = 6 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifss = 7 + FMT_BINARYOP; // +INT - public static final int OPCODE_ifse = 8 + FMT_BINARYOP; // +INT - + public static final int BINARY_OPERATOR = UNARY_ASSIGNABLE+11; + + public static final int OPCODE_ifeq = BINARY_OPERATOR+0; + public static final int OPCODE_ifne = BINARY_OPERATOR+1; + public static final int OPCODE_iflt = BINARY_OPERATOR+2; + public static final int OPCODE_ifle = BINARY_OPERATOR+3; + public static final int OPCODE_ifgt = BINARY_OPERATOR+4; + public static final int OPCODE_ifge = BINARY_OPERATOR+5; + // ========================================================================= // Binary Assignables // ========================================================================= - public static final int OPCODE_add = 0 + FMT_BINARYASSIGN; - public static final int OPCODE_sub = 1 + FMT_BINARYASSIGN; - public static final int OPCODE_mul = 2 + FMT_BINARYASSIGN; - public static final int OPCODE_div = 3 + FMT_BINARYASSIGN; - public static final int OPCODE_rem = 4 + FMT_BINARYASSIGN; - public static final int OPCODE_bitwiseor = 5 + FMT_BINARYASSIGN; - public static final int OPCODE_bitwisexor = 6 + FMT_BINARYASSIGN; - public static final int OPCODE_bitwiseand = 7 + FMT_BINARYASSIGN; - public static final int OPCODE_lshr = 8 + FMT_BINARYASSIGN; - public static final int OPCODE_rshr = 9 + FMT_BINARYASSIGN; - public static final int OPCODE_indexof = 11 + FMT_BINARYASSIGN; - public static final int OPCODE_listgen = 12 + FMT_BINARYASSIGN; -// public static final int OPCODE_unionl = 13 + FMT_BINARYASSIGN; -// public static final int OPCODE_unionr = 14 + FMT_BINARYASSIGN; -// public static final int OPCODE_intersect = 15 + FMT_BINARYASSIGN; -// public static final int OPCODE_intersectl = 16 + FMT_BINARYASSIGN; -// public static final int OPCODE_intersectr = 17 + FMT_BINARYASSIGN; -// public static final int OPCODE_difference = 18 + FMT_BINARYASSIGN; -// public static final int OPCODE_differencel = 19 + FMT_BINARYASSIGN; -// public static final int OPCODE_append = 20 + FMT_BINARYASSIGN; -// public static final int OPCODE_appendl = 21 + FMT_BINARYASSIGN; -// public static final int OPCODE_appendr = 22 + FMT_BINARYASSIGN; - - // ========================================================================= - // Nary Operators - // ========================================================================= - public static final int OPCODE_loop = 0 + FMT_NARYOP; -// public static final int OPCODE_forall = 1 + FMT_NARYOP; - public static final int OPCODE_quantify = 2 + FMT_NARYOP; - public static final int OPCODE_void = 7 + FMT_NARYOP; + public static final int BINARY_ASSIGNABLE = BINARY_OPERATOR+6; + + public static final int OPCODE_add = BINARY_ASSIGNABLE+0; + public static final int OPCODE_sub = BINARY_ASSIGNABLE+1; + public static final int OPCODE_mul = BINARY_ASSIGNABLE+2; + public static final int OPCODE_div = BINARY_ASSIGNABLE+3; + public static final int OPCODE_rem = BINARY_ASSIGNABLE+4; + public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+5; + public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+6; + public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+7; + public static final int OPCODE_lshr = BINARY_ASSIGNABLE+8; + public static final int OPCODE_rshr = BINARY_ASSIGNABLE+9; + public static final int OPCODE_indexof = BINARY_ASSIGNABLE+10; + public static final int OPCODE_listgen = BINARY_ASSIGNABLE+11; // ========================================================================= // Nary Assignables // ========================================================================= - public static final int OPCODE_newlist = 0 + FMT_NARYASSIGN; -// public static final int OPCODE_newset = 1 + FMT_NARYASSIGN; -// public static final int OPCODE_newmap = 2 + FMT_NARYASSIGN; -// public static final int OPCODE_newtuple = 3 + FMT_NARYASSIGN; - public static final int OPCODE_indirectinvokefn = 4 + FMT_NARYASSIGN; - public static final int OPCODE_indirectinvokemd = 5 + FMT_NARYASSIGN; -// public static final int OPCODE_sublist = 6 + FMT_NARYASSIGN; - public static final int OPCODE_invokefn = 8 + FMT_NARYASSIGN; // +NAMEIDX - public static final int OPCODE_invokemd = 9 + FMT_NARYASSIGN; // +NAMEIDX - public static final int OPCODE_lambdafn = 10 + FMT_NARYASSIGN; // +NAMEIDX - public static final int OPCODE_lambdamd = 11 + FMT_NARYASSIGN; // +NAMEIDX - public static final int OPCODE_newrecord = 12 + FMT_NARYASSIGN;// +OTHER - + public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+12; + + public static final int OPCODE_newlist = NARY_ASSIGNABLE+0; + public static final int OPCODE_newrecord = NARY_ASSIGNABLE+1; + public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; + public static final int OPCODE_indirectinvoke = NARY_ASSIGNABLE+3; + public static final int OPCODE_lambda = NARY_ASSIGNABLE+4; + public static final int OPCODE_loop = NARY_ASSIGNABLE+5; + public static final int OPCODE_quantify = NARY_ASSIGNABLE+6; + public static final int OPCODE_update = NARY_ASSIGNABLE+7; + public static final int OPCODE_void = NARY_ASSIGNABLE+8; + // ========================================================================= - // Other + // Bytecode Schemas // ========================================================================= - public static final int OPCODE_trycatch = 0 + FMT_OTHER; - public static final int OPCODE_update = 1 + FMT_OTHER; - public static final int OPCODE_assertblock = 2 + FMT_OTHER; - public static final int OPCODE_assumeblock = 3 + FMT_OTHER; - public static final int OPCODE_invariantblock = 4 + FMT_OTHER; - - // this is where I will locate the WIDE and WIDEWIDE Markers - public static final int OPCODE_wide = 29 + FMT_OTHER; - public static final int OPCODE_widerest = 30 + FMT_OTHER; - public static final int OPCODE_widewide = 31 + FMT_OTHER; + + public enum Targets { + ZERO, ONE, TWO, MANY + } + + public enum Operands { + ZERO, ONE, TWO, MANY + } + + public enum Types { + ZERO, ONE, TWO, MANY + } + + public enum Extras { + STRING, // index into string pool + CONSTANT, // index into constant pool + NAME, // index into name pool + TARGET, // branch target + BLOCK, // block index + STRING_ARRAY, // determined on the fly + SWITCH_ARRAY, // determined on the fly + } + + public static abstract class Schema { + private final Targets targets; + private final Operands operands; + private final Types types; + private final Extras[] extras; + + public Schema(Targets targets, Operands operands, Types types, Extras... extras) { + this.targets = targets; + this.operands = operands; + this.types = types; + this.extras = extras; + } + + public Extras[] extras() { + return extras; + } + + public abstract Code construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras); + + public String toString() { + return "<" + targets.toString() + " targets, " + operands + " operands, " + types + " types, " + Arrays.toString(extras) + ">"; + } + } } diff --git a/modules/wyil/src/wyil/lang/CodeBlock.java b/modules/wyil/src/wyil/lang/CodeBlock.java deleted file mode 100644 index 6c3b0b482a..0000000000 --- a/modules/wyil/src/wyil/lang/CodeBlock.java +++ /dev/null @@ -1,176 +0,0 @@ -package wyil.lang; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -/** - *

- * Represents a complete sequence of bytecode instructions. For example, every - * function or method body is a single Block. Likewise, the invariant for a - * given type is a Block. Finally, a Block permits attributes to be attached to - * every bytecode it contains. An example attribute is one for holding the - * location of the source code which generated the bytecode. - *

- * - * @author David J. Pearce - * - */ -public class CodeBlock implements Iterable { - protected ArrayList bytecodes; - - public CodeBlock() { - bytecodes = new ArrayList(); - } - - public CodeBlock(Code... bytecodes) { - this.bytecodes = new ArrayList(); - Collections.addAll(this.bytecodes,bytecodes); - } - - public CodeBlock(Collection bytecodes) { - this.bytecodes = new ArrayList(bytecodes); - } - - // =================================================================== - // Accessor Methods - // =================================================================== - - /** - * Determine the number of slots used in this block. - * - * @return - */ - public int numSlots() { - HashSet slots = new HashSet(); - for (Code c : bytecodes) { - c.registers(slots); - } - int r = 0; - for (int i : slots) { - r = Math.max(r, i + 1); - } - return r; - } - - /** - * Determine the exact slots used in this block. - * - * @return - */ - public Set slots() { - HashSet slots = new HashSet(); - for (Code c : bytecodes) { - c.registers(slots); - } - return slots; - } - - public int size() { - return bytecodes.size(); - } - - public Iterator iterator() { - return bytecodes.iterator(); - } - - public Code get(int index) { - return bytecodes.get(index); - } - - /** - * Get the entry associated with a given bytecode id. This will recurse - * nested code blocks as necessary to locate this bytecode. - * - * @param id - * @return - */ - public Code get(Index index) { - int[] indexArray = index.toArray(); - CodeBlock iterator = this; - int i=0; - while (i < indexArray.length - 1) { - iterator = (CodeBlock) iterator.get(indexArray[i]); - i = i + 1; - } - return iterator.get(indexArray[i]); - } - - /** - * Returns a reference to the internal bytecode array. Note that this list - * is not intended to be modified. - * - * @return - */ - public List bytecodes() { - return Collections.unmodifiableList(bytecodes); - } - - // =================================================================== - // Append Methods - // =================================================================== - - /** - * Append a bytecode onto the end of this block. It is assumed that the - * bytecode employs the same environment as this block. - * - * @param code - * --- bytecode to append - */ - public boolean add(Code code) { - return bytecodes.add(code); - } - - // =================================================================== - // Insert Methods - // =================================================================== - - /** - *

Insert a bytecode at a given position in this block. It is assumed that - * the bytecode employs the same environment as this block. The bytecode at - * the given position (and any after it) are shifted one position down.

- * - * @param index --- position to insert at. - * @param code --- bytecode to insert at the given position. - */ - public void add(int index, Code code) { - bytecodes.add(index,code); - } - - // =================================================================== - // Update Methods - // =================================================================== - - /** - *

- * Replace the bytecode at a given position in this block with another. It - * is assumed that the bytecode employs the same environment as this block. - *

- * - * @param index --- position of bytecode to replace. - * @param code --- bytecode to replace with. - */ - public void set(int index, Code code) { - bytecodes.set(index, code); - } - - // =================================================================== - // Replace and Remove Methods - // =================================================================== - - /** - *

- * Remove the bytecode at a given position in this block with another. - *

- * - * @param index --- position of bytecode to replace. * - */ - public Code remove(int index) { - return bytecodes.remove(index); - } -} \ No newline at end of file diff --git a/modules/wyil/src/wyil/lang/CodeForest.java b/modules/wyil/src/wyil/lang/CodeForest.java new file mode 100644 index 0000000000..47fbdb6cc0 --- /dev/null +++ b/modules/wyil/src/wyil/lang/CodeForest.java @@ -0,0 +1,287 @@ +package wyil.lang; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import wycc.util.Pair; + +/** + * A CodeBlock can be thought of in different ways. For example, it can be + * thought of as a forest of rooted trees; or, more simply, as an array of + * bytecode sequences. Some bytecodes can be thought of as compound structures + * containing nested blocks of bytecodes. In reality, such compound bytecodes + * are not truly nested, but contain block identifiers representing their + * contents. + * + * @author David J. Pearce + * + */ +public class CodeForest { + private final ArrayList registers; + private final ArrayList roots; + private final ArrayList blocks; + + public CodeForest() { + this(Collections.EMPTY_LIST); + } + + public CodeForest(List registers) { + this.registers = new ArrayList(registers); + this.roots = new ArrayList(); + this.blocks = new ArrayList(); + } + + // =================================================================== + // Accessor Methods + // =================================================================== + + /** + * Get the number of blocks within this code forest. + * + * @return + */ + public int numBlocks() { + return blocks.size(); + } + + /** + * Get the number of registered roots within this forest. + * + * @return + */ + public int numRoots() { + return roots.size(); + } + + public int numRegisters() { + return registers.size(); + } + + /** + * Get the list of registers declared for this block. This list is mutable + * and may be read or written. + * + * @return + */ + public List registers() { + return registers; + } + + /** + * Return a given root within the block. Roots are themselves indexed from + * 0. + * + * @param i + * @return + */ + public int getRoot(int i) { + return roots.get(i); + } + + /** + * Get a specific block within this forest. The returned list is mutable and + * can be modified at will. + * + * @param index + * @return + */ + public Block get(int index) { + return blocks.get(index); + } + + /** + * Get a specific register declared in this forest. + * + * @param index + * @return + */ + public Register getRegister(int index) { + return registers.get(index); + } + + /** + * Get a specific bytecode withing this forest. + * @param index + * @return + */ + public Entry get(Index index) { + return blocks.get(index.block).get(index.offset); + } + + /** + * Add a new block to the forest, whilst returning its identifier. + * + * @param block + * @return + */ + public int add(Block block) { + blocks.add(block); + return blocks.size()-1; + } + + /** + * Add a new block to the forest which is also a root block. Root blocks are + * not subject to garbage collection. + * + * @param block + * @return + */ + public int addAsRoot(Block block) { + int index = add(block); + roots.add(index); + return index; + } + + /** + * Mark block identifier as a root + * + * @param root + */ + public void addRoot(int root) { + roots.add(root); + } + + /** + * Garbage collection unused blocks. + */ + public void gc() { + + } + + /** + * Represents a bytecode location within a code forest. This is simply a + * pair of the block identifier and the position within that block. + * + * @author David J. Pearce + * + */ + public static final class Index { + private int block; + private int offset; + + public Index(int block, int offset) { + this.block = block; + this.offset = offset; + } + + public int block() { return block; } + public int offset() { return offset; } + + public boolean equals(Object o) { + if(o instanceof Index) { + Index i = (Index) o; + return block == i.block && offset == i.offset; + } + return false; + } + + public int hashCode() { + return block ^ offset; + } + + public Index next() { + return new Index(block,offset+1); + } + } + + /** + * Represents a sequence of bytecodes within a code forest. + * + * @author David J. Pearce + * + */ + public static class Block extends ArrayList { + + public Block() { + super(); + } + + public Block(Collection entries) { + super(entries); + } + public void add(Code code, Attribute...attributes) { + super.add(new Entry(code,attributes)); + } + public void add(Code code, List attributes) { + super.add(new Entry(code,attributes)); + } + public void add(int start, Code code, Attribute...attributes) { + super.add(start, new Entry(code,attributes)); + } + public void add(int start, Code code, List attributes) { + super.add(start, new Entry(code,attributes)); + } + public void set(int i, Code code, Attribute...attributes) { + super.set(i,new Entry(code,attributes)); + } + public void set(int i, Code code, List attributes) { + super.set(i,new Entry(code,attributes)); + } + } + + /** + * Represents an entry within a code block. This is a pairing of a bytecode + * and a list of bytecodes. + * + * @author David J. Pearce + * + */ + public static class Entry extends Pair> { + public Entry(Code code, List attributes) { + super(code,attributes); + } + public Entry(Code code, Attribute... attributes) { + super(code,Arrays.asList(attributes)); + } + public Code code() { + return first(); + } + public T attribute(Class clazz) { + List attributes = second(); + for(int i=0;i!=attributes.size();++i) { + Attribute a = attributes.get(i); + if(clazz.isInstance(a)) { + return (T) a; + } + } + return null; + } + public List attributes() { + return second(); + } + } + + /** + * Represents the declaration information associated with a given register. + * + * @author David J. Pearce + * + */ + public static class Register { + private final Type type; + private final String name; + + public Register(Type type, String name) { + this.type = type; + this.name = name; + } + + public Type type() { + return type; + } + + public String name() { + return name; + } + + public String toString() { + return type + " " + name; + } + } +} \ No newline at end of file diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java index 107e4d6e37..507135eef4 100644 --- a/modules/wyil/src/wyil/lang/CodeUtils.java +++ b/modules/wyil/src/wyil/lang/CodeUtils.java @@ -6,7 +6,6 @@ import java.util.Map; import wyil.lang.Codes.Comparator; -import wyil.util.AttributedCodeBlock; public class CodeUtils { @@ -86,9 +85,19 @@ public static Codes.Comparator invert(Codes.Comparator cop) { * @param block * @return */ - public static Map buildLabelMap(AttributedCodeBlock block) { - HashMap labels = new HashMap(); - buildLabelMap(new CodeBlock.Index(null), null, labels, block); + public static Map buildLabelMap(CodeForest forest) { + HashMap labels = new HashMap(); + for (int i = 0; i != forest.numBlocks(); ++i) { + CodeForest.Block block = forest.get(i); + for (int j = 0; j != block.size(); ++j) { + Code code = block.get(j).code(); + if (code instanceof Codes.Label) { + // Found a label, so register it in the labels map + Codes.Label label = (Codes.Label) code; + labels.put(label.label, new CodeForest.Index(i, j)); + } + } + } return labels; } @@ -102,21 +111,8 @@ public static Map buildLabelMap(AttributedCodeBlock blo * @param block * Root block */ - private static void buildLabelMap(CodeBlock.Index index, CodeBlock.Index parent, - Map labels, CodeBlock block) { + private static void buildLabelMap(int blockID, Map labels, CodeForest forest) { // - for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i); - if (code instanceof Codes.Label) { - // Found a label, so register it in the labels map - Codes.Label label = (Codes.Label) code; - labels.put(label.label, index); - } else if (code instanceof CodeBlock) { - // Found a subblock, so traverse that - CodeBlock subblock = (CodeBlock) code; - buildLabelMap(index.firstWithin(), index, labels, subblock); - } - index = index.next(); - } + } } diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index 95e076de6d..daef2a3850 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -7,6 +7,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import wycc.lang.NameID; import wycc.util.Pair; @@ -76,8 +77,8 @@ public abstract class Codes { * --- message to report upon failure. * @return */ - public static Assert Assert(Collection bytecodes) { - return new Assert(bytecodes); + public static Assert Assert(int block) { + return new Assert(block); } /** @@ -88,8 +89,8 @@ public static Assert Assert(Collection bytecodes) { * --- message to report upon failure. * @return */ - public static Assume Assume(Collection bytecodes) { - return new Assume(bytecodes); + public static Assume Assume(int block) { + return new Assume(block); } public static BinaryOperator BinaryOperator(Type type, int target, int leftOperand, @@ -191,8 +192,8 @@ public static Invoke Invoke(Type.FunctionOrMethod fun, int[] targets, * --- message to report upon failure. * @return */ - public static Invariant Invariant(Collection bytecodes) { - return new Invariant(bytecodes); + public static Invariant Invariant(int block) { + return new Invariant(block); } @@ -244,12 +245,8 @@ public static IndexOf IndexOf(Type.EffectiveArray type, int target, return new IndexOf(type, target, leftOperand, rightOperand); } - public static Loop Loop(int[] operands, Collection bytecodes) { - return new Loop(operands,bytecodes); - } - - public static Loop Loop(int[] operands, Code... bytecodes) { - return new Loop(operands,bytecodes); + public static Loop Loop(int[] modifiedOperands, int block) { + return new Loop(modifiedOperands,block); } /** @@ -374,22 +371,11 @@ public static Dereference Dereference(Type.Reference type, int target, return new Dereference(type, target, operand); } - - public static Quantify Quantify( - int startOperand, int endOperand, int indexOperand, - int[] modifiedOperands, Collection bytecodes) { - return new Quantify(startOperand, endOperand, indexOperand, - modifiedOperands, bytecodes); + public static Quantify Quantify(int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, + int block) { + return new Quantify(startOperand, endOperand, indexOperand, modifiedOperands, block); } - public static Quantify Quantify( - int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, - Code... bytecodes) { - return new Quantify(startOperand, endOperand, indexOperand, - modifiedOperands, bytecodes); - } - - public static Update Update(Type beforeType, int target, Collection operands, int operand, Type afterType, Collection fields) { @@ -542,7 +528,7 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return BinaryOperator(type(0), nTargets[0], nOperands[0], nOperands[1], kind); } @@ -606,40 +592,29 @@ public String toString() { * */ public static final class Convert extends AbstractBytecode { - public final Type result; - + private Convert(Type from, int target, int operand, Type result) { - super(from, target, operand); - if (result == null) { - throw new IllegalArgumentException( - "Convert to argument cannot be null"); - } - this.result = result; + super(new Type[]{from,result}, new int[]{target}, operand); } - public Code.Unit clone(int[] nTargets, int[] nOperands) { - return Convert(type(0), nTargets[0], nOperands[0], result); + public Code clone(int[] nTargets, int[] nOperands) { + return Convert(type(0), nTargets[0], nOperands[0], type(1)); } + public Type result() { + return type(1); + } + public int opcode() { return OPCODE_convert; } - public int hashCode() { - return result.hashCode() + super.hashCode(); - } - public boolean equals(Object o) { - if (o instanceof Convert) { - Convert c = (Convert) o; - return super.equals(c) && result.equals(c.result); - } - return false; + return o instanceof Convert && super.equals(o); } public String toString() { - return "convert %" + target(0) + " = %" + operand(0) + " " + result - + " : " + type(0); + return "convert %" + target(0) + " = %" + operand(0) + " " + result() + " : " + type(0); } } @@ -713,7 +688,7 @@ public String toString() { } @Override - protected Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return new Const(nTargets[0],constant); } } @@ -765,7 +740,7 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return Assign(type(0), nTargets[0], nOperands[0]); } @@ -822,7 +797,7 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return Debug(nOperands[0]); } @@ -842,9 +817,9 @@ public String toString() { * @author David J. Pearce * */ - public static abstract class AssertOrAssume extends Code.Compound { - private AssertOrAssume(Collection bytecodes) { - super(bytecodes); + public static abstract class AssertOrAssume extends AbstractCompoundBytecode { + private AssertOrAssume(int block) { + super(block, new Type[0], new int[0],new int[0]); } } /** @@ -855,33 +830,25 @@ private AssertOrAssume(Collection bytecodes) { */ public static class Assert extends AssertOrAssume { - private Assert(Collection bytecodes) { - super(bytecodes); + private Assert(int block) { + super(block); } public int opcode() { - return OPCODE_assertblock; + return OPCODE_assert; } public String toString() { - return "assert"; - } - - public int hashCode() { - return bytecodes.hashCode(); + return "assert #" + block; } public boolean equals(Object o) { - if (o instanceof Assert) { - Assert f = (Assert) o; - return bytecodes.equals(f.bytecodes); - } - return false; + return o instanceof Assume && super.equals(o); } @Override - public Assert clone() { - return new Assert(bytecodes); + protected Code clone(int[] nTargets, int[] nOperands) { + return this; } } @@ -893,33 +860,25 @@ public Assert clone() { */ public static final class Assume extends AssertOrAssume { - private Assume(Collection bytecodes) { - super(bytecodes); + private Assume(int block) { + super(block); } public int opcode() { - return OPCODE_assumeblock; + return OPCODE_assume; } public String toString() { - return "assume "; + return "assume #" + block; } - public int hashCode() { - return bytecodes.hashCode(); - } - public boolean equals(Object o) { - if (o instanceof Assume) { - Assume f = (Assume) o; - return bytecodes.equals(f.bytecodes); - } - return false; + return o instanceof Assume && super.equals(o); } @Override - public Assume clone() { - return new Assume(bytecodes); + protected Code clone(int[] nTargets, int[] nOperands) { + return this; } } @@ -942,7 +901,7 @@ public int opcode() { } @Override - protected Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return this; } @@ -992,7 +951,7 @@ private FieldLoad(Type.EffectiveRecord type, int target, int operand, String fie } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return FieldLoad(type(0), nTargets[0], nOperands[0], field); } @@ -1063,12 +1022,9 @@ public String toString() { * @author David J. Pearce * */ - public static final class Goto extends AbstractBytecode { - public final String target; - + public static final class Goto extends AbstractBranchingBytecode { private Goto(String target) { - super(new Type[0],new int[0]); - this.target = target; + super(target,new Type[0],new int[0]); } public int opcode() { @@ -1076,34 +1032,26 @@ public int opcode() { } public Goto relabel(Map labels) { - String nlabel = labels.get(target); + String nlabel = labels.get(destination()); if (nlabel == null) { return this; } else { return Goto(nlabel); } } - - public int hashCode() { - return target.hashCode(); - } public boolean equals(Object o) { - if (o instanceof Goto) { - return target.equals(((Goto) o).target); - } - return false; + return o instanceof Goto && super.equals(o); } @Override - protected Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return this; } public String toString() { - return "goto " + target; - } - + return "goto " + destination(); + } } /** @@ -1157,27 +1105,17 @@ public String toString() { * @author David J. Pearce * */ - public static final class If extends AbstractBytecode { - public final String target; + public static final class If extends AbstractBranchingBytecode { public final Comparator op; private If(Type type, int leftOperand, int rightOperand, Comparator op, String target) { - super(new Type[]{type}, new int[0], leftOperand, rightOperand); - if (op == null) { - throw new IllegalArgumentException( - "IfGoto op argument cannot be null"); - } - if (target == null) { - throw new IllegalArgumentException( - "IfGoto target argument cannot be null"); - } + super(target,new Type[]{type}, new int[0], leftOperand, rightOperand); this.op = op; - this.target = target; } public If relabel(Map labels) { - String nlabel = labels.get(target); + String nlabel = labels.get(destination()); if (nlabel == null) { return this; } else { @@ -1190,29 +1128,24 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { - return If(types[0], nOperands[0], nOperands[1], op, target); + public Code clone(int[] nTargets, int[] nOperands) { + return If(types[0], nOperands[0], nOperands[1], op, destination()); } public int hashCode() { - return super.hashCode() + op.hashCode() + target.hashCode(); + return super.hashCode() + op.hashCode(); } public boolean equals(Object o) { if (o instanceof If) { If ig = (If) o; - return op == ig.op && target.equals(ig.target) - && super.equals(ig); + return op == ig.op && super.equals(ig); } return false; } - public String codeString() { - return null; - } - public String toString() { - return "if" + op + " %" + operands[0] + ", %" + operands[1] + " goto " + target + " : " + types[0]; + return "if" + op + " %" + operands[0] + ", %" + operands[1] + " goto " + destination() + " : " + types[0]; } } @@ -1303,23 +1236,9 @@ private Comparator(int offset) { * @author David J. Pearce * */ - public static final class IfIs extends AbstractBytecode { - public final String target; - public final Type rightOperand; - - private IfIs(Type type, int leftOperand, Type rightOperand, - String target) { - super(new Type[]{type}, new int[0], leftOperand); - if (rightOperand == null) { - throw new IllegalArgumentException( - "IfIs test argument cannot be null"); - } - if (target == null) { - throw new IllegalArgumentException( - "IfIs target argument cannot be null"); - } - this.target = target; - this.rightOperand = rightOperand; + public static final class IfIs extends AbstractBranchingBytecode { + private IfIs(Type type, int leftOperand, Type rightOperand, String target) { + super(target, new Type[] { type, rightOperand }, new int[0], leftOperand); } public int opcode() { @@ -1327,30 +1246,25 @@ public int opcode() { } public IfIs relabel(Map labels) { - String nlabel = labels.get(target); + String nlabel = labels.get(destination()); if (nlabel == null) { return this; } else { - return IfIs(types[0], operands[0], rightOperand, nlabel); + return IfIs(types[0], operands[0], types[1], nlabel); } } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { - return IfIs(types[0], nOperands[0], rightOperand, target); + public Code clone(int[] nTargets, int[] nOperands) { + return IfIs(types[0], nOperands[0], types[1], destination()); } - + public boolean equals(Object o) { - if (o instanceof IfIs) { - IfIs ig = (IfIs) o; - return super.equals(o) && rightOperand.equals(ig.rightOperand) - && target.equals(ig.target); - } - return false; + return o instanceof IfIs && super.equals(o); } public String toString() { - return "ifis" + " %" + operands[0] + ", " + rightOperand + " goto " + target + " : " + types[0]; + return "ifis" + " %" + operands[0] + ", " + types[1] + " goto " + destination() + " : " + types[0]; } } @@ -1369,8 +1283,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class IndirectInvoke extends - AbstractBytecode { + public static final class IndirectInvoke extends AbstractBytecode { /** * Construct an indirect invocation bytecode which assigns to an @@ -1417,15 +1330,11 @@ public int[] parameters() { } public int opcode() { - if (type(0) instanceof Type.Function) { - return OPCODE_indirectinvokefn; - } else { - return OPCODE_indirectinvokemd; - } + return OPCODE_indirectinvoke; } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return IndirectInvoke(type(0), nTargets, nOperands[0], Arrays.copyOfRange(nOperands, 1, nOperands.length)); } @@ -1448,12 +1357,12 @@ public String toString() { */ public static class Invariant extends Assert { - private Invariant(Collection bytecodes) { - super(bytecodes); + private Invariant(int block) { + super(block); } public int opcode() { - return OPCODE_invariantblock; + return OPCODE_invariant; } public String toString() { @@ -1461,20 +1370,20 @@ public String toString() { } public int hashCode() { - return bytecodes.hashCode(); + return block; } public boolean equals(Object o) { if (o instanceof Invariant) { Invariant f = (Invariant) o; - return bytecodes.equals(f.bytecodes); + return block == f.block; } return false; } @Override public Invariant clone() { - return new Invariant(bytecodes); + return this; } } @@ -1513,7 +1422,7 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return Not(nTargets[0], nOperands[0]); } @@ -1574,8 +1483,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class Invoke extends - AbstractBytecode { + public static final class Invoke extends AbstractBytecode { public final NameID name; private Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, @@ -1585,11 +1493,7 @@ private Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, } public int opcode() { - if (type(0) instanceof Type.Function) { - return OPCODE_invokefn; - } else { - return OPCODE_invokemd; - } + return OPCODE_invoke; } public int hashCode() { @@ -1597,7 +1501,7 @@ public int hashCode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return Invoke(type(0), nTargets, nOperands, name); } @@ -1615,8 +1519,7 @@ public String toString() { } } - public static final class Lambda extends - AbstractBytecode { + public static final class Lambda extends AbstractBytecode { public final NameID name; private Lambda(Type.FunctionOrMethod type, int target, int[] operands, @@ -1626,11 +1529,7 @@ private Lambda(Type.FunctionOrMethod type, int target, int[] operands, } public int opcode() { - if (type(0) instanceof Type.Function) { - return OPCODE_lambdafn; - } else { - return OPCODE_lambdamd; - } + return OPCODE_lambda; } public int hashCode() { @@ -1638,7 +1537,7 @@ public int hashCode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return Lambda(type(0), nTargets[0], nOperands, name); } @@ -1662,7 +1561,7 @@ public String toString() { * @author David J. Pearce * */ - public static class Label extends Code.Unit { + public static class Label implements Code { public final String label; private Label(String label) { @@ -1683,7 +1582,7 @@ public Label relabel(Map labels) { } @Override - public Code.Unit remap(Map binding) { + public Code remap(Map binding) { return this; } @@ -1701,6 +1600,11 @@ public boolean equals(Object o) { public String toString() { return "." + label; } + + @Override + public void registers(Set register) { + // TODO Auto-generated method stub + } } /** @@ -1742,7 +1646,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return ArrayGenerator(type(0), nTargets[0], nOperands[0],nOperands[1]); } @@ -1791,7 +1695,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return LengthOf(type(0), nTargets[0], nOperands[0]); } @@ -1847,7 +1751,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return IndexOf(type(0), nTargets[0], nOperands[0], nOperands[1]); } @@ -1907,7 +1811,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return Move(type(0), nTargets[0], nOperands[0]); } @@ -1964,156 +1868,67 @@ public String toString() { * @author David J. Pearce * */ - public static class Loop extends Code.Compound { - public final int[] modifiedOperands; - - private Loop(int[] modifies, Code... codes) { - super(codes); - this.modifiedOperands = modifies; - } + public static class Loop extends AbstractCompoundBytecode { - private Loop(int[] modifies, Collection codes) { - super(codes); - this.modifiedOperands = modifies; + private Loop(int[] targets, int block, int... operands) { + super(block, new Type[0], targets, operands); } public int opcode() { return OPCODE_loop; } - @Override - public Code.Compound remap(Map binding) { - int[] nOperands = remapOperands(binding, modifiedOperands); - ArrayList bytecodes = this.bytecodes; - - for (int i = 0; i != bytecodes.size(); ++i) { - Code code = bytecodes.get(i); - Code nCode = code.remap(binding); - if (code != nCode) { - if (bytecodes == this.bytecodes) { - bytecodes = new ArrayList(bytecodes); - } - bytecodes.set(i, nCode); - } - } - - if (nOperands != modifiedOperands || bytecodes != this.bytecodes) { - return Loop(nOperands, bytecodes); - } else { - return this; - } - } - - public int hashCode() { - return bytecodes.hashCode() + Arrays.hashCode(modifiedOperands); - } - public boolean equals(Object o) { if (o instanceof Loop) { Loop f = (Loop) o; - return bytecodes.equals(f.bytecodes) - && Arrays.equals(modifiedOperands, f.modifiedOperands); + return block == f.block && super.equals(f); } return false; } @Override - public Loop clone() { - return new Loop(modifiedOperands,bytecodes); + public Loop clone(int[] nTargets, int[] nOperands) { + return new Loop(nTargets, block, nOperands); } public String toString() { - return "loop " + arrayToString(modifiedOperands); + return "loop " + arrayToString(targets()) + " = " + block; } } public static final class Quantify extends Loop { - public final int startOperand; - public final int endOperand; - public final int indexOperand; - private Quantify(int startOperand,int endOperand, - int indexOperand, int[] modifies, Collection bytecodes) { - super(modifies, bytecodes); - this.startOperand = startOperand; - this.endOperand = endOperand; - this.indexOperand = indexOperand; + int indexOperand, int[] targets, int block) { + super(targets, block, startOperand, endOperand, indexOperand); } - private Quantify(int startOperand, int endOperand, int indexOperand, - int[] modifies, Code[] bytecodes) { - super(modifies, bytecodes); - this.startOperand = startOperand; - this.endOperand = endOperand; - this.indexOperand = indexOperand; - } - public int opcode() { return OPCODE_quantify; } - @Override - public void registers(java.util.Set registers) { - registers.add(indexOperand); - registers.add(startOperand); - registers.add(endOperand); - super.registers(registers); + public int startOperand() { + return operands[0]; } - - @Override - public Code.Compound remap(Map binding) { - int[] nModifiedOperands = remapOperands(binding, modifiedOperands); - ArrayList bytecodes = this.bytecodes; - - for (int i = 0; i != bytecodes.size(); ++i) { - Code code = bytecodes.get(i); - Code nCode = code.remap(binding); - if (code != nCode) { - if (bytecodes == this.bytecodes) { - bytecodes = new ArrayList(bytecodes); - } - bytecodes.set(i, nCode); - } - } - Integer nIndexOperand = binding.get(indexOperand); - Integer nStartOperand = binding.get(startOperand); - Integer nEndOperand = binding.get(endOperand); - if (nStartOperand != null || nEndOperand != null || nIndexOperand != null - || nModifiedOperands != modifiedOperands || bytecodes != this.bytecodes) { - nStartOperand = nStartOperand != null ? nStartOperand - : startOperand; - nEndOperand = nEndOperand != null ? nEndOperand - : endOperand; - nIndexOperand = nIndexOperand != null ? nIndexOperand - : indexOperand; - return Quantify(nStartOperand, nEndOperand, nIndexOperand, - nModifiedOperands, bytecodes); - } else { - return this; - } + + public int endOperand() { + return operands[1]; } - public int hashCode() { - return super.hashCode() + startOperand + endOperand + indexOperand - + Arrays.hashCode(modifiedOperands); + public int indexOperand() { + return operands[2]; } - + public boolean equals(Object o) { if (o instanceof Quantify) { Quantify f = (Quantify) o; - return startOperand == f.startOperand - && endOperand == f.endOperand - && indexOperand == f.indexOperand - && Arrays.equals(modifiedOperands, f.modifiedOperands) - && bytecodes.equals(f.bytecodes); + return super.equals(f); } return false; } public String toString() { - return "quantify %" + indexOperand + " in %" + startOperand + "..%" - + endOperand + arrayToString(modifiedOperands); + return "quantify " + arrayToString(targets()) + " = #" + block() + arrayToString(operands()); } } @@ -2250,7 +2065,6 @@ public void remove() { */ public static final class Update extends AbstractBytecode implements Iterable { - public final Type afterType; public final ArrayList fields; /** @@ -2273,24 +2087,21 @@ public static final class Update extends AbstractBytecode */ private Update(Type beforeType, int target, int[] operands, int operand, Type afterType, Collection fields) { - super(beforeType, target, append(operands,operand)); + super(new Type[]{beforeType,afterType}, new int[]{target}, append(operands,operand)); if (fields == null) { throw new IllegalArgumentException( "FieldStore fields argument cannot be null"); } - this.afterType = afterType; this.fields = new ArrayList(fields); } // Helper used for clone() - private Update(Type beforeType, int target, int[] operands, - Type afterType, Collection fields) { - super(beforeType, target, operands); + private Update(Type[] types, int[] targets, int[] operands, Collection fields) { + super(types,targets,operands); if (fields == null) { throw new IllegalArgumentException( "FieldStore fields argument cannot be null"); } - this.afterType = afterType; this.fields = new ArrayList(fields); } @@ -2337,16 +2148,20 @@ public int level() { } public Iterator iterator() { - return new UpdateIterator(afterType, level(), keys(), fields); + return new UpdateIterator(afterType(), level(), keys(), fields); } + public Type afterType() { + return types[1]; + } + /** * Extract the type for the right-hand side of this assignment. * * @return */ public Type rhs() { - Type iter = afterType; + Type iter = afterType(); int fieldIndex = 0; for (int i = 0; i != level(); ++i) { @@ -2369,17 +2184,14 @@ public Type rhs() { } @Override - public final Code.Unit clone(int[] nTargets, int[] nOperands) { - return Update(type(0), nTargets[0], - Arrays.copyOf(nOperands, nOperands.length - 1), - nOperands[nOperands.length - 1], afterType, fields); + public final Code clone(int[] nTargets, int[] nOperands) { + return new Update(types, nTargets, nOperands, fields); } public boolean equals(Object o) { if (o instanceof Update) { Update i = (Update) o; - return super.equals(o) && afterType.equals(i.afterType) - && fields.equals(i.fields); + return super.equals(o) && fields.equals(i.fields); } return false; } @@ -2398,7 +2210,7 @@ public String toString() { r = "(*" + r + ")"; } } - return "update " + r + " = %" + result() + " : " + type(0) + " -> " + afterType; + return "update " + r + " = %" + result() + " : " + type(0) + " -> " + afterType(); } } @@ -2437,7 +2249,7 @@ private NewRecord(Type.Record type, int target, int[] operands) { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return NewRecord(type(0), nTargets[0], nOperands); } @@ -2497,7 +2309,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return NewArray(type(0), nTargets[0], nOperands); } @@ -2531,7 +2343,7 @@ public int opcode() { } @Override - protected Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return this; } @@ -2584,7 +2396,7 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return new Return(Arrays.copyOf(types, types.length), nOperands); } @@ -2710,7 +2522,7 @@ public String toString() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return new Switch(types[0], nOperands[0], defaultTarget, branches); } @@ -2753,7 +2565,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return Invert(type(0), nTargets[0], nOperands[0]); } @@ -2798,8 +2610,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class NewObject extends - AbstractBytecode { + public static final class NewObject extends AbstractBytecode { private NewObject(Type.Reference type, int target, int operand) { super(type, target, operand); @@ -2810,7 +2621,7 @@ public int opcode() { return OPCODE_newobject; } - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return NewObject(type(0), nTargets[0], nOperands[0]); } @@ -2833,8 +2644,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class Dereference extends - AbstractBytecode { + public static final class Dereference extends AbstractBytecode { private Dereference(Type.Reference type, int target, int operand) { super(type, target, operand); @@ -2845,7 +2655,7 @@ public int opcode() { return OPCODE_dereference; } - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return Dereference(type(0), nTargets[0], nOperands[0]); } @@ -2918,7 +2728,7 @@ public int opcode() { } @Override - public Code.Unit clone(int[] nTargets, int[] nOperands) { + public Code clone(int[] nTargets, int[] nOperands) { return UnaryOperator(type(0), nTargets[0], nOperands[0], kind); } @@ -2959,7 +2769,7 @@ public int opcode() { } @Override - protected Code.Unit clone(int[] nTargets, int[] nOperands) { + protected Code clone(int[] nTargets, int[] nOperands) { return Void(type(0), nOperands); } diff --git a/modules/wyil/src/wyil/lang/WyilFile.java b/modules/wyil/src/wyil/lang/WyilFile.java index c434844ee9..bd40ddd5e3 100755 --- a/modules/wyil/src/wyil/lang/WyilFile.java +++ b/modules/wyil/src/wyil/lang/WyilFile.java @@ -35,7 +35,6 @@ import wyfs.lang.Content; import wyfs.lang.Path; import wyil.io.*; -import wyil.util.AttributedCodeBlock; /** *

@@ -452,17 +451,16 @@ public boolean hasModifier(Modifier modifier) { */ public static final class Type extends Declaration { private wyil.lang.Type type; - private AttributedCodeBlock invariant; + private CodeForest invariant; - public Type(Collection modifiers, String name, wyil.lang.Type type, - AttributedCodeBlock invariant, Attribute... attributes) { - super(name,modifiers,attributes); + public Type(Collection modifiers, String name, wyil.lang.Type type, CodeForest invariant, + Attribute... attributes) { + super(name, modifiers, attributes); this.type = type; this.invariant = invariant; } - public Type(Collection modifiers, String name, - wyil.lang.Type type, AttributedCodeBlock invariant, + public Type(Collection modifiers, String name, wyil.lang.Type type, CodeForest invariant, Collection attributes) { super(name, modifiers, attributes); this.type = type; @@ -473,7 +471,7 @@ public wyil.lang.Type type() { return type; } - public AttributedCodeBlock invariant() { + public CodeForest invariant() { return invariant; } } @@ -511,32 +509,26 @@ public wyil.lang.Constant constant() { public static final class FunctionOrMethod extends Declaration { private wyil.lang.Type.FunctionOrMethod type; - private final ArrayList precondition; - private final ArrayList postcondition; - private final AttributedCodeBlock body; + private int numPreconditions; + private int numPostconditions; + private final CodeForest forest; - public FunctionOrMethod(Collection modifiers, String name, - wyil.lang.Type.FunctionOrMethod type, AttributedCodeBlock body, - List precondition, - List postcondition, - Attribute... attributes) { + public FunctionOrMethod(Collection modifiers, String name, wyil.lang.Type.FunctionOrMethod type, + CodeForest forest, int numPreconditions, int numPostconditions, Attribute... attributes) { super(name, modifiers, attributes); this.type = type; - this.body = body; - this.precondition = new ArrayList(precondition); - this.postcondition = new ArrayList(postcondition); + this.forest = forest; + this.numPreconditions = numPreconditions; + this.numPostconditions = numPostconditions; } - public FunctionOrMethod(Collection modifiers, String name, - wyil.lang.Type.FunctionOrMethod type, AttributedCodeBlock body, - List precondition, - List postcondition, - Collection attributes) { + public FunctionOrMethod(Collection modifiers, String name, wyil.lang.Type.FunctionOrMethod type, + CodeForest forest, int numPreconditions, int numPostconditions, Collection attributes) { super(name, modifiers, attributes); this.type = type; - this.body = body; - this.precondition = new ArrayList(precondition); - this.postcondition = new ArrayList(postcondition); + this.forest = forest; + this.numPreconditions = numPreconditions; + this.numPostconditions = numPostconditions; } public wyil.lang.Type.FunctionOrMethod type() { @@ -551,16 +543,51 @@ public boolean isMethod() { return type instanceof wyil.lang.Type.Method; } - public AttributedCodeBlock body() { - return body; + public CodeForest code() { + return forest; } - - public List precondition() { - return precondition; + + /** + * Get the list of blocks within the code forest that represent the + * preconditions of this function/method. + * + * @return + */ + public int[] preconditions() { + int[] ids = new int[numPreconditions]; + for(int i=0;i!=numPreconditions;++i) { + ids[i] = forest.getRoot(i); + } + return ids; + } + + /** + * Get the list of blocks within the code forest that represent the + * postconditions of this function/method. + * + * @return + */ + public int[] postconditions() { + int[] ids = new int[numPostconditions]; + for(int i=0;i!=numPostconditions;++i) { + ids[i] = forest.getRoot(i+numPreconditions); + } + return ids; } - - public List postcondition() { - return postcondition; + + /** + * Get the block corresponding to the body of this function, or null if + * no such body exists. + * + * @return + */ + public Integer body() { + int r = numPreconditions + numPostconditions; + if(r == forest.numRoots()) { + return null; + } else { + return forest.getRoot(r); + } } } } diff --git a/modules/wyil/src/wyil/transforms/ConstantPropagation.java b/modules/wyil/src/wyil/transforms/ConstantPropagation.java deleted file mode 100755 index 9aa8daa238..0000000000 --- a/modules/wyil/src/wyil/transforms/ConstantPropagation.java +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package wyil.transforms; - -import static wyil.util.ErrorMessages.internalFailure; - -import java.math.BigInteger; -import java.util.*; - -import wybs.lang.Builder; -import wycc.lang.SyntaxError; -import wycc.lang.Transform; -import wycc.util.Pair; -import wyautl.util.BigRational; -import wyfs.lang.Path; -import wyil.lang.*; -import wyil.util.*; -import wyil.util.dfa.ForwardFlowAnalysis; - -/** - *

- * Propagates constants throughout the bytecodes making up the functions, - * methods and invariants of a WyIL file. This involves rewriting bytecodes - * where appropriate, and possibly even removing them if they become redundant. - * Consider the following simple bytecode sequence: - *

- * - *
- * const %0 = 1
- * const %1 = 2
- * add %2 = %0, %2
- * assign %3 = %2
- * 
- * - *

- * In this example, the constant propagation analysis will track the fact that - * registers %0 and %1 hold constants. As such, it - * will determine that register %2 holds a constant (3 - * in this case). Hence, it will conclude that register %3 holds a - * constant. Therefore, it will rewrite the above bytecodes into the following: - *

- * - *
- * const %0 = 1
- * const %1 = 2
- * const %2 = 3
- * const %3 = 3
- * 
- * - *

- * Note that it doesn't eliminate the assignments to registers %0 - - * %2 (even if these are no longer live). The reason for this is - * simply that the constant propagation phase does not have access to liveness - * information. Such redundant bytecodes will be eliminated by later phases. - *

- * - * - * - * @author David J. Pearce - * - */ -public class ConstantPropagation extends ForwardFlowAnalysis implements Transform { - private static final HashMap rewrites = new HashMap(); - - /** - * Determines whether constant propagation is enabled or not. - */ - private boolean enabled = getEnable(); - - public ConstantPropagation(Builder builder) { - - } - - @Override - public void apply(WyilFile module) { - if(enabled) { - super.apply(module); - } - } - - public static String describeEnable() { - return "Enable/disable constant propagation"; - } - - public static boolean getEnable() { - return true; // default value - } - - public void setEnable(boolean flag) { - this.enabled = flag; - } - - @Override - public WyilFile.Type propagate(WyilFile.Type type) { - AttributedCodeBlock invariant = type.invariant(); - if (invariant != null) { - invariant = propagate(invariant); - return new WyilFile.Type(type.modifiers(), type.name(), - type.type(), invariant, type.attributes()); - } - return type; - } - - public Env initialStore() { - Env environment = new Env(); - int nvars = rootBlock.numSlots(); - - for (int i=0; i != nvars; ++i) { - environment.add(null); - } - - return environment; - } - - @Override - public WyilFile.FunctionOrMethod propagate(WyilFile.FunctionOrMethod method) { - ArrayList requires = new ArrayList<>( - method.precondition()); - for (int i = 0; i != requires.size(); ++i) { - AttributedCodeBlock tmp = propagate(requires.get(i)); - requires.set(i, tmp); - } - ArrayList ensures = new ArrayList<>( - method.postcondition()); - for (int i = 0; i != ensures.size(); ++i) { - AttributedCodeBlock tmp = propagate(ensures.get(i)); - ensures.set(i, tmp); - } - - AttributedCodeBlock body = method.body(); - if (body != null) { - body = propagate(body); - } - - return new WyilFile.FunctionOrMethod(method.modifiers(), method.name(), - method.type(), body, requires, ensures, method.attributes()); - } - - public AttributedCodeBlock propagate(AttributedCodeBlock body) { - rootBlock = body; - stores = new HashMap(); - rewrites.clear(); - - // TODO: propagate constants through pre- and post-conditions. - - Env environment = initialStore(); - propagate(null, body, environment); - - // At this point, we apply the inserts - AttributedCodeBlock nbody = new AttributedCodeBlock(); - for(int i=0;i!=body.size();++i) { - Rewrite rewrite = rewrites.get(i); - if(rewrite != null) { - nbody.add(rewrite.code); - } else { - nbody.add(body.get(i)); - } - } - - return nbody; - } - - @Override - public Env propagate(CodeBlock.Index index, Code code, Env environment) { - - // reset the rewrites for this code, in case it changes - rewrites.remove(index); - - environment = (Env) environment.clone(); - - if(code instanceof Codes.BinaryOperator) { - infer(index,(Codes.BinaryOperator)code,environment); - } else if(code instanceof Codes.Convert) { - infer(index,(Codes.Convert)code,environment); - } else if(code instanceof Codes.Const) { - infer(index, (Codes.Const)code,environment); - } else if(code instanceof Codes.Debug) { - infer(index, (Codes.Debug)code,environment); - } else if(code instanceof Codes.AssertOrAssume) { - infer(index, (Codes.AssertOrAssume)code,environment); - } else if(code instanceof Codes.Fail) { - infer(index,(Codes.Fail)code,environment); - } else if(code instanceof Codes.FieldLoad) { - infer(index,(Codes.FieldLoad)code,environment); - } else if(code instanceof Codes.IndirectInvoke) { - infer(index, (Codes.IndirectInvoke)code,environment); - } else if(code instanceof Codes.Invoke) { - infer(index, (Codes.Invoke)code,environment); - } else if(code instanceof Codes.Invert) { - infer(index,(Codes.Invert)code,environment); - } else if(code instanceof Codes.Lambda) { - // skip - } else if(code instanceof Codes.Label) { - // skip - } else if(code instanceof Codes.LengthOf) { - infer(index,(Codes.LengthOf)code,environment); - } else if(code instanceof Codes.IndexOf) { - infer(index,(Codes.IndexOf)code,environment); - } else if(code instanceof Codes.Assign) { - infer(index,(Codes.Assign)code,environment); - } else if(code instanceof Codes.Update) { - infer(index, (Codes.Update)code,environment); - } else if(code instanceof Codes.NewArray) { - infer(index,(Codes.NewArray)code,environment); - } else if(code instanceof Codes.NewRecord) { - infer(index,(Codes.NewRecord)code,environment); - } else if(code instanceof Codes.UnaryOperator) { - infer(index,(Codes.UnaryOperator)code,environment); - } else if(code instanceof Codes.Dereference) { - infer(index,(Codes.Dereference)code,environment); - } else if(code instanceof Codes.Return) { - infer(index, (Codes.Return)code,environment); - } else if(code instanceof Codes.Nop) { - // skip - } else if(code instanceof Codes.NewObject) { - infer(index,(Codes.NewObject)code,environment); - } else { - throw new SyntaxError.InternalFailure("unknown: " + code.getClass().getName(),filename,0,-1); - } - - return environment; - } - - public void infer(CodeBlock.Index index, Codes.AssertOrAssume code, Env environment) { - } - - public void infer(CodeBlock.Index index, Codes.BinaryOperator code, Env environment) { - Constant lhs = environment.get(code.operand(0)); - Constant rhs = environment.get(code.operand(1)); - Constant result = null; - - if (lhs instanceof Constant.Integer - && rhs instanceof Constant.Integer) { - Constant.Integer lnum = (Constant.Integer) lhs; - Constant.Integer rnum = (Constant.Integer) rhs; - - switch (code.kind) { - case ADD: { - result = lnum.add(rnum); - break; - } - case SUB: { - result = lnum.subtract(rnum); - break; - } - case MUL: { - result = lnum.multiply(rnum); - break; - } - case DIV: { - result = lnum.divide(rnum); - break; - } - case REM: { - result = lnum.remainder(rnum); - break; - } - } - } - - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.Convert code, - Env environment) { - // TODO: implement this - Constant val = environment.get(code.operand(0)); - - invalidate(code.target(0),environment); - } - - public void infer(CodeBlock.Index index, Codes.Const code, - Env environment) { - invalidate(code.target(),environment); - } - - public void infer(CodeBlock.Index index, Codes.Debug code, Env environment) { - } - - public void infer(CodeBlock.Index index, Codes.Fail code, - Env environment) { - } - - public void infer(CodeBlock.Index index, Codes.FieldLoad code, - Env environment) { - Constant src = environment.get(code.operand(0)); - - Constant result = null; - if (src instanceof Constant.Record) { - Constant.Record rec = (Constant.Record) src; - result = rec.values.get(code.field); - } - - assign(code.target(0),result,environment,index); - } - - public void infer(CodeBlock.Index index, Codes.IndirectInvoke code, - Env environment) { - - // TODO: in principle we can do better here in the case that the target - // is a constant. This seems pretty unlikely though ... - - for(int target : code.targets()) { - invalidate(target,environment); - } - } - - public void infer(CodeBlock.Index index, Codes.Invoke code, - Env environment) { - - for(int target : code.targets()) { - invalidate(target,environment); - } - } - - public void infer(CodeBlock.Index index, Codes.Lambda code, Env environment) { - // For now, don't do anything! - assign(code.target(0), null, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.LengthOf code, Env environment) { - Constant val = environment.get(code.operand(0)); - Constant.Array list = (Constant.Array) val; - Constant result = Constant.V_INTEGER(BigInteger.valueOf(list.values.size())); - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.IndexOf code, Env environment) { - Constant src = environment.get(code.operand(0)); - Constant idx = environment.get(code.operand(1)); - Constant result = null; - if (idx instanceof Constant.Integer && src instanceof Constant.Array) { - Constant.Integer num = (Constant.Integer) idx; - Constant.Array list = (Constant.Array) src; - int i = num.value.intValue(); - if (i >= 0 && i < list.values.size()) { - result = list.values.get(i); - } - } - - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.Assign code, - Env environment) { - Constant result = environment.get(code.operand(0)); - assign(code.target(0),result,environment,index); - } - - public void infer(CodeBlock.Index index, Codes.Update code, Env environment) { - // TODO: implement this! - invalidate(code.target(0), environment); - } - - public void infer(CodeBlock.Index index, Codes.NewRecord code, Env environment) { - HashMap values = new HashMap(); - ArrayList keys = new ArrayList(code.type(0).keys()); - Collections.sort(keys); - boolean isValue = true; - int[] code_operands = code.operands(); - for (int i = 0; i != code_operands.length; ++i) { - Constant val = environment.get(code_operands[i]); - if (isRealConstant(val)) { - values.put(keys.get(i), val); - } else { - isValue = false; - } - } - - Constant result = null; - if (isValue) { - result = Constant.V_RECORD(values); - } - - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.NewArray code, Env environment) { - ArrayList values = new ArrayList(); - - boolean isValue = true; - int[] code_operands = code.operands(); - for (int i = 0; i != code_operands.length; ++i) { - Constant val = environment.get(code_operands[i]); - if (isRealConstant(val)) { - values.add(val); - } else { - isValue = false; - } - } - - Constant result = null; - if (isValue) { - result = Constant.V_ARRAY(values); - } - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.Return code, Env environment) { - } - - public void infer(CodeBlock.Index index, Codes.Invert code, Env environment) { - Constant val = environment.get(code.operand(0)); - Constant result = null; - - if (val instanceof Constant.Byte) { - Constant.Byte num = (Constant.Byte) val; - result = Constant.V_BYTE((byte) ~num.value); - } - - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.UnaryOperator code, Env environment) { - // needs to be updated to support numerator and denominator - Constant val = environment.get(code.operand(0)); - Constant result = null; - - switch (code.kind) { - case NEG: - if (val instanceof Constant.Integer) { - Constant.Integer num = (Constant.Integer) val; - result = Constant.V_INTEGER(num.value.negate()); - } - } - - assign(code.target(0), result, environment, index); - } - - public void infer(CodeBlock.Index index, Codes.NewObject code, - Env environment) { - invalidate(code.target(0), environment); - } - - public void infer(CodeBlock.Index index, Codes.Dereference code, Env environment) { - invalidate(code.target(0), environment); - } - - @Override - public Pair propagate(CodeBlock.Index index, Codes.If code, Env environment) { - environment = (Env) environment.clone(); - return new Pair<>(environment, environment); - } - - @Override - public Pair propagate(CodeBlock.Index index, Codes.IfIs code, Env environment) { - environment = (Env) environment.clone(); - return new Pair<>(environment, environment); - } - - @Override - public List propagate(CodeBlock.Index index, Codes.Switch code, Env environment) { - environment = (Env) environment.clone(); - - ArrayList stores = new ArrayList<>(); - for (int i = 0; i != code.branches.size(); ++i) { - stores.add(environment); - } - - return stores; - } - - @Override - public Env propagate(CodeBlock.Index index, Codes.Loop loop, Env environment) { - - environment = new Env(environment); - - if(loop instanceof Codes.Quantify) { - Codes.Quantify fall = (Codes.Quantify) loop; - - // TO DO: could unroll loop if src collection is a value. - invalidate(fall.indexOperand,environment); - } - - // Now, kill every variable which is modified in the loop. This is a - // safety precaution, and it's possible we could do better here in some - // circumstances (e.g. by unrolling the loop). - - for(int modifiedRegister : loop.modifiedOperands) { - invalidate(modifiedRegister,environment); - } - - Env oldEnv = null; - Env newEnv = environment; - CodeBlock block = (CodeBlock) loop; - - do { - // iterate until a fixed point reached - oldEnv = newEnv; - newEnv = propagate(index, block, oldEnv); - newEnv = join(environment, newEnv); - } while (!newEnv.equals(oldEnv)); - - return newEnv; - } - - /** - * Invalidate the record for a given register in the environment. - * Specifically, if that register was considered to hold a constant - * beforehand, it no longer will be. - * - * @param register - * Register being invalidated - * @param environment - * Environment in which it is to be invalidated - */ - public void invalidate(int register, Env environment) { - environment.set(register, null); - } - - /** - * Update the environment to record an assignment of a constant to a given - * register. - * - * @param register - * Register being assigned - * @param constant - * Constant being assigned. This may be null, in which case it - * means the register is assigned something which isn't a - * constant - * @param environment - * Environment recording which variables are constants - * @param index - * Index of bytecode in question from root Block - */ - public void assign(int register, Constant constant, Env environment, - CodeBlock.Index index) { - environment.set(register, constant); - - if (isRealConstant(constant)) { - Code code = Codes.Const(register, constant); - rewrites.put(index, new Rewrite(code)); - } - } - - public static boolean isRealConstant(Constant c) { - return c != null && !(c instanceof Alias); - } - - public Env join(Env env1, Env env2) { - if (env2 == null) { - return env1; - } else if (env1 == null) { - return env2; - } - Env env = new Env(); - for (int i = 0; i != Math.min(env1.size(), env2.size()); ++i) { - Constant mt = env1.get(i); - Constant ot = env2.get(i); - if (ot instanceof Constant && mt instanceof Constant && ot.equals(mt)) { - env.add(mt); - } else { - env.add(null); - } - } - return env; - } - - public final static class Env extends ArrayList { - public Env() { - } - public Env(Collection v) { - super(v); - } - public Env clone() { - return new Env(this); - } - } - - private static class Rewrite { - public final Code code; - - public Rewrite(Code rewrite) { - this.code = rewrite; - } - } - - private static class Alias extends Constant { - public final int reg; - - public Alias(int reg) { - this.reg = reg; - } - - public int hashCode() { - return reg; - } - - public boolean equals(Object o) { - if(o instanceof Alias) { - Alias a = (Alias) o; - return reg == a.reg; - } - return false; - } - - public wyil.lang.Type type() { - return wyil.lang.Type.T_ANY; - } - - public int compareTo(Constant c) { - if(c instanceof Alias) { - Alias a = (Alias) c; - if(reg < a.reg) { - return -1; - } else if(reg == a.reg) { - return 0; - } - } - return 1; - } - } -} diff --git a/modules/wyil/src/wyil/transforms/DeadCodeElimination.java b/modules/wyil/src/wyil/transforms/DeadCodeElimination.java deleted file mode 100644 index fa74a350f4..0000000000 --- a/modules/wyil/src/wyil/transforms/DeadCodeElimination.java +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package wyil.transforms; - -import java.io.IOException; -import java.util.*; - -import wybs.lang.Builder; -import wycc.lang.Transform; -import wycc.util.Pair; -import wyil.lang.*; -import wyil.util.AttributedCodeBlock; - -/** - *

- * Removes dead-code from method and function bodies, preconditions, - * postconditions and type invariants. Dead-code is defined as code which is - * unreachable through the corresponding control-flow graph starting from the - * entry point. For example, consider: - *

- * - *
- *   const %1 = 0                 
- *   ifge %0, %1 goto label0           
- *   neg %2 = %0                       
- *   assign %0 = %2
- *   goto label0
- *   const %1 = 0   // unreachable            
- * .label0                                 
- *   return %0                         
- *   return         // unreachable
- * 
- *

- * Here, we can see two bytecodes which are unreachable by any path through the - * control-flow graph starting from the entry point. Both of these, therefore, - * are considered dead-code and can safely be removed. - *

- *

- * The algorithm traverses a given code block using (roughly speaking) a - * depth-first search of the corresponding control-flow graph. Bytecodes which - * are not visited during this traversal are guaranteed to be unreachable and, - * hence, are removed. Observe that, however, some unreachable bytecodes may - * remain if their unreachable status depends on more than just reachability - * (i.e. an unrealisable path). - *

- * - * @author David J. Pearce - * - */ -public class DeadCodeElimination implements Transform { - - /** - * Determines whether constant propagation is enabled or not. - */ - private boolean enabled = getEnable(); - - - public static String describeEnable() { - return "Enable/disable constant propagation"; - } - - public static boolean getEnable() { - return true; // default value - } - - public void setEnable(boolean flag) { - this.enabled = flag; - } - - public DeadCodeElimination(Builder builder) { - - } - - /** - * Apply this transformation to all type, function and method declarations - * of a given module. - */ - public void apply(WyilFile module) throws IOException { - if(enabled) { - for(WyilFile.Type type : module.types()) { - transform(type); - } - for(WyilFile.FunctionOrMethod method : module.functionOrMethods()) { - transform(method); - } - } - } - - /** - * Transform the invariant block for a given type declaration (if one - * exists). - * - * @param type - */ - public void transform(WyilFile.Type type) { - AttributedCodeBlock invariant = type.invariant(); - - if (invariant != null) { - transform(invariant); - } - } - - /** - * Transform all code blocks found within a given function or method - * declaration. - * - * @param method - */ - public void transform(WyilFile.FunctionOrMethod method) { - - // First, process any preconditions. - for(AttributedCodeBlock precondition : method.precondition()) { - transform(precondition); - } - - // Second, process the method's body (if there is one). - AttributedCodeBlock body = method.body(); - if(body != null) { - transform(body); - } - - // Finally, process any postconditions. - for(AttributedCodeBlock postcondition : method.postcondition()) { - transform(postcondition); - } - } - - /** - * Traverse all reachable bytecodes in the given block, starting from the - * entry point. The traversal corresponds (roughly speaking) to a - * depth-first search of the control-flow graph. Bytecodes which are not - * visited during this traversal are guaranteed to be unreachable and, - * hence, are removed. Observe that, however, some unreachable bytecodes may - * remain if their unreachable status depends on more than just reachability - * (i.e. an unrealisable path). - * - * @param block - */ - private void transform(AttributedCodeBlock block) { - CodeBlock.Index entry = new CodeBlock.Index(CodeBlock.Index.ROOT); - // Construct the label map which will label labels to their bytecode - // indices. This allows is to correctly handle branching instructions. - HashMap labelMap = new HashMap(); - buildLabelMap(CodeBlock.Index.ROOT,block,labelMap); - // Initialise the worklist which will contain the set of bytecode - // addresses remaining to be explored. Initially, this is populated just - // with the root index. An invariant of the worklist is that all entries - // on the worklist have already been added to the visited set. - Stack worklist = new Stack(); - worklist.push(entry); - // Initialise the visited set, which contains the set of bytecodes which - // have been explored. When the search is completed, this will tell us - // which bytecodes are dead-code (i.e. because they weren't explored). - HashSet visited = new HashSet(); - visited.add(entry); - // Now, iterate until all branches of exploration have been explored and - // the worklist is empty. - while(!worklist.isEmpty()) { - CodeBlock.Index index = worklist.pop(); - Code code = block.get(index); - - if(code instanceof Codes.Goto) { - Codes.Goto g = (Codes.Goto) code; - addTarget(labelMap.get(g.target),visited,worklist,block); - } else if(code instanceof Codes.If) { - Codes.If ig = (Codes.If) code; - addTarget(index.next(),visited,worklist,block); - addTarget(labelMap.get(ig.target),visited,worklist,block); - } else if(code instanceof Codes.IfIs) { - Codes.IfIs ig = (Codes.IfIs) code; - addTarget(index.next(),visited,worklist,block); - addTarget(labelMap.get(ig.target),visited,worklist,block); - } else if(code instanceof Codes.Switch) { - Codes.Switch sw = (Codes.Switch) code; - for(Pair p : sw.branches) { - addTarget(labelMap.get(p.second()),visited,worklist,block); - } - addTarget(labelMap.get(sw.defaultTarget),visited,worklist,block); - } else if(code instanceof Codes.Return || code instanceof Codes.Fail) { - // Terminating bytecodes - } else if(code instanceof CodeBlock) { - // This is a block which contains other bytecodes and/or blocks. - // Therefore, we need to explore the block as well. In all - // cases, we want to also continue onto the following statement - // as well. - addTarget(index.next(),visited,worklist,block); - addTarget(index.firstWithin(),visited,worklist,block); - } else { - // Sequential bytecode, so fall through to next index (if it - // exists). - addTarget(index.next(),visited,worklist,block); - } - } - - // Now, remove all visited indices from the set of all indices within - // the block to determine which ones are dead code. - List indices = block.indices(); - indices.removeAll(visited); - // FIXME: need to actually remove unused bytecodes! - System.out.println("FIXME --- DeadCodeElimination"); - // block.removeAll(indices); - } - - private static void buildLabelMap(CodeBlock.Index parent, CodeBlock block, - HashMap map) { - for (int i = 0; i != block.size(); ++i) { - Code c = block.get(i); - if (c instanceof Codes.Label) { - Codes.Label l = (Codes.Label) c; - map.put(l.label, new CodeBlock.Index(parent, i)); - } else if (c instanceof CodeBlock) { - buildLabelMap(new CodeBlock.Index(parent, i), (CodeBlock) c, - map); - } - } - } - - /** - * Added a given bytecode to the list of bytecodes which have been explored. - * Furthermore, in the case that the bytecode has not previously been - * explored, it is added to the worklist. - * - * @param index - * --- The index of the bytecode in question. - * @param visited - * --- The list of bytecodes which have been previously explored. - * @param worklist - * --- The list of bytecodes which remain to be explored. - */ - private static void addTarget(CodeBlock.Index index, - HashSet visited, Stack worklist, - AttributedCodeBlock block) { - // First, check whether or not this bytecode exists, which is necessary - // as it won't exist if we've run over the end of a block (for - // example). Second, check whether or not this bytecode has been visited - // already as, if so, we don't need to visit it again. - if(block.contains(index) && !visited.contains(index)) { - visited.add(index); - worklist.push(index); - } - } -} diff --git a/modules/wyil/src/wyil/transforms/LiveVariablesAnalysis.java b/modules/wyil/src/wyil/transforms/LiveVariablesAnalysis.java deleted file mode 100755 index 1e739aef9a..0000000000 --- a/modules/wyil/src/wyil/transforms/LiveVariablesAnalysis.java +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package wyil.transforms; - -import java.util.*; - -import static wycc.lang.SyntaxError.*; -import static wyil.util.ErrorMessages.*; -import wybs.lang.Builder; -import wycc.lang.Transform; -import wycc.util.Pair; -import wyfs.lang.Path; -import wyil.lang.Code; -import wyil.lang.CodeBlock; -import wyil.lang.Codes; -import wyil.lang.WyilFile; -import wyil.lang.Type; -import wyil.util.AttributedCodeBlock; -import wyil.util.dfa.*; - -/** - *

- * Implements a classic live variables analysis to enable efficient reference - * counting. Compound structures in Whiley (e.g. lists, sets, records, etc) are - * reference counted. This means we count the number of aliases to them - * in the heap and on the stack. Then, when a compound structure is updated, we - * can perform an inplace update if the reference count is 1; otherwise, - * we have to clone the structure. Cloning is potentially expensive, and we want - * to avoid it as much as possible. Therefore, as soon as a variable is no - * longer live, we should decrement its reference count. This is signalled using - * the special void bytecode which, literally, "voids" a given - * register thereby allowing us to release it. - *

- *

- * The purpose of this class is to determine when a variable is live, and when - * it is not. The live variables analysis operates as a backwards analysis, - * propagating information from variable uses (i.e. load bytecodes) - * back to their definitions (i.e. store bytecodes). For more - * information on Live Variables Analysis, see the Wikipedia - * page. - *

- * - * @author David J. Pearce, 2011 - * - */ -public class LiveVariablesAnalysis extends BackwardFlowAnalysis implements Transform { - private static final HashMap rewrites = new HashMap(); - - /** - * Determines whether constant propagation is enabled or not. - */ - private boolean enabled = getEnable(); - - private boolean nops = getNops(); - - public LiveVariablesAnalysis(Builder builder) { - - } - - @Override - public void apply(WyilFile module) { - if(enabled) { - super.apply(module); - } - } - - - public static String describeEnable() { - return "Enable/disable live variables analysis"; - } - - public static boolean getEnable() { - return true; // default value - } - - public void setEnable(boolean flag) { - this.enabled = flag; - } - - public static String describeNops() { - return "Enable/disable replacement with nops (helpful for debugging)"; - } - - public static boolean getNops() { - return false; // default value - } - - public void setNops(boolean flag) { - this.nops = flag; - } - - @Override - public WyilFile.Type propagate(WyilFile.Type type) { - AttributedCodeBlock invariant = type.invariant(); - if (invariant != null) { - invariant = propagate(invariant); - return new WyilFile.Type(type.modifiers(), type.name(), - type.type(), invariant, type.attributes()); - } - return type; - } - - /** - * Last store for the live variables analysis is empty, because all - * variables are considered to be dead at the end of a method/function. - * - * @return - */ - @Override - public Env lastStore() { return EMPTY_ENV; } - - @Override - public WyilFile.FunctionOrMethod propagate(WyilFile.FunctionOrMethod mcase) { - - ArrayList requires = new ArrayList( - mcase.precondition()); - for (int i = 0; i != requires.size(); ++i) { - AttributedCodeBlock tmp = propagate(requires.get(i)); - requires.set(i, tmp); - } - ArrayList ensures = new ArrayList( - mcase.postcondition()); - for (int i = 0; i != ensures.size(); ++i) { - AttributedCodeBlock tmp = propagate(ensures.get(i)); - ensures.set(i, tmp); - } - AttributedCodeBlock body = mcase.body(); - if (body != null) { - body = propagate(body); - } - - return new WyilFile.FunctionOrMethod(method.modifiers(), method.name(), - method.type(), body, requires, ensures, mcase.attributes()); - } - - public AttributedCodeBlock propagate(AttributedCodeBlock body) { - rootBlock = body; - stores = new HashMap(); - rewrites.clear(); - Env environment = lastStore(); - propagate(null, body, environment, Collections.EMPTY_LIST); - - // At this point, we apply the inserts - AttributedCodeBlock nbody = new AttributedCodeBlock(); - for(int i=0;i!=body.size();++i) { - Code rewrite = rewrites.get(i); - if(rewrite != null) { - if (!(rewrite instanceof Codes.Nop) || nops) { - // FIXME: missing attributes !? - nbody.add(rewrite); - } - } else { - nbody.add(body.get(i)); - } - } - - return nbody; - } - - @Override - public Env propagate(CodeBlock.Index index, Code code, Env environment) { - rewrites.put(index,null); - boolean isLive = true; - environment = (Env) environment.clone(); - - if (code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode aa = (Code.AbstractBytecode) code; - if(code instanceof Codes.Update) { - Codes.Update cu = (Codes.Update) aa; - // In the normal case, this bytecode is considered live if the - // assigned register is live. However, in the case of an - // indirect assignment, then it is always considered live. - if(!(cu.type(0) instanceof Type.Reference)) { - // No, this is not an indirect assignment through a - // reference - isLive = environment.contains(cu.target(0)); - } - } else { - for(int target : aa.targets()) { - isLive = environment.remove(target); - } - } - } - - if ((isLive && code instanceof Code.AbstractBytecode) - || (code instanceof Codes.Invoke && ((Codes.Invoke) code).type(0) instanceof Type.Method) - || (code instanceof Codes.IndirectInvoke - && ((Codes.IndirectInvoke) code).type(0) instanceof Type.Method)) { - // FIXME: this seems to be a problem if there are no assigned variables! - Code.AbstractBytecode c = (Code.AbstractBytecode) code; - for (int operand : c.operands()) { - environment.add(operand); - } - } else if(!isLive) { - rewrites.put(index, Codes.Nop); - } else { - // const - } - - return environment; - } - - @Override - public Env propagate(CodeBlock.Index index, Codes.If code, Env trueEnv, - Env falseEnv) { - Env r = join(trueEnv, falseEnv); - - r.add(code.operand(0)); - r.add(code.operand(1)); - - return r; - } - - @Override - protected Env propagate(Type handler, Env normalEnv, Env exceptionEnv) { - - return join(normalEnv, exceptionEnv); - } - - @Override - public Env propagate(CodeBlock.Index index, Codes.IfIs code, Env trueEnv, Env falseEnv) { - Env r = join(trueEnv,falseEnv); - - r.add(code.operand(0)); - - return r; - } - - @Override - public Env propagate(CodeBlock.Index index, Codes.Switch code, - List environments, Env defEnv) { - Env environment = defEnv; - - for(int i=0;i!=code.branches.size();++i) { - environment = join(environment,environments.get(i)); - } - - environment.add(code.operand(0)); - - return environment; - } - - public Env propagate(CodeBlock.Index index, Codes.Loop loop, Env environment, - List> handlers) { - rewrites.put(index,null); // to overrule any earlier rewrites - - Env oldEnv = null; - Env newEnv = null; - - if(loop instanceof Codes.Quantify) { - Codes.Quantify fall = (Codes.Quantify) loop; - environment = new Env(environment); - environment.add(fall.startOperand); - environment.add(fall.endOperand); - } else { - environment = EMPTY_ENV; - } - CodeBlock block = (CodeBlock) loop; - do { - // iterate until a fixed point reached - oldEnv = newEnv != null ? newEnv : environment; - newEnv = propagate(index, block, oldEnv, handlers); - newEnv = join(environment,newEnv); - } while (!newEnv.equals(oldEnv)); - - environment = newEnv; - - if(loop instanceof Codes.Quantify) { - Codes.Quantify fall = (Codes.Quantify) loop; - // FIXME: is the following really necessary? - environment.remove(fall.indexOperand); - } - - // Now, check whether any of the modified operands are no longer live. - int nInvalidatedOperands = 0; - for(int mo : loop.modifiedOperands) { - if(!environment.contains(mo)) { - nInvalidatedOperands++; - } - } - if(nInvalidatedOperands > 0) { - // ok, yes, at least one is not live - int[] nModifiedOperands = new int[loop.modifiedOperands.length - nInvalidatedOperands]; - int j = 0; - for(int mo : loop.modifiedOperands) { - if(environment.contains(mo)) { - nModifiedOperands[j++] = mo; - } - } - Code code; - if(loop instanceof Codes.Quantify) { - Codes.Quantify fall = (Codes.Quantify) loop; - - // FIXME: the following is surely broken ? - - code = Codes.Quantify(fall.startOperand, fall.endOperand, - fall.indexOperand, nModifiedOperands, loop.bytecodes()); - } else { - - // FIXME: the following is surely broken ? - - code = Codes.Loop(nModifiedOperands,loop.bytecodes()); - } - rewrites.put(index, code); - } - - return environment; - } - - private Env join(Env env1, Env env2) { - // implements set union - Env r = new Env(env1); - r.addAll(env2); - return r; - } - - private static final Env EMPTY_ENV = new Env(); - - public static final class Env extends HashSet { - public Env() { - super(); - } - - public Env(Env env) { - super(env); - } - } -} diff --git a/modules/wyil/src/wyil/transforms/LoopVariants.java b/modules/wyil/src/wyil/transforms/LoopVariants.java index a9f8a1db3c..214ce6b45f 100644 --- a/modules/wyil/src/wyil/transforms/LoopVariants.java +++ b/modules/wyil/src/wyil/transforms/LoopVariants.java @@ -7,7 +7,7 @@ import wybs.lang.Builder; import wycc.lang.Transform; -import wyil.lang.CodeBlock; +import wyil.lang.CodeForest; import wyil.lang.Code; import wyil.lang.Codes; import wyil.lang.Type; @@ -85,16 +85,16 @@ public void apply(WyilFile module) { } public void infer(WyilFile.Type type) { - CodeBlock invariant = type.invariant(); - if (invariant != null) { - infer(invariant); + CodeForest forest = type.invariant(); + for(int i=0;i!=forest.numRoots();++i) { + infer(forest.getRoot(i),forest); } } public void infer(WyilFile.FunctionOrMethod method) { - CodeBlock body = method.body(); - if(body != null) { - infer(body); + CodeForest forest = method.code(); + for(int i=0;i!=forest.numRoots();++i) { + infer(forest.getRoot(i),forest); } } @@ -107,34 +107,35 @@ public void infer(WyilFile.FunctionOrMethod method) { * @param method * @return */ - protected BitSet infer(CodeBlock block) { - BitSet modified = new BitSet(block.numSlots()); + protected BitSet infer(int blockID, CodeForest forest) { + CodeForest.Block block = forest.get(blockID); + BitSet modified = new BitSet(forest.registers().size()); int size = block.size(); - for(int i=0;i sig_params = sig.params(); - Constant[] frame = new Constant[Math.max(sig_params.size(), - body.numSlots())]; + Constant[] frame = new Constant[code.numRegisters()]; for (int i = 0; i != sig_params.size(); ++i) { frame[i] = args[i]; } // Finally, let's do it! - return (Constant[]) executeAllWithin(frame, new Context(null, body)); + CodeForest.Index pc = new CodeForest.Index(fm.body(), 0); + return (Constant[]) executeAllWithin(frame, new Context(pc, code)); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } @@ -118,21 +118,21 @@ public Constant[] execute(NameID nid, Type.FunctionOrMethod sig, * @return */ private Object executeAllWithin(Constant[] frame, Context context) { - AttributedCodeBlock block = context.block; - CodeBlock.Index parent = context.pc; - CodeBlock.Index pc = parent == null ? new CodeBlock.Index(null, 0) - : parent.firstWithin(); - - do { - Object r = execute(frame, new Context(pc, block)); + CodeForest forest = context.forest; + CodeForest.Index pc = context.pc; + int block = pc.block(); + CodeForest.Block codes = forest.get(pc.block()); + + while(pc.block() == block && pc.offset() < codes.size()) { + Object r = execute(frame, new Context(pc, context.forest)); // Now, see whether we are continuing or not - if (r instanceof CodeBlock.Index) { - pc = (CodeBlock.Index) r; + if (r instanceof CodeForest.Index) { + pc = (CodeForest.Index) r; } else { return r; } - } while (pc.isWithin(parent) && block.contains(pc)); - if (!pc.isWithin(parent)) { + } + if (pc.block() != block) { // non-local exit return pc; } else { @@ -153,7 +153,7 @@ private Object executeAllWithin(Constant[] frame, Context context) { * @return */ private Object execute(Constant[] frame, Context context) { - Code bytecode = context.block.get(context.pc); + Code bytecode = context.forest.get(context.pc).code(); // FIXME: turn this into a switch statement? if (bytecode instanceof Codes.Invariant) { return execute((Codes.Invariant) bytecode, frame, context); @@ -239,10 +239,10 @@ private Object execute(Constant[] frame, Context context) { * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, Context context) { // - Object r = executeAllWithin(frame, context); + CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); + Object r = executeAllWithin(frame, new Context(pc,context.forest)); // if (r == null) { // Body of assert fell through to next @@ -407,7 +407,7 @@ private Object execute(Codes.Convert bytecode, Constant[] frame, Context context) { try { Constant operand = frame[bytecode.operand(0)]; - Type target = expander.getUnderlyingType(bytecode.result); + Type target = expander.getUnderlyingType(bytecode.result()); frame[bytecode.target(0)] = convert(operand, target, context); return context.pc.next(); } catch (IOException e) { @@ -594,8 +594,8 @@ private Object execute(Codes.FieldLoad bytecode, Constant[] frame, private Object execute(Codes.Quantify bytecode, Constant[] frame, Context context) { - Constant startOperand = frame[bytecode.startOperand]; - Constant endOperand = frame[bytecode.endOperand]; + Constant startOperand = frame[bytecode.startOperand()]; + Constant endOperand = frame[bytecode.endOperand()]; checkType(startOperand, context, Constant.Integer.class); checkType(endOperand, context, Constant.Integer.class); Constant.Integer so = (Constant.Integer) startOperand; @@ -604,9 +604,10 @@ private Object execute(Codes.Quantify bytecode, Constant[] frame, int end = eo.value.intValue(); for (int i = start; i < end; ++i) { // Assign the index variable - frame[bytecode.indexOperand] = Constant.V_INTEGER(BigInteger.valueOf(i)); + frame[bytecode.indexOperand()] = Constant.V_INTEGER(BigInteger.valueOf(i)); // Execute loop body for one iteration - Object r = executeAllWithin(frame, context); + CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); + Object r = executeAllWithin(frame, new Context(pc,context.forest)); // Now, check whether we fell through to the end or not. If not, // then we must have exited somehow so return to signal that. if (r != null) { @@ -619,7 +620,7 @@ private Object execute(Codes.Quantify bytecode, Constant[] frame, private Object execute(Codes.Goto bytecode, Constant[] frame, Context context) { - return context.getLabel(bytecode.target); + return context.getLabel(bytecode.destination()); } private Object execute(Codes.If bytecode, Constant[] frame, Context context) { @@ -653,7 +654,7 @@ private Object execute(Codes.If bytecode, Constant[] frame, Context context) { if (result) { // branch taken, so jump to destination label - return context.getLabel(bytecode.target); + return context.getLabel(bytecode.destination()); } else { // branch not taken, so fall through to next bytecode. return context.pc.next(); @@ -683,10 +684,10 @@ private boolean lessThan(Constant lhs, Constant rhs, boolean isStrict, } private Object execute(Codes.IfIs bytecode, Constant[] frame, Context context) { - Type typeOperand = bytecode.rightOperand; + Type typeOperand = bytecode.type(1); Constant op = frame[bytecode.operand(0)]; if (isMemberOfType(op, typeOperand, context)) { - return context.getLabel(bytecode.target); + return context.getLabel(bytecode.destination()); } // No, it doesn't so fall through to next instruction return context.pc.next(); @@ -791,11 +792,12 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { return false; } // Check any invariant associated with this type - AttributedCodeBlock invariant = td.invariant(); - if (invariant != null) { - Constant[] frame = new Constant[invariant.numSlots()]; + CodeForest invariant = td.invariant(); + if (invariant.numBlocks() > 0) { + Constant[] frame = new Constant[invariant.numRegisters()]; frame[0] = value; - executeAllWithin(frame, new Context(null, invariant)); + CodeForest.Index pc = new CodeForest.Index(invariant.getRoot(0), 0); + executeAllWithin(frame, new Context(pc, invariant)); } // Done return true; @@ -870,20 +872,20 @@ private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, // constant arguments provided in the lambda itself along with those // operands provided for the "holes". Constant.Lambda func = (Constant.Lambda) operand; + List func_arguments = func.arguments; int[] operands = bytecode.operands(); - Constant[] arguments = new Constant[Math.max(func.arguments.size(), - operands.length - 1)]; - int operandIndex = 1; - for (int i = 0; i != arguments.length; ++i) { - if (i >= func.arguments.size() || func.arguments.get(i) == null) { - arguments[i] = frame[operands[operandIndex]]; - operandIndex = operandIndex + 1; - } else { - arguments[i] = func.arguments.get(i); + Constant[] arguments = new Constant[func_arguments.size() + (operands.length-1)]; + { + int i=0; + for (int j = 1; j != operands.length; ++j) { + arguments[i++] = frame[operands[j]]; + } + for (int j = 0; j != func_arguments.size(); ++j) { + arguments[i++] = func.arguments.get(j); } } // Make the actual call - Constant[] results = execute(func.name, func.type(), arguments); + Constant[] results = execute(func.name, func.type(), arguments); // Check whether a return value was expected or not int[] targets = bytecode.targets(); List returns = bytecode.type(0).returns(); @@ -959,11 +961,10 @@ private Object execute(Codes.Lambda bytecode, Constant[] frame, int[] operands = bytecode.operands(); Constant[] arguments = new Constant[operands.length]; + for (int i = 0; i != arguments.length; ++i) { int reg = operands[i]; - if (reg != Codes.NULL_REG) { - arguments[i] = frame[reg]; - } + arguments[i] = frame[reg]; } // FIXME: need to do something with the operands here. frame[bytecode.target(0)] = Constant.V_LAMBDA(bytecode.name, bytecode.type(0), arguments); @@ -1029,7 +1030,8 @@ private Object execute(Codes.Loop bytecode, Constant[] frame, Object r; do { // Keep executing the loop body until we exit it somehow. - r = executeAllWithin(frame, context); + CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); + r = executeAllWithin(frame, new Context(pc,context.forest)); } while (r == null); // If we get here, then we have exited the loop body without falling @@ -1365,18 +1367,18 @@ private Object deadCode(Context context) { * */ public static class Context { - public final CodeBlock.Index pc; - public final AttributedCodeBlock block; - private Map labels; + public final CodeForest.Index pc; + public final CodeForest forest; + private Map labels; - public Context(CodeBlock.Index pc, AttributedCodeBlock block) { + public Context(CodeForest.Index pc, CodeForest block) { this.pc = pc; - this.block = block; + this.forest = block; } - public CodeBlock.Index getLabel(String label) { + public CodeForest.Index getLabel(String label) { if (labels == null) { - labels = CodeUtils.buildLabelMap(block); + labels = CodeUtils.buildLabelMap(forest); } return labels.get(label); } diff --git a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java index 5badf08d73..c9b70fbd16 100755 --- a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java +++ b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java @@ -36,7 +36,6 @@ import wycc.util.Pair; import wyil.attributes.SourceLocation; import wyil.lang.*; -import wyil.util.AttributedCodeBlock; import static wyil.util.ErrorMessages.*; public abstract class BackwardFlowAnalysis { @@ -54,7 +53,7 @@ public abstract class BackwardFlowAnalysis { /** * The root block currently being propagated through. */ - protected AttributedCodeBlock rootBlock; + protected CodeForest forest; /** * The temporary abstract stores being generated during propagation. @@ -92,15 +91,14 @@ protected WyilFile.FunctionOrMethod propagate( WyilFile.FunctionOrMethod method) { this.method = method; this.stores = new HashMap(); - this.rootBlock = method.body(); + this.forest = method.code(); T last = lastStore(); - propagate(null, rootBlock, last, Collections.EMPTY_LIST); + propagate(method.body(), last, Collections.EMPTY_LIST); // FIXME: should we propagate through the precondition and postconditions !? - return new WyilFile.FunctionOrMethod(method.modifiers(), method.name(), - method.type(), method.body(), method.precondition(), - method.postcondition(), method.attributes()); + return new WyilFile.FunctionOrMethod(method.modifiers(), method.name(), method.type(), method.code(), + method.preconditions().length, method.postconditions().length, method.attributes()); } /** @@ -118,13 +116,14 @@ protected WyilFile.FunctionOrMethod propagate( * The list of active exception handlers * @return */ - protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, T store, List> handlers) { - + protected T propagate(int blockID, T store, List> handlers) { + CodeForest.Block block = forest.get(blockID); + for (int i = block.size()-1; i >= 0; --i) { - Code code = block.get(i); + Code code = block.get(i).code(); // Construct the bytecode index - CodeBlock.Index id = new CodeBlock.Index(parentIndex,i); + CodeForest.Index id = new CodeForest.Index(blockID,i); try { // First, check for a label which may have incoming information. @@ -137,11 +136,11 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, T store, Lis stores.put(l.label,store); } else if (code instanceof Codes.If) { Codes.If ifgoto = (Codes.If) code; - T trueStore = stores.get(ifgoto.target); + T trueStore = stores.get(ifgoto.destination()); store = propagate(id, ifgoto, trueStore, store); } else if (code instanceof Codes.IfIs) { Codes.IfIs iftype = (Codes.IfIs) code; - T trueStore = stores.get(iftype.target); + T trueStore = stores.get(iftype.destination()); store = propagate(id, iftype, trueStore, store); } else if (code instanceof Codes.Switch) { Codes.Switch sw = (Codes.Switch) code; @@ -156,7 +155,7 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, T store, Lis store = propagate(id, sw, swStores, defStore); } else if (code instanceof Codes.Goto) { Codes.Goto gto = (Codes.Goto) code; - store = stores.get(gto.target); + store = stores.get(gto.destination()); } else { // This indicates a sequential statement was encountered. if (code instanceof Codes.Return @@ -168,7 +167,8 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, T store, Lis } catch (SyntaxError se) { throw se; } catch (Throwable ex) { - internalFailure("internal failure", filename, ex, rootBlock.attribute(id,SourceLocation.class)); } + internalFailure("internal failure", filename, ex, forest.get(id).attribute(SourceLocation.class)); + } } return store; @@ -194,7 +194,7 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, T store, Lis * statement on the false branch. * @return */ - protected abstract T propagate(CodeBlock.Index index, Codes.If ifgoto, + protected abstract T propagate(CodeForest.Index index, Codes.If ifgoto, T trueStore, T falseStore); /** @@ -216,7 +216,7 @@ protected abstract T propagate(CodeBlock.Index index, Codes.If ifgoto, * statement on the false branch. * @return */ - protected abstract T propagate(CodeBlock.Index index, Codes.IfIs iftype, T trueStore, + protected abstract T propagate(CodeForest.Index index, Codes.IfIs iftype, T trueStore, T falseStore); /** @@ -236,7 +236,7 @@ protected abstract T propagate(CodeBlock.Index index, Codes.IfIs iftype, T trueS * --- abstract store coming from default branch * @return */ - protected abstract T propagate(CodeBlock.Index index, Codes.Switch sw, + protected abstract T propagate(CodeForest.Index index, Codes.Switch sw, List stores, T defStore); /** @@ -254,7 +254,7 @@ protected abstract T propagate(CodeBlock.Index index, Codes.Switch sw, * statement. * @return */ - protected abstract T propagate(CodeBlock.Index index, Codes.Loop code, T store, + protected abstract T propagate(CodeForest.Index index, Codes.Loop code, T store, List> handlers); /** @@ -272,7 +272,7 @@ protected abstract T propagate(CodeBlock.Index index, Codes.Loop code, T store, * statement. * @return */ - protected abstract T propagate(CodeBlock.Index index, Code code, T store); + protected abstract T propagate(CodeForest.Index index, Code code, T store); /** * Propagate from an exception handler. diff --git a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java index 1be76b5950..94212ef240 100755 --- a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java +++ b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java @@ -51,9 +51,9 @@ public abstract class ForwardFlowAnalysis { protected WyilFile.FunctionOrMethod method; /** - * The root block currently being propagated through. + * The code forest currently being propagated through. */ - protected AttributedCodeBlock rootBlock; + protected CodeForest forest; /** * The temporary abstract stores being generated during propagation. @@ -92,12 +92,12 @@ protected WyilFile.FunctionOrMethod propagate( WyilFile.FunctionOrMethod method) { this.method = method; this.stores = new HashMap(); - this.rootBlock = method.body(); + this.forest = method.code(); T init = initialStore(); - propagate(null, rootBlock, init); + propagate(method.body(), init); return new WyilFile.FunctionOrMethod(method.modifiers(), method.name(), - method.type(), method.body(), method.precondition(), - method.postcondition(), method.attributes()); + method.type(), method.code(), method.preconditions().length, + method.postconditions().length, method.attributes()); } /** @@ -115,14 +115,14 @@ protected WyilFile.FunctionOrMethod propagate( * The list of active exception handlers * @return */ - protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, - T store) { - + protected T propagate(int blockID, T store) { + + CodeForest.Block block = forest.get(blockID); for (int i = 0; i < block.size(); ++i) { - Code code = block.get(i); + Code code = block.get(i).code(); // Construct the bytecode ID - CodeBlock.Index id = new CodeBlock.Index(parentIndex,i); + CodeForest.Index id = new CodeForest.Index(blockID,i); try { // First, check for a label which may have incoming information. @@ -150,12 +150,12 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, Codes.If ifgoto = (Codes.If) code; Pair r = propagate(id, ifgoto, store); store = r.second(); - merge(ifgoto.target, r.first(), stores); + merge(ifgoto.destination(), r.first(), stores); } else if (code instanceof Codes.IfIs) { Codes.IfIs ifgoto = (Codes.IfIs) code; Pair r = propagate(id, ifgoto, store); store = r.second(); - merge(ifgoto.target, r.first(), stores); + merge(ifgoto.destination(), r.first(), stores); } else if (code instanceof Codes.Switch) { Codes.Switch sw = (Codes.Switch) code; @@ -172,7 +172,7 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, store = null; } else if (code instanceof Codes.Goto) { Codes.Goto gto = (Codes.Goto) code; - merge(gto.target, store, stores); + merge(gto.destination(), store, stores); store = null; } else { // This indicates a sequential statement was encountered. @@ -186,7 +186,7 @@ protected T propagate(CodeBlock.Index parentIndex, CodeBlock block, } catch (SyntaxError se) { throw se; } catch (Throwable ex) { - internalFailure("internal failure", filename, ex, rootBlock.attribute(id,SourceLocation.class)); + internalFailure("internal failure", filename, ex, forest.get(id).attribute(SourceLocation.class)); } } @@ -220,7 +220,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract Pair propagate(CodeBlock.Index index, Codes.If ifgoto, T store); + protected abstract Pair propagate(CodeForest.Index index, Codes.If ifgoto, T store); /** *

@@ -240,7 +240,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract Pair propagate(CodeBlock.Index index, Codes.IfIs iftype, T store); + protected abstract Pair propagate(CodeForest.Index index, Codes.IfIs iftype, T store); /** *

@@ -257,7 +257,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract List propagate(CodeBlock.Index index, Codes.Switch sw, T store); + protected abstract List propagate(CodeForest.Index index, Codes.Switch sw, T store); /** *

@@ -280,7 +280,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract T propagate(CodeBlock.Index index, Codes.Loop code, T store); + protected abstract T propagate(CodeForest.Index index, Codes.Loop code, T store); /** *

@@ -297,7 +297,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract T propagate(CodeBlock.Index index, Code code, T store); + protected abstract T propagate(CodeForest.Index index, Code code, T store); /** * Determine the initial store for the current method case. diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index e5a4449eb4..92e83528ed 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -398,7 +398,7 @@ private AttributedCodeBlock patchInvariantBlock(String falseBranch, return copy; } - private void patchInvariantBlockHelper(String falseBranch, CodeBlock block) { + private void patchInvariantBlockHelper(String falseBranch, CodeForest block) { for (int i = 0; i != block.size(); ++i) { // This is still a valid index Code c = block.get(i); @@ -557,11 +557,11 @@ private void translate(AttributedCodeBlock blk, int freeSlot, * @param bytecodes * List of bytecodes being accumulated */ - private void translate(CodeBlock.Index parentIndex, CodeBlock block, + private void translate(CodeForest.Index parentIndex, CodeForest block, int freeSlot, ArrayList bytecodes) { for (int i = 0; i != block.size(); ++i) { - CodeBlock.Index index = new CodeBlock.Index(parentIndex, i); + CodeForest.Index index = new CodeForest.Index(parentIndex, i); SourceLocation loc = rootBlock.attribute(index, SourceLocation.class); if (loc != null) { @@ -589,7 +589,7 @@ private void translate(CodeBlock.Index parentIndex, CodeBlock block, * The list of bytecodes being accumulated * @return */ - private int translate(CodeBlock.Index index, Code code, int freeSlot, + private int translate(CodeForest.Index index, Code code, int freeSlot, ArrayList bytecodes) { try { @@ -675,20 +675,20 @@ private int translate(CodeBlock.Index index, Code code, int freeSlot, } - private void translate(CodeBlock.Index index, Codes.AssertOrAssume c, + private void translate(CodeForest.Index index, Codes.AssertOrAssume c, int freeSlot, ArrayList bytecodes) { if(c instanceof Codes.Invariant) { // essentially a no-op for now } else if(c instanceof Codes.Assert) { Codes.Assert ca = (Codes.Assert) c; - translate(index, (CodeBlock) c, freeSlot, bytecodes); + translate(index, (CodeForest) c, freeSlot, bytecodes); } else { Codes.Assume ca = (Codes.Assume) c; - translate(index, (CodeBlock) c, freeSlot, bytecodes); + translate(index, (CodeForest) c, freeSlot, bytecodes); } } - private void translate(CodeBlock.Index index, Codes.Const c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Const c, int freeSlot, ArrayList bytecodes) { Constant constant = c.constant; JvmType jt = convertUnderlyingType(constant.type()); @@ -706,13 +706,13 @@ private void translate(CodeBlock.Index index, Codes.Const c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(), jt)); } - private void translate(CodeBlock.Index index, Codes.Convert c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Convert c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); addCoercion(c.type(0), c.result, freeSlot, constants, bytecodes); bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.result))); } - private void translate(CodeBlock.Index index, Codes.Update code, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Update code, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(code.target(0), convertUnderlyingType(code.type(0)))); translateUpdate(code.iterator(), code, bytecodes); bytecodes.add(new Bytecode.Store(code.target(0), convertUnderlyingType(code.afterType))); @@ -855,7 +855,7 @@ private void translateUpdate(Codes.ReferenceLVal lval, Iterator iter bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "setState", setFunType, Bytecode.InvokeMode.VIRTUAL)); } - private void translate(CodeBlock.Index index, Codes.Return c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Return c, int freeSlot, ArrayList bytecodes) { JvmType jt = null; int[] operands = c.operands(); @@ -870,7 +870,7 @@ private void translate(CodeBlock.Index index, Codes.Return c, int freeSlot, bytecodes.add(new Bytecode.Return(jt)); } - private void translate(CodeBlock.Index index, Codes.Switch c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Switch c, int freeSlot, ArrayList bytecodes) { ArrayList> cases = new ArrayList(); @@ -914,14 +914,14 @@ private void translate(CodeBlock.Index index, Codes.Switch c, int freeSlot, } } - private void translateIfGoto(CodeBlock.Index index, Codes.If code, int freeSlot, ArrayList bytecodes) { + private void translateIfGoto(CodeForest.Index index, Codes.If code, int freeSlot, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(code.type(0)); bytecodes.add(new Bytecode.Load(code.operand(0), jt)); bytecodes.add(new Bytecode.Load(code.operand(1), jt)); translateIfGoto(index, code.type(0), code.op, code.target, freeSlot, bytecodes); } - private void translateIfGoto(CodeBlock.Index index, Type c_type, + private void translateIfGoto(CodeForest.Index index, Type c_type, Codes.Comparator cop, String target, int freeSlot, ArrayList bytecodes) { @@ -1006,7 +1006,7 @@ private void translateIfGoto(CodeBlock.Index index, Type c_type, bytecodes.add(new Bytecode.If(op, target)); } - private void translate(CodeBlock.Index index, Codes.IfIs c, int freeSlot, + private void translate(CodeForest.Index index, Codes.IfIs c, int freeSlot, ArrayList bytecodes) { // In this case, we're updating the type of a local variable. To @@ -1224,7 +1224,7 @@ private void translateInvariantTest(String falseTarget, Type type, } } - private void translate(CodeBlock.Index index, Codes.Loop c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Loop c, int freeSlot, ArrayList bytecodes) { // Allocate header label for loop @@ -1232,12 +1232,12 @@ private void translate(CodeBlock.Index index, Codes.Loop c, int freeSlot, bytecodes.add(new Bytecode.Label(loopHeader)); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(index, (CodeBlock) c, freeSlot, bytecodes); + translate(index, (CodeForest) c, freeSlot, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); } - private int translate(CodeBlock.Index index, Codes.Quantify c, int freeSlot, + private int translate(CodeForest.Index index, Codes.Quantify c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.startOperand,WHILEYINT)); @@ -1266,7 +1266,7 @@ private int translate(CodeBlock.Index index, Codes.Quantify c, int freeSlot, convertUnderlyingType(Type.T_INT))); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(index, (CodeBlock) c, freeSlot + 1, bytecodes); + translate(index, (CodeForest) c, freeSlot + 1, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); bytecodes.add(new Bytecode.Label(loopExit)); @@ -1274,38 +1274,38 @@ private int translate(CodeBlock.Index index, Codes.Quantify c, int freeSlot, return freeSlot; } - private void translate(CodeBlock.Index index, Codes.Goto c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Goto c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Goto(c.target)); } - private void translate(CodeBlock.Index index, Codes.Label c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Label c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Label(c.label)); } - private void translate(CodeBlock.Index index, Codes.Debug c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Debug c, int freeSlot, ArrayList bytecodes) { JvmType.Function ftype = new JvmType.Function(T_VOID, WHILEYARRAY); bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "print", ftype, Bytecode.InvokeMode.STATIC)); } - private void translate(CodeBlock.Index index, Codes.Assign c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Assign c, int freeSlot, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.Load(c.operand(0), jt)); bytecodes.add(new Bytecode.Store(c.target(0), jt)); } - private void translate(CodeBlock.Index index, Codes.Move c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Move c, int freeSlot, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.Load(c.operand(0), jt)); bytecodes.add(new Bytecode.Store(c.target(0), jt)); } - private void translate(CodeBlock.Index index, Codes.ArrayGenerator c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.ArrayGenerator c, int freeSlot, ArrayList bytecodes) { JvmType elementType = convertUnderlyingType(c.type(0).element()); bytecodes.add(new Bytecode.Load(c.operand(0), elementType)); addWriteConversion(c.type(0).element(), bytecodes); @@ -1315,7 +1315,7 @@ private void translate(CodeBlock.Index index, Codes.ArrayGenerator c, int freeSl bytecodes.add(new Bytecode.Store(c.target(0), WHILEYARRAY)); } - private void translate(CodeBlock.Index index, Codes.LengthOf c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.LengthOf c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType((Type) c.type(0)))); JvmType.Clazz ctype = JAVA_LANG_OBJECT; JvmType.Function ftype = new JvmType.Function(WHILEYINT); @@ -1323,7 +1323,7 @@ private void translate(CodeBlock.Index index, Codes.LengthOf c, int freeSlot, Ar bytecodes.add(new Bytecode.Store(c.target(0), WHILEYINT)); } - private void translate(CodeBlock.Index index, Codes.IndexOf c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.IndexOf c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); @@ -1332,7 +1332,7 @@ private void translate(CodeBlock.Index index, Codes.IndexOf c, int freeSlot, Arr bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.type(0).element()))); } - private void translate(CodeBlock.Index index, Codes.Fail c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.LoadConst("runtime fault encountered")); @@ -1341,7 +1341,7 @@ private void translate(CodeBlock.Index index, Codes.Fail c, int freeSlot, ArrayL bytecodes.add(new Bytecode.Throw()); } - private void translate(CodeBlock.Index index, Codes.FieldLoad c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYRECORD)); bytecodes.add(new Bytecode.LoadConst(c.field)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYRECORD, JAVA_LANG_STRING); @@ -1350,7 +1350,7 @@ private void translate(CodeBlock.Index index, Codes.FieldLoad c, int freeSlot, A bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.fieldType()))); } - private void translate(CodeBlock.Index index, Codes.BinaryOperator c, + private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeSlot, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); @@ -1431,7 +1431,7 @@ private void translate(CodeBlock.Index index, Codes.BinaryOperator c, bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeBlock.Index index, Codes.Invert c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Invert c, int freeSlot, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.Load(c.operand(0), type)); JvmType.Function ftype = new JvmType.Function(type); @@ -1439,7 +1439,7 @@ private void translate(CodeBlock.Index index, Codes.Invert c, int freeSlot, Arra bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeBlock.Index index, Codes.UnaryOperator c, + private void translate(CodeForest.Index index, Codes.UnaryOperator c, int freeSlot, ArrayList bytecodes) { JvmType srcType = convertUnderlyingType(c.type(0)); JvmType targetType = null; @@ -1456,7 +1456,7 @@ private void translate(CodeBlock.Index index, Codes.UnaryOperator c, bytecodes.add(new Bytecode.Store(c.target(0), targetType)); } - private void translate(CodeBlock.Index index, Codes.NewObject c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.New(WHILEYOBJECT)); bytecodes.add(new Bytecode.Dup(WHILEYOBJECT)); @@ -1467,7 +1467,7 @@ private void translate(CodeBlock.Index index, Codes.NewObject c, int freeSlot, A bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeBlock.Index index, Codes.Dereference c, + private void translate(CodeForest.Index index, Codes.Dereference c, int freeSlot, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT); @@ -1479,7 +1479,7 @@ private void translate(CodeBlock.Index index, Codes.Dereference c, bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.type(0).element()))); } - protected void translate(CodeBlock.Index index, Codes.NewArray c, int freeSlot, ArrayList bytecodes) { + protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(WHILEYARRAY)); bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); bytecodes.add(new Bytecode.LoadConst(c.operands().length)); @@ -1496,7 +1496,7 @@ protected void translate(CodeBlock.Index index, Codes.NewArray c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), WHILEYARRAY)); } - private void translate(CodeBlock.Index index, Codes.NewRecord code, + private void translate(CodeForest.Index index, Codes.NewRecord code, int freeSlot, ArrayList bytecodes) { construct(WHILEYRECORD, freeSlot, bytecodes); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); @@ -1519,7 +1519,7 @@ private void translate(CodeBlock.Index index, Codes.NewRecord code, bytecodes.add(new Bytecode.Store(code.target(0), WHILEYRECORD)); } - private void translate(CodeBlock.Index index, Codes.Lambda c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, ArrayList bytecodes) { // First, build and register lambda class which calls the given function // or method. This class will extend class wyjc.runtime.WyLambda. @@ -1582,7 +1582,7 @@ private void translate(CodeBlock.Index index, Codes.Lambda c, int freeSlot, Arra bytecodes.add(new Bytecode.Store(c.target(0), clazz)); } - private void translate(CodeBlock.Index index, Codes.Invoke c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Invoke c, int freeSlot, ArrayList bytecodes) { for (int i = 0; i != c.operands().length; ++i) { int register = c.operands()[i]; @@ -1609,7 +1609,7 @@ private void translate(CodeBlock.Index index, Codes.Invoke c, int freeSlot, Arra } } - private void translate(CodeBlock.Index index, Codes.IndirectInvoke c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.IndirectInvoke c, int freeSlot, ArrayList bytecodes) { Type.FunctionOrMethod ft = c.type(0); JvmType.Clazz owner = (JvmType.Clazz) convertUnderlyingType(ft); bytecodes.add(new Bytecode.Load(c.reference(), convertUnderlyingType(ft))); From 44be82b519555c67293c1a6fd50db709328c68e5 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Thu, 25 Feb 2016 18:32:04 +1300 Subject: [PATCH 07/43] WyIL: Update VcGenerator This updates the VcGenerator to compile again and pass tests. Unfortunately, it's looking something of a mess now and is in dire need of some serious refactoring. I have thought about how to do this, and report my findings in #501 --- .../wyc/src/wyc/builder/CodeGenerator.java | 231 +-- modules/wyil/src/wyil/builders/VcBranch.java | 4 +- .../src/wyil/builders/VcExprGenerator.java | 421 +++++ .../wyil/src/wyil/builders/VcGenerator.java | 1437 ++--------------- modules/wyil/src/wyil/builders/VcUtils.java | 669 ++++++++ .../src/wyil/builders/Wyil2WyalBuilder.java | 2 - modules/wyil/src/wyil/io/WyilFileReader.java | 4 +- modules/wyil/src/wyil/lang/Code.java | 5 +- modules/wyil/src/wyil/lang/CodeForest.java | 8 + modules/wyil/src/wyil/lang/Codes.java | 12 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 303 ++-- 11 files changed, 1530 insertions(+), 1566 deletions(-) create mode 100644 modules/wyil/src/wyil/builders/VcExprGenerator.java create mode 100644 modules/wyil/src/wyil/builders/VcUtils.java diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index d1f70c976b..f4ef03ce4f 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -239,6 +239,17 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw // Generate body // ================================================================== + // First, reset the environment. This is necessary because the + // environment may have been contaminated with local variables arising + // in the precondition or postcondition. Such variables are possible if + // quantifiers are used. + + // FIXME: using resetEnvironment feels ugly and could fail when multiple + // variables of the same name are declared in the body of a function + // or method (currently this is only possible via quantifiers, but won't + // fail because they have the same type). + environment = resetEnvironment(environment, declarations); + CodeForest.Block body = new CodeForest.Block(); forest.addAsRoot(body); for (Stmt s : fd.statements) { @@ -489,7 +500,8 @@ private void generate(VariableDeclaration s, Environment environment, CodeForest * with error reporting as it determines the enclosing file. * @return */ - private void generate(Assign s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { + private void generate(Assign s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { // First, we translate all right-hand side expressions and assign them // to temporary registers. ArrayList operands = new ArrayList(); @@ -803,7 +815,8 @@ private void generate(Stmt.Debug s, Environment environment, CodeForest.Block bl * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Fail s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { + private void generate(Stmt.Fail s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { block.add(Codes.Fail(), attributes(s)); } @@ -852,12 +865,11 @@ private void generate(Stmt.Fail s, Environment environment, CodeForest.Block blo * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.IfElse s, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) { + private void generate(Stmt.IfElse s, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { String falseLab = CodeUtils.freshLabel(); - String exitLab = s.falseBranch.isEmpty() ? falseLab : CodeUtils - .freshLabel(); + String exitLab = s.falseBranch.isEmpty() ? falseLab : CodeUtils.freshLabel(); generateCondition(falseLab, invert(s.condition), environment, block, forest, context); @@ -1734,8 +1746,7 @@ public int generate(Expr expression, Environment environment, CodeForest.Block b return generate((Expr.New) expression, environment, block, forest, context); } else { // should be dead-code - internalFailure("unknown expression: " - + expression.getClass().getName(), context, expression); + internalFailure("unknown expression: " + expression.getClass().getName(), context, expression); } } catch (ResolveError rex) { syntaxError(rex.getMessage(), context, expression, rex); @@ -1748,16 +1759,15 @@ public int generate(Expr expression, Environment environment, CodeForest.Block b return -1; // deadcode } - public int generate(Expr.FunctionOrMethodCall expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { + public int generate(Expr.FunctionOrMethodCall expr, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) throws ResolveError { int target = environment.allocate(expr.result().raw()); generateStmt(expr, environment, block, forest, context, target); return target; } - public int generate(Expr.IndirectFunctionOrMethodCall expr, - Environment environment, CodeForest.Block block, CodeForest forest, Context context) - throws ResolveError { + public int generate(Expr.IndirectFunctionOrMethodCall expr, Environment environment, CodeForest.Block block, + CodeForest forest, Context context) throws ResolveError { int target = environment.allocate(expr.result().raw()); generateStmt(expr, environment, block, forest, context, target); return target; @@ -1790,14 +1800,14 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block ArrayList operands = new ArrayList(); ArrayList paramTypes = new ArrayList(); CodeForest bodyForest = new CodeForest(); - List declarations = bodyForest.registers(); - Environment benv = new Environment(); + List declarations = bodyForest.registers(); + Environment benv = new Environment(); for (int i = 0; i != tfm_params.size(); ++i) { Type type = tfm_params.get(i); String name = expr_params.get(i).name; benv.allocate(type, name); paramTypes.add(type); - declarations.add(new CodeForest.Register(type,name)); + declarations.add(new CodeForest.Register(type, name)); } for (Pair v : Exprs.uses(expr.body, context)) { if (benv.get(v.second()) == null) { @@ -1805,27 +1815,28 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block benv.allocate(type, v.second()); paramTypes.add(type); operands.add(environment.get(v.second())); - declarations.add(new CodeForest.Register(type,v.second())); + declarations.add(new CodeForest.Register(type, v.second())); } } // Generate body based on current environment - + CodeForest.Block bodyBlock = new CodeForest.Block(); bodyForest.addAsRoot(bodyBlock); if (tfm.returns().isEmpty()) { bodyBlock.add(Codes.Return(), attributes(expr)); } else { int target = generate(expr.body, benv, bodyBlock, bodyForest, context); - bodyBlock.add(Codes.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), attributes(expr)); + bodyBlock.add(Codes.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), + attributes(expr)); } // Add type information for all temporary registers allocated // during code generation. This complements the existing information // about declared variables. - for(int i=declarations.size();i!=benv.size();i=i+1) { + for (int i = declarations.size(); i != benv.size(); i = i + 1) { Type t = benv.type(i); - declarations.add(new CodeForest.Register(t,null)); - } + declarations.add(new CodeForest.Register(t, null)); + } // Create concrete type for private lambda function Type.FunctionOrMethod cfm; if (tfm instanceof Type.Function) { @@ -1839,8 +1850,8 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block String name = "$lambda" + id; ArrayList modifiers = new ArrayList(); modifiers.add(Modifier.PRIVATE); - WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod( - modifiers, name, cfm, bodyForest, 0, 0, attributes(expr)); + WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod(modifiers, name, cfm, bodyForest, 0, 0, + attributes(expr)); lambdas.add(lambda); Path.ID mid = context.file().module; NameID nid = new NameID(mid, name); @@ -1851,8 +1862,8 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block return target; } - private int generate(Expr.ConstantAccess expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { + private int generate(Expr.ConstantAccess expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) throws ResolveError { Constant val = expr.value; int target = environment.allocate(val.type()); block.add(Codes.Const(target, val), attributes(expr)); @@ -1872,35 +1883,31 @@ private int generate(Expr.LocalVariable expr, Environment environment, CodeFores } } - private int generate(Expr.UnOp expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) { + private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int operand = generate(expr.mhs, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); switch (expr.op) { case NEG: - block.add(Codes.UnaryOperator(expr.result().raw(), target, operand, - Codes.UnaryOperatorKind.NEG), attributes(expr)); + block.add(Codes.UnaryOperator(expr.result().raw(), target, operand, Codes.UnaryOperatorKind.NEG), + attributes(expr)); break; case INVERT: - block.add(Codes.Invert(expr.result().raw(), target, operand), - attributes(expr)); + block.add(Codes.Invert(expr.result().raw(), target, operand), attributes(expr)); break; case NOT: String falseLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); generateCondition(falseLabel, expr.mhs, environment, block, forest, context); - block.add(Codes.Const(target, Constant.V_BOOL(true)), - attributes(expr)); + block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(expr)); block.add(Codes.Goto(exitLabel)); block.add(Codes.Label(falseLabel)); - block.add(Codes.Const(target, Constant.V_BOOL(false)), - attributes(expr)); + block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(expr)); block.add(Codes.Label(exitLabel)); break; default: // should be dead-code - internalFailure("unexpected unary operator encountered", context, - expr); + internalFailure("unexpected unary operator encountered", context, expr); return -1; } return target; @@ -1914,27 +1921,25 @@ private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Blo return target; } - private int generate(Expr.Dereference expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) { + private int generate(Expr.Dereference expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int operand = generate(expr.src, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - block.add(Codes.Dereference(expr.srcType.raw(), target, operand), - attributes(expr)); + block.add(Codes.Dereference(expr.srcType.raw(), target, operand), attributes(expr)); return target; } - private int generate(Expr.IndexOf expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) { + private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int srcOperand = generate(expr.src, environment, block, forest, context); int idxOperand = generate(expr.index, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - block.add(Codes.IndexOf((Type.Array) expr.srcType.raw(), target, srcOperand, - idxOperand), attributes(expr)); + block.add(Codes.IndexOf((Type.Array) expr.srcType.raw(), target, srcOperand, idxOperand), attributes(expr)); return target; } - private int generate(Expr.Cast expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) { + private int generate(Expr.Cast expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int operand = generate(expr.expr, environment, block, forest, context); Type from = expr.expr.result().raw(); Type to = expr.result().raw(); @@ -1943,20 +1948,17 @@ private int generate(Expr.Cast expr, Environment environment, return target; } - private int generate(Expr.BinOp v, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) throws Exception { + private int generate(Expr.BinOp v, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) throws Exception { // could probably use a range test for this somehow - if (v.op == Expr.BOp.EQ || v.op == Expr.BOp.NEQ || v.op == Expr.BOp.LT - || v.op == Expr.BOp.LTEQ || v.op == Expr.BOp.GT - || v.op == Expr.BOp.GTEQ - || v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { + if (v.op == Expr.BOp.EQ || v.op == Expr.BOp.NEQ || v.op == Expr.BOp.LT || v.op == Expr.BOp.LTEQ + || v.op == Expr.BOp.GT || v.op == Expr.BOp.GTEQ || v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { String trueLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); generateCondition(trueLabel, v, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - block.add(Codes.Const(target, Constant.V_BOOL(false)), - attributes(v)); + block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(v)); block.add(Codes.Goto(exitLabel)); block.add(Codes.Label(trueLabel)); block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(v)); @@ -1977,23 +1979,23 @@ private int generate(Expr.BinOp v, Environment environment, } } - private int generate(Expr.ArrayInitialiser expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) { + private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int[] operands = generate(expr.arguments, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - block.add(Codes.NewArray((Type.Array) expr.type.raw(), target, operands), - attributes(expr)); + block.add(Codes.NewArray((Type.Array) expr.type.raw(), target, operands), attributes(expr)); return target; } - private int generate(Expr.ArrayGenerator expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { + private int generate(Expr.ArrayGenerator expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) { int element = generate(expr.element, environment, block, forest, context); int count = generate(expr.count, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); block.add(Codes.ArrayGenerator((Type.Array) expr.type.raw(), target, element, count), attributes(expr)); return target; } - + private int generate(Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { String trueLabel = CodeUtils.freshLabel(); @@ -2032,8 +2034,8 @@ private int generate(Expr.FieldAccess expr, Environment environment, CodeForest. return target; } - private int generate(Expr.New expr, Environment environment, - CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { + private int generate(Expr.New expr, Environment environment, CodeForest.Block block, CodeForest forest, + Context context) throws ResolveError { int operand = generate(expr.expr, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); block.add(Codes.NewObject(expr.type.raw(), target, operand)); @@ -2054,8 +2056,7 @@ private int[] generate(List arguments, Environment environment, CodeForest // Helpers // ========================================================================= - private Codes.BinaryOperatorKind OP2BOP(Expr.BOp bop, - SyntacticElement elem, Context context) { + private Codes.BinaryOperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context context) { switch (bop) { case ADD: return Codes.BinaryOperatorKind.ADD; @@ -2084,8 +2085,7 @@ private Codes.BinaryOperatorKind OP2BOP(Expr.BOp bop, return null; } - private Codes.Comparator OP2COP(Expr.BOp bop, SyntacticElement elem, - Context context) { + private Codes.Comparator OP2COP(Expr.BOp bop, SyntacticElement elem, Context context) { switch (bop) { case EQ: return Codes.Comparator.EQ; @@ -2113,36 +2113,28 @@ private static Expr invert(Expr e) { Expr.BinOp nbop = null; switch (bop.op) { case AND: - nbop = new Expr.BinOp(Expr.BOp.OR, invert(bop.lhs), - invert(bop.rhs), e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.OR, invert(bop.lhs), invert(bop.rhs), e.attributes()); break; case OR: - nbop = new Expr.BinOp(Expr.BOp.AND, invert(bop.lhs), - invert(bop.rhs), e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.AND, invert(bop.lhs), invert(bop.rhs), e.attributes()); break; case EQ: - nbop = new Expr.BinOp(Expr.BOp.NEQ, bop.lhs, bop.rhs, - e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.NEQ, bop.lhs, bop.rhs, e.attributes()); break; case NEQ: - nbop = new Expr.BinOp(Expr.BOp.EQ, bop.lhs, bop.rhs, - e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.EQ, bop.lhs, bop.rhs, e.attributes()); break; case LT: - nbop = new Expr.BinOp(Expr.BOp.GTEQ, bop.lhs, bop.rhs, - e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.GTEQ, bop.lhs, bop.rhs, e.attributes()); break; case LTEQ: - nbop = new Expr.BinOp(Expr.BOp.GT, bop.lhs, bop.rhs, - e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.GT, bop.lhs, bop.rhs, e.attributes()); break; case GT: - nbop = new Expr.BinOp(Expr.BOp.LTEQ, bop.lhs, bop.rhs, - e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.LTEQ, bop.lhs, bop.rhs, e.attributes()); break; case GTEQ: - nbop = new Expr.BinOp(Expr.BOp.LT, bop.lhs, bop.rhs, - e.attributes()); + nbop = new Expr.BinOp(Expr.BOp.LT, bop.lhs, bop.rhs, e.attributes()); break; } if (nbop != null) { @@ -2161,60 +2153,74 @@ private static Expr invert(Expr e) { r.type = Nominal.T_BOOL; return r; } - + + /** + * This resets the environment so that all variable names refer to those + * variables indicated in the given list of declared registers. This flushes + * out any unwanted assignments of variable names to temporary registers + * which can occur when we have multiple variables with the same name. + * + * @param environment + * @param declarations + * @return + */ + public Environment resetEnvironment(Environment environment, List declarations) { + Environment nenv = new Environment(); + for(int i=0;i!=declarations.size();++i) { + CodeForest.Register reg = declarations.get(i); + nenv.allocate(reg.type(),reg.name()); + } + for(int i=declarations.size();i block, - List declarations, + public void buildVariableDeclarations(List block, List declarations, Environment environment, WhileyFile.Context context) { // for (int i = 0; i != block.size(); ++i) { - buildVariableDeclarations(block.get(i), declarations, environment, - context); + buildVariableDeclarations(block.get(i), declarations, environment, context); } } - public void buildVariableDeclarations(Stmt stmt, - List declarations, Environment environment, + public void buildVariableDeclarations(Stmt stmt, List declarations, Environment environment, WhileyFile.Context context) { - if (stmt instanceof Assign || stmt instanceof Assert - || stmt instanceof Assume || stmt instanceof Return - || stmt instanceof Debug || stmt instanceof Fail - || stmt instanceof Break || stmt instanceof Continue - || stmt instanceof Expr.MethodCall - || stmt instanceof Expr.IndirectMethodCall - || stmt instanceof Expr.FunctionCall - || stmt instanceof Expr.IndirectFunctionCall + if (stmt instanceof Assign || stmt instanceof Assert || stmt instanceof Assume || stmt instanceof Return + || stmt instanceof Debug || stmt instanceof Fail || stmt instanceof Break || stmt instanceof Continue + || stmt instanceof Expr.MethodCall || stmt instanceof Expr.IndirectMethodCall + || stmt instanceof Expr.FunctionCall || stmt instanceof Expr.IndirectFunctionCall || stmt instanceof Expr.New || stmt instanceof Skip) { // Don't need to do anything in these cases. return; } else if (stmt instanceof VariableDeclaration) { VariableDeclaration d = (VariableDeclaration) stmt; - declarations.add(new CodeForest.Register(d.type.nominal(),d.parameter.name)); - environment.allocate(d.type.raw(),d.parameter.name); + declarations.add(new CodeForest.Register(d.type.nominal(), d.parameter.name)); + environment.allocate(d.type.raw(), d.parameter.name); } else if (stmt instanceof IfElse) { IfElse s = (IfElse) stmt; buildVariableDeclarations(s.trueBranch, declarations, environment, context); buildVariableDeclarations(s.falseBranch, declarations, environment, context); } else if (stmt instanceof Switch) { Switch s = (Switch) stmt; - for(Stmt.Case c : s.cases) { - buildVariableDeclarations(c.stmts,declarations, environment, context); + for (Stmt.Case c : s.cases) { + buildVariableDeclarations(c.stmts, declarations, environment, context); } } else if (stmt instanceof While) { While s = (While) stmt; - buildVariableDeclarations(s.body,declarations, environment, context); + buildVariableDeclarations(s.body, declarations, environment, context); } else if (stmt instanceof DoWhile) { DoWhile s = (DoWhile) stmt; - buildVariableDeclarations(s.body,declarations, environment, context); + buildVariableDeclarations(s.body, declarations, environment, context); } else { // should be dead-code - WhileyFile.internalFailure("unknown statement: " - + stmt.getClass().getName(), context, stmt); + WhileyFile.internalFailure("unknown statement: " + stmt.getClass().getName(), context, stmt); } } @@ -2225,8 +2231,7 @@ public void buildVariableDeclarations(Stmt stmt, * @param elem * @return */ - private static List attributes( - SyntacticElement elem) { + private static List attributes(SyntacticElement elem) { ArrayList attrs = new ArrayList(); Attribute.Source s = elem.attribute(Attribute.Source.class); if (s != null) { @@ -2235,10 +2240,10 @@ private static List attributes( } return attrs; } - - public List toIntegerList(int...items) { + + public List toIntegerList(int... items) { ArrayList list = new ArrayList(); - for(int i=0;i!=items.length;++i) { + for (int i = 0; i != items.length; ++i) { list.add(items[i]); } return list; @@ -2305,7 +2310,7 @@ public void put(int idx, String v) { public ArrayList asRegisters() { ArrayList registers = new ArrayList(); - for(int i=0;i!=idx2type.size();++i) { + for (int i = 0; i != idx2type.size(); ++i) { Type t = idx2type.get(i); registers.add(new CodeForest.Register(t, get(i))); } diff --git a/modules/wyil/src/wyil/builders/VcBranch.java b/modules/wyil/src/wyil/builders/VcBranch.java index ddf31e5266..6571f283c5 100644 --- a/modules/wyil/src/wyil/builders/VcBranch.java +++ b/modules/wyil/src/wyil/builders/VcBranch.java @@ -148,13 +148,13 @@ public enum State { * --- Variable names to use as prefixes when generating register * names. If null, the default names are used instead. */ - public VcBranch(int numInputs, String[] prefixes) { + public VcBranch(int numInputs, CodeForest.Index pc, String[] prefixes) { int numSlots = numInputs; this.parents = new VcBranch[0]; this.environment = new Expr[numSlots]; this.versions = new int[numSlots]; this.constraints = null; - this.pc = new CodeForest.Index(CodeForest.Index.ROOT); + this.pc = pc; this.state = State.ACTIVE; if (prefixes == null) { diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java new file mode 100644 index 0000000000..852b489428 --- /dev/null +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -0,0 +1,421 @@ +package wyil.builders; + +import static wyil.util.ErrorMessages.errorMessage; +import static wyil.util.ErrorMessages.internalFailure; +import static wyil.util.ErrorMessages.syntaxError; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import wybs.lang.Builder; +import wycc.lang.Attribute; +import wycc.lang.NameID; +import wycc.lang.SyntaxError; +import wycc.lang.SyntaxError.InternalFailure; +import wycs.core.Value; +import wycs.syntax.Expr; +import wycs.syntax.SyntacticType; +import wyfs.lang.Path; +import wyfs.util.Trie; +import wyil.lang.Code; +import wyil.lang.CodeForest; +import wyil.lang.Codes; +import wyil.lang.Type; +import wyil.lang.WyilFile; +import wyil.util.ErrorMessages; + +public class VcExprGenerator { + private final String filename; + private final Builder builder; + private final VcUtils utils; + + public VcExprGenerator(String filename, Builder builder, VcUtils utils) { + this.filename = filename; + this.builder = builder; + this.utils = utils; + } + /** + * Dispatch transform over unit bytecodes. Each unit bytecode is guaranteed + * to continue afterwards, and not to fork any new branches. + * + * @param code + * The bytecode being transformed over. + * @param block + * The root block being iterated over. + * @param branch + * The branch on entry to the bytecode. + */ + public void transform(Code code, CodeForest forest, VcBranch branch) { + try { + if (code instanceof Codes.LengthOf) { + transformUnary(Expr.Unary.Op.LENGTHOF, (Codes.LengthOf) code, + branch, forest); + } else if (code instanceof Codes.BinaryOperator) { + Codes.BinaryOperator bc = (Codes.BinaryOperator) code; + transformBinary(binaryOperatorMap[bc.kind.ordinal()], bc, + branch, forest); + } else if (code instanceof Codes.ArrayGenerator) { + transform((Codes.ArrayGenerator) code, forest, branch); + } else if (code instanceof Codes.NewArray) { + transformNary(Expr.Nary.Op.ARRAY, (Codes.NewArray) code, branch, forest); + } else if (code instanceof Codes.NewRecord) { + transformNary(Expr.Nary.Op.TUPLE, (Codes.NewRecord) code, branch, forest); + } else if (code instanceof Codes.Convert) { + transform((Codes.Convert) code, forest, branch); + } else if (code instanceof Codes.Const) { + transform((Codes.Const) code, forest, branch); + } else if (code instanceof Codes.Debug) { + // skip + } else if (code instanceof Codes.FieldLoad) { + transform((Codes.FieldLoad) code, forest, branch); + } else if (code instanceof Codes.IndirectInvoke) { + transform((Codes.IndirectInvoke) code, forest, branch); + } else if (code instanceof Codes.Invoke) { + transform((Codes.Invoke) code, forest, branch); + } else if (code instanceof Codes.Invert) { + transform((Codes.Invert) code, forest, branch); + } else if (code instanceof Codes.Label) { + // skip + } else if (code instanceof Codes.IndexOf) { + transform((Codes.IndexOf) code, forest, branch); + } else if (code instanceof Codes.Move) { + transform((Codes.Move) code, forest, branch); + } else if (code instanceof Codes.Assign) { + transform((Codes.Assign) code, forest, branch); + } else if (code instanceof Codes.Update) { + transform((Codes.Update) code, forest, branch); + } else if (code instanceof Codes.UnaryOperator) { + transform((Codes.UnaryOperator) code, forest, branch); + } else if (code instanceof Codes.Dereference) { + transform((Codes.Dereference) code, forest, branch); + } else if (code instanceof Codes.Nop) { + // skip + } else if (code instanceof Codes.NewObject) { + transform((Codes.NewObject) code, forest, branch); + } else if (code instanceof Codes.Lambda) { + transform((Codes.Lambda) code, forest, branch); + } else { + internalFailure("unknown: " + code.getClass().getName(), filename, + forest.get(branch.pc()).attributes()); + } + } catch (InternalFailure e) { + throw e; + } catch (SyntaxError e) { + throw e; + } catch (Throwable e) { + internalFailure(e.getMessage(), filename, e, forest.get(branch.pc()).attributes()); + } + } + + protected void transform(Codes.Assign code, CodeForest forest, + VcBranch branch) { + for (int i = 0; i != code.operands().length; ++i) { + branch.write(code.target(i), branch.read(code.operand(i))); + } + } + + /** + * Maps binary bytecodes into expression opcodes. + */ + private static Expr.Binary.Op[] binaryOperatorMap = { Expr.Binary.Op.ADD, + Expr.Binary.Op.SUB, + Expr.Binary.Op.MUL, + Expr.Binary.Op.DIV, + Expr.Binary.Op.REM, + null, + null, // bitwise or + null, // bitwise xor + null, // bitwise and + null, // left shift + null // right shift + }; + + protected void transform(Codes.Convert code, CodeForest forest, VcBranch branch) { + Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()); + Expr result = branch.read(code.operand(0)); + SyntacticType type = utils.convert(code.result(), forest.get(branch.pc()).attributes()); + branch.write(code.target(0), new Expr.Cast(type, result, attributes)); + } + + protected void transform(Codes.Const code, CodeForest forest, VcBranch branch) { + Value val = utils.convert(code.constant, forest, branch); + branch.write(code.target(), new Expr.Constant(val, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + } + + protected void transform(Codes.Debug code, CodeForest forest, + VcBranch branch) { + // do nout + } + + protected void transform(Codes.Dereference code, CodeForest forest, VcBranch branch) { + branch.havoc(code.target(0)); + } + + protected void transform(Codes.FieldLoad code, CodeForest forest, VcBranch branch) { + ArrayList fields = new ArrayList(code.type(0).fields().keySet()); + Collections.sort(fields); + Expr src = branch.read(code.operand(0)); + Expr index = new Expr.Constant(Value.Integer(BigInteger.valueOf(fields.indexOf(code.field)))); + Expr result = new Expr.IndexOf(src, index, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())); + branch.write(code.target(0), result); + } + + protected void transform(Codes.IndirectInvoke code, + CodeForest forest, VcBranch branch) { + for(int target : code.targets()) { + branch.havoc(target); + } + } + + protected void transform(Codes.Invoke code, CodeForest forest, + VcBranch branch) throws Exception { + Collection attributes = forest.get(branch.pc()).attributes(); + Collection wyccAttributes = VcUtils.toWycsAttributes(attributes); + int[] code_operands = code.operands(); + int[] targets = code.targets(); + + if (targets.length > 0) { + // Need to assume the post-condition holds. + Expr[] operands = new Expr[code_operands.length]; + for (int i = 0; i != code_operands.length; ++i) { + operands[i] = branch.read(code_operands[i]); + } + Expr argument = operands.length == 1 ? operands[0] : new Expr.Nary( + Expr.Nary.Op.TUPLE, operands,wyccAttributes); + branch.write(code.targets()[0], new Expr.Invoke(code.name.name(), + code.name.module(), Collections.EMPTY_LIST, argument, + wyccAttributes)); + + // This is a potential fix for #488, although it doesn't work + // FIXME: needs to updated to handle multiple returns as well + if (utils.containsNominal(code.type(0).returns().get(0), attributes)) { + // This is required to handle the implicit constraints implied + // by a nominal type. See #488. + Expr nominalTest = new Expr.Is(branch.read(code.targets()[0]), + utils.convert(code.type(0).returns().get(0), attributes)); + branch.assume(nominalTest); + } + + // Here, we must find the name of the corresponding postcondition so + // that we can assume it. + int numPostconditions = countPostconditions(code.name, code.type(0), forest, branch); + + if (numPostconditions > 0) { + // To assume the post-condition holds after the method, we + // simply called the corresponding post-condition macros. + Expr[] arguments = new Expr[operands.length + targets.length]; + System.arraycopy(operands, 0, arguments, 0, operands.length); + for(int i=0;i!=targets.length;++i) { + arguments[operands.length+i] = branch.read(targets[i]); + } + String prefix = code.name.name() + "_ensures_"; + for (int i = 0; i != numPostconditions; ++i) { + Expr.Invoke macro = new Expr.Invoke(prefix + i, + code.name.module(), Collections.EMPTY_LIST, + new Expr.Nary(Expr.Nary.Op.TUPLE, arguments)); + branch.assume(macro); + } + } + } + } + + protected void transform(Codes.Invert code, CodeForest forest, VcBranch branch) { + branch.havoc(code.target(0)); + } + + protected void transform(Codes.IndexOf code, CodeForest forest, VcBranch branch) { + Expr src = branch.read(code.operand(0)); + Expr idx = branch.read(code.operand(1)); + branch.write(code.target(0), + new Expr.IndexOf(src, idx, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + } + + protected void transform(Codes.ArrayGenerator code, CodeForest forest, VcBranch branch) { + Collection wyilAttributes = forest.get(branch.pc()).attributes(); + Collection attributes = VcUtils.toWycsAttributes(wyilAttributes); + Expr element = branch.read(code.operand(0)); + Expr count = branch.read(code.operand(1)); + branch.havoc(code.target(0)); + Expr arg = new Expr.Nary(Expr.Nary.Op.TUPLE, new Expr[] { branch.read(code.target(0)), element, count }, + attributes); + ArrayList generics = new ArrayList(); + generics.add(utils.convert(code.type(0).element(),wyilAttributes)); + Expr.Invoke macro = new Expr.Invoke("generate", Trie.fromString("wycs/core/Array"), + generics, arg); + branch.assume(macro); + } + + protected void transform(Codes.Lambda code, CodeForest forest, VcBranch branch) { + // TODO: implement lambdas somehow? + branch.havoc(code.target(0)); + } + + protected void transform(Codes.Move code, VcBranch branch) { + branch.write(code.target(0), branch.read(code.operand(0))); + } + + protected void transform(Codes.NewObject code, CodeForest forest, VcBranch branch) { + branch.havoc(code.target(0)); + } + + protected void transform(Codes.Nop code, CodeForest forest, + VcBranch branch) { + // do nout + } + + protected void transform(Codes.UnaryOperator code, CodeForest forest, VcBranch branch) { + switch (code.kind) { + case NEG: + transformUnary(Expr.Unary.Op.NEG, code, branch, forest); + break; + default: + branch.havoc(code.target(0)); + } + } + + protected void transform(Codes.Update code, CodeForest forest, VcBranch branch) { + Expr result = branch.read(code.result()); + Expr oldSource = branch.read(code.target(0)); + Expr newSource = branch.havoc(code.target(0)); + updateHelper(code.iterator(), oldSource, newSource, result, branch, forest); + } + + protected void updateHelper(Iterator iter, Expr oldSource, Expr newSource, Expr result, VcBranch branch, + CodeForest forest) { + Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()); + if (!iter.hasNext()) { + branch.assume(new Expr.Binary(Expr.Binary.Op.EQ, newSource, result, attributes)); + } else { + Codes.LVal lv = iter.next(); + if (lv instanceof Codes.RecordLVal) { + Codes.RecordLVal rlv = (Codes.RecordLVal) lv; + ArrayList fields = new ArrayList(rlv.rawType().fields().keySet()); + Collections.sort(fields); + int index = fields.indexOf(rlv.field); + for (int i = 0; i != fields.size(); ++i) { + Expr j = new Expr.Constant(Value.Integer(BigInteger.valueOf(i))); + Expr oldS = new Expr.IndexOf(oldSource, j, attributes); + Expr newS = new Expr.IndexOf(newSource, j, attributes); + if (i != index) { + branch.assume(new Expr.Binary(Expr.Binary.Op.EQ, oldS, newS, attributes)); + } else { + updateHelper(iter, oldS, newS, result, branch, forest); + } + } + } else if (lv instanceof Codes.ArrayLVal) { + Codes.ArrayLVal rlv = (Codes.ArrayLVal) lv; + Expr index = branch.read(rlv.indexOperand); + Expr oldS = new Expr.IndexOf(oldSource, index, attributes); + Expr newS = new Expr.IndexOf(newSource, index, attributes); + updateHelper(iter, oldS, newS, result, branch, forest); + Expr arg = new Expr.Nary(Expr.Nary.Op.TUPLE, new Expr[] { oldSource, newSource, index }, attributes); + ArrayList generics = new ArrayList(); + generics.add(utils.convert(rlv.rawType().element(),Collections.EMPTY_LIST)); + Expr.Invoke macro = new Expr.Invoke("update", Trie.fromString("wycs/core/Array"), generics, arg); + branch.assume(macro); + } + } + } + + /** + * Transform an assignable unary bytecode using a given target operator. + * This must read the operand and then create the appropriate target + * expression. Finally, the result of the bytecode must be written back to + * the enclosing branch. + * + * @param operator + * --- The target operator + * @param code + * --- The bytecode being translated + * @param branch + * --- The enclosing branch + */ + protected void transformUnary(Expr.Unary.Op operator, Code.AbstractBytecode code, VcBranch branch, + CodeForest forest) { + Expr lhs = branch.read(code.operand(0)); + branch.write(code.target(0), new Expr.Unary(operator, lhs, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + } + + /** + * Transform an assignable binary bytecode using a given target operator. + * This must read both operands and then create the appropriate target + * expression. Finally, the result of the bytecode must be written back to + * the enclosing branch. + * + * @param operator + * --- The target operator + * @param code + * --- The bytecode being translated + * @param branch + * --- The enclosing branch + */ + protected void transformBinary(Expr.Binary.Op operator, Code.AbstractBytecode code, VcBranch branch, + CodeForest forest) { + Expr lhs = branch.read(code.operand(0)); + Expr rhs = branch.read(code.operand(1)); + + if (operator != null) { + branch.write(code.target(0), + new Expr.Binary(operator, lhs, rhs, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + } else { + // In this case, we have a binary operator which we don't know how + // to translate into WyCS. Therefore, we need to invalidate the + // target register to signal this. + branch.havoc(code.target(0)); + } + } + + /** + * Transform an assignable nary bytecode using a given target operator. This + * must read all operands and then create the appropriate target expression. + * Finally, the result of the bytecode must be written back to the enclosing + * branch. + * + * @param operator + * --- The target operator + * @param code + * --- The bytecode being translated + * @param branch + * --- The enclosing branch + */ + protected void transformNary(Expr.Nary.Op operator, Code.AbstractBytecode code, VcBranch branch, + CodeForest forest) { + int[] code_operands = code.operands(); + Expr[] vals = new Expr[code_operands.length]; + for (int i = 0; i != vals.length; ++i) { + vals[i] = branch.read(code_operands[i]); + } + branch.write(code.target(0), new Expr.Nary(operator, vals, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + } + + /** + * Find the postcondition associated with a given function or method. This + * maybe contained in the same file, or in a different file. This may + * require loading that file in memory to access this information. + * + * @param name + * --- Fully qualified name of function + * @param fun + * --- Type of fucntion. + * @param block + * --- Enclosing block (for debugging purposes). + * @param branch + * --- Enclosing branch (for debugging purposes). + * @return + * @throws Exception + */ + private int countPostconditions(NameID name, Type.FunctionOrMethod fun, + CodeForest forest, VcBranch branch) throws Exception { + Path.Entry e = builder.project().get(name.module(), WyilFile.ContentType); + if (e == null) { + syntaxError(errorMessage(ErrorMessages.RESOLUTION_ERROR, name.module().toString()), filename, + forest.get(branch.pc()).attributes()); + } + WyilFile m = e.read(); + WyilFile.FunctionOrMethod method = m.functionOrMethod(name.name(), fun); + return method.postconditions().length; + } +} diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java index ad12ed4c9f..5435884773 100644 --- a/modules/wyil/src/wyil/builders/VcGenerator.java +++ b/modules/wyil/src/wyil/builders/VcGenerator.java @@ -40,7 +40,6 @@ import wyil.builders.VcBranch.State; import wyil.lang.*; import wyil.lang.CodeForest.Index; -import wyil.util.AttributedCodeBlock; import wyil.util.ErrorMessages; import wyil.util.TypeExpander; import wycc.lang.Attribute; @@ -63,13 +62,15 @@ public class VcGenerator { private final Builder builder; private final TypeExpander expander; + private VcExprGenerator unitGen; + private VcUtils utils; private String filename; private WyalFile wyalFile; WyilFile.FunctionOrMethod method; public VcGenerator(Builder builder) { this.builder = builder; - this.expander = new TypeExpander(builder.project()); + this.expander = new TypeExpander(builder.project()); } // =============================================================================== @@ -78,6 +79,8 @@ public VcGenerator(Builder builder) { protected WyalFile transform(WyilFile wyilFile) { filename = wyilFile.filename(); + utils = new VcUtils(filename,builder,expander); + unitGen = new VcExprGenerator(filename,builder,utils); wyalFile = new WyalFile(wyilFile.id(), filename); addImports(); @@ -115,19 +118,19 @@ protected void transform(WyilFile.Constant decl, WyilFile wyilFile) { * @param wyilFile */ protected void transform(WyilFile.Type typeDecl, WyilFile wyilFile) { - AttributedCodeBlock body = typeDecl.invariant(); + CodeForest forest = typeDecl.invariant(); Expr invariant = null; // FIXME: get the register prefix! Expr.Variable var = new Expr.Variable("r0"); - if (body != null) { - VcBranch master = new VcBranch(Math.max(1, body.numSlots()), null); + if (forest.numBlocks() > 0) { + CodeForest.Index root = new CodeForest.Index(forest.getRoot(0), 0); + VcBranch master = new VcBranch(Math.max(1, forest.numRegisters()), root, null); master.write(0, var); // Pass the given branch through the type invariant, producing // exactly one exit branch from which we can generate the invariant // expression. Type[] environment = new Type[] { typeDecl.type() }; - List exitBranches = transform(master, - CodeForest.Index.ROOT, true, environment, body); + List exitBranches = transform(master, root, true, environment, forest); // At this point, we are guaranteed exactly one exit branch because // there is only ever one exit point from an invariant. for (VcBranch exitBranch : exitBranches) { @@ -141,11 +144,10 @@ protected void transform(WyilFile.Type typeDecl, WyilFile wyilFile) { // existential quantifier. } - TypePattern.Leaf pattern = new TypePattern.Leaf(convert( - typeDecl.type(), Collections.EMPTY_LIST), var); + TypePattern.Leaf pattern = new TypePattern.Leaf(utils.convert(typeDecl.type(), Collections.EMPTY_LIST), var); wyalFile.add(wyalFile.new Type(typeDecl.name(), Collections.EMPTY_LIST, - pattern, invariant, toWycsAttributes(typeDecl.attributes()))); + pattern, invariant, VcUtils.toWycsAttributes(typeDecl.attributes()))); } protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { @@ -153,28 +155,27 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { this.method = method; Type.FunctionOrMethod fmm = method.type(); - AttributedCodeBlock body = method.body(); - List precondition = method.precondition(); - List postcondition = method.postcondition(); - VariableDeclarations rds = method.attribute(VariableDeclarations.class); + CodeForest forest = method.code(); + int[] preconditions = method.preconditions(); + int[] postconditions = method.postconditions(); // First, translate pre- and post-conditions into macro blocks. These // can then be used in various places to assume or enforce pre / // post-conditions. For example, when ensure a pre-condition is met at // an invocation site, we can call this macro directly. String prefix = method.name() + "_requires_"; - for (int i = 0; i != precondition.size(); ++i) { - buildMacroBlock(prefix + i, CodeForest.Index.ROOT, - precondition.get(i), fmm.params(), true); + for (int i = 0; i != preconditions.length; ++i) { + CodeForest.Index pc = new CodeForest.Index(preconditions[i], 0); + buildMacroBlock(prefix + i, pc, forest, fmm.params(), true); } prefix = method.name() + "_ensures_"; List postEnvironment = append(fmm.params(), fmm.returns()); - for (int i = 0; i != postcondition.size(); ++i) { - buildMacroBlock(prefix + i, CodeForest.Index.ROOT, - postcondition.get(i), postEnvironment, true); + for (int i = 0; i != postconditions.length; ++i) { + CodeForest.Index pc = new CodeForest.Index(postconditions[i], 0); + buildMacroBlock(prefix + i, pc, forest, postEnvironment, true); } // Finally, add a function representing this function or method. - buildFunctionBlock(method.name(), fmm.params(), fmm.returns()); + utils.createFunctionPrototype(wyalFile, method.name(), fmm.params(), fmm.returns()); if (method.hasModifier(Modifier.NATIVE)) { // We don't consider native methods because they have empty bodies, @@ -183,15 +184,15 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { return; } - Pair registerInfo = parseRegisterDeclarations(rds); + Pair registerInfo = VcUtils.parseRegisterDeclarations(forest); String[] prefixes = registerInfo.first(); Type[] bodyEnvironment = registerInfo.second(); // Construct the master branch and initialise all parameters with their // declared types in the master branch. The master branch needs to have // at least as many slots as there are parameters, though may require // more if the body uses them. - VcBranch master = new VcBranch(Math.max(body.numSlots(), fmm.params() - .size()), prefixes); + CodeForest.Index pc = new CodeForest.Index(method.body(), 0); + VcBranch master = new VcBranch(Math.max(forest.numRegisters(), fmm.params().size()), pc, prefixes); Expr[] arguments = new Expr[fmm.params().size()]; for (int i = 0; i != fmm.params().size(); ++i) { @@ -203,7 +204,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // Second, assume all preconditions. To do this, we simply invoke the // precondition macro for each one. prefix = method.name() + "_requires_"; - for (int i = 0; i != precondition.size(); ++i) { + for (int i = 0; i != preconditions.length; ++i) { Expr arg = arguments.length == 1 ? arguments[0] : new Expr.Nary( Expr.Nary.Op.TUPLE, arguments); Expr.Invoke macro = new Expr.Invoke(prefix + i, wyilFile.id(), @@ -216,8 +217,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // active. Terminated branches are those which have reached a return // statement, whilst failed branches are those which have reached a fail // statement. - List exitBranches = transform(master, CodeForest.Index.ROOT, - false, bodyEnvironment, body); + List exitBranches = transform(master, pc, false, bodyEnvironment, forest); // Examine all branches produced from the body. Each should be in one of // two states: terminated or failed. Failed states indicate some @@ -232,11 +232,10 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // verification condition which enforces that the constraints // leading up to this position cannot hold. In other words, that // this is an unreachable path. - Expr vc = buildVerificationCondition( - new Expr.Constant(Value.Bool(false)), branch, - bodyEnvironment, body); + Expr vc = buildVerificationCondition(new Expr.Constant(Value.Bool(false)), branch, bodyEnvironment, + forest); wyalFile.add(wyalFile.new Assert("assertion failed", vc, - toWycsAttributes(body.attributes(branch.pc())))); + VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); break; } case TERMINATED: { @@ -244,26 +243,26 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // In this case, there is not return value and, hence, there // is no need to ensure the postcondition holds. } else { - List attributes = body.attributes(branch.pc()); - Collection wycsAttributes = toWycsAttributes(attributes); + List attributes = forest.get(branch.pc()).attributes(); + Collection wycsAttributes = VcUtils.toWycsAttributes(attributes); // Find the return statement in question - Codes.Return ret = (Codes.Return) body.get(branch.pc()); + Codes.Return ret = (Codes.Return) forest.get(branch.pc()).code(); // Construct verification check to ensure that return // type invariant holds // FIXME: need proper support for multiple returns Expr returnedOperand = branch.read(ret.operand(0)); Type rawType = expand(bodyEnvironment[ret.operand(0)],attributes); Expr rawTest = new Expr.Is(returnedOperand, - convert(rawType, attributes)); + utils.convert(rawType, attributes)); // FIXME: needs to handle all returns - if (containsNominal(fmm.returns().get(0),attributes)) { + if (utils.containsNominal(fmm.returns().get(0),attributes)) { // FIXME: we need the raw test here, because the // verifier can't work out the type of the expression // otherwise. Expr nominalTest = new Expr.Is(returnedOperand, - convert(fmm.returns().get(0), attributes)); + utils.convert(fmm.returns().get(0), attributes)); Expr vc = buildVerificationCondition(nominalTest, - branch, bodyEnvironment, body, rawTest); + branch, bodyEnvironment, forest, rawTest); // FIXME: add contextual information here wyalFile.add(wyalFile.new Assert( "return type invariant not satisfied", vc, @@ -284,25 +283,21 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { // more detailed context information in the case of a // failure about which post-condition is failing. prefix = method.name() + "_ensures_"; - for (int i = 0; i != postcondition.size(); ++i) { - Expr arg = arguments.length == 1 ? arguments[0] - : new Expr.Nary(Expr.Nary.Op.TUPLE, arguments); - Expr.Invoke macro = new Expr.Invoke(prefix + i, - wyilFile.id(), Collections.EMPTY_LIST, arg); - Expr vc = buildVerificationCondition(macro, branch, - bodyEnvironment, body, rawTest); + for (int i = 0; i != postconditions.length; ++i) { + Expr arg = arguments.length == 1 ? arguments[0] : new Expr.Nary(Expr.Nary.Op.TUPLE, arguments); + Expr.Invoke macro = new Expr.Invoke(prefix + i, wyilFile.id(), Collections.EMPTY_LIST, arg); + Expr vc = buildVerificationCondition(macro, branch, bodyEnvironment, forest, rawTest); // FIXME: add contextual information here - wyalFile.add(wyalFile.new Assert( - "postcondition not satisfied", vc, - toWycsAttributes(body.attributes(branch.pc())))); + wyalFile.add(wyalFile.new Assert("postcondition not satisfied", vc, + VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); } } break; } default: // This should be impossible to reach! - internalFailure("unreachable code reached! (" + branch.state() - + ")", filename, body.attributes(branch.pc())); + internalFailure("unreachable code reached! (" + branch.state() + ")", filename, + forest.get(branch.pc()).attributes()); } } } @@ -324,12 +319,12 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { * * @return List of branches which reach the end of the block. */ - public List transform(VcBranch branch, CodeForest.Index root, - boolean isInvariant, Type[] environment, AttributedCodeBlock block) { + public List transform(VcBranch branch, CodeForest.Index root, boolean isInvariant, Type[] environment, + CodeForest forest) { // Construct the label map which is needed for conditional branches - Map labels = CodeUtils.buildLabelMap(block); - Pair> p = transform(root, 0, branch, false, isInvariant, - environment, labels, block); + Map labels = CodeUtils.buildLabelMap(forest); + Pair> p = transform(root.block(), 0, null, branch, false, isInvariant, environment, + labels, forest); // Ok, return list of exit branches return p.second(); } @@ -380,12 +375,12 @@ public List transform(VcBranch branch, CodeForest.Index root, * this is marked as terminated. * */ - protected Pair> transform(CodeForest.Index parent, - int offset, VcBranch entryState, boolean breakOnInvariant, boolean isInvariant, - Type[] environment, Map labels, AttributedCodeBlock block) { + protected Pair> transform(int block, int offset, CodeForest.Index parent, + VcBranch entryState, boolean breakOnInvariant, boolean isInvariant, Type[] environment, + Map labels, CodeForest forest) { // Move state to correct location - CodeForest.Index start = new CodeForest.Index(parent); - entryState.goTo(new CodeForest.Index(parent, offset)); + entryState.goTo(new CodeForest.Index(block, offset)); + CodeForest.Block blk = forest.get(block); // Construct list of branches being processed. Stack worklist = new Stack(); ArrayList exitBranches = new ArrayList(); @@ -399,36 +394,34 @@ protected Pair> transform(CodeForest.Index parent, // The program counter represents the current position of the branch // being explored. CodeForest.Index pc = branch.pc(); - // Determine whether to continue executing this branch, or whether // it has completed within this scope. - if (!pc.isWithin(parent) || branch.state() != VcBranch.State.ACTIVE) { + if (pc.block() != block || branch.state() != VcBranch.State.ACTIVE) { // This indicates the given branch has either exited this block // via a non-local branch, or terminated in some fashion. // Therefore, this branch is moved into the list of exit // branches. exitBranches.add(branch); - } else if (!block.contains(pc)) { + } else if (pc.block() == block && pc.offset() >= blk.size()) { // This indicates the given branch has exited this block by // falling through. We now need to check for breakOnInvariant. if (breakOnInvariant) { // Break On Invariant is enabled, therefore continue going // around. - branch.goTo(start); + branch.goTo(parent); } else if (parent != null) { // No reset, allow to exit branch as normal. First, set // branch location to the next instruction following the // parent instruction containing this block. branch.goTo(parent.next()); } else { - internalFailure("unreachable code reached!", filename, - block.attributes(pc)); + internalFailure("unreachable code reached!", filename, forest.get(pc).attributes()); } fallThruBranches.add(branch); } else { // Continue executing this branch as it is still within the // scope of this block. - Code code = block.get(pc); + Code code = forest.get(pc).code(); // Now, dispatch statements. Control statements are treated // specially from unit statements. if (code instanceof Codes.AssertOrAssume) { @@ -441,9 +434,8 @@ protected Pair> transform(CodeForest.Index parent, fallThruBranches.add(branch); } else { boolean isAssert = code instanceof Codes.Assert; - Pair> p = transform( - (Codes.AssertOrAssume) code, isAssert, branch, - environment, labels, block); + Pair> p = transform((Codes.AssertOrAssume) code, isAssert, branch, + environment, labels, forest); if(p.first() != null) { worklist.add(p.first()); } @@ -452,41 +444,39 @@ protected Pair> transform(CodeForest.Index parent, } else if (code instanceof Codes.If || code instanceof Codes.IfIs || code instanceof Codes.Switch - || code instanceof Code.Compound) { + || code instanceof Code.AbstractCompoundBytecode) { List bs; if (code instanceof Codes.If) { - bs = transform((Codes.If) code, branch, labels, block); + bs = transform((Codes.If) code, branch, labels, forest); } else if (code instanceof Codes.IfIs) { - bs = transform((Codes.IfIs) code, branch, labels, block); + bs = transform((Codes.IfIs) code, branch, labels, forest); } else if (code instanceof Codes.Switch) { bs = transform((Codes.Switch) code, branch, labels, - block); + forest); } else if (code instanceof Codes.Quantify) { bs = transform((Codes.Quantify) code, branch, - isInvariant, environment, labels, block); + isInvariant, environment, labels, forest); } else { bs = transform((Codes.Loop) code, branch, environment, - labels, block); + labels, forest); } worklist.addAll(bs); } else if (code instanceof Codes.Goto) { - transform((Codes.Goto) code, branch, labels, block); + transform((Codes.Goto) code, branch, labels, forest); worklist.push(branch); } else if (code instanceof Codes.Return) { transform((Codes.Return) code, branch); exitBranches.add(branch); } else if (code instanceof Codes.Fail) { - transform((Codes.Fail) code, branch, block); + transform((Codes.Fail) code, branch, forest); exitBranches.add(branch); } else { // Unit statement. First, check whether or not there are any // preconditions for this statement and, if so, add // appropriate verification conditions to enforce them. - Code.Unit unit = (Code.Unit) code; if (!isInvariant) { - Pair[] preconditions = getPreconditions( - unit, branch, environment, block); + Pair[] preconditions = utils.getPreconditions(code, branch, environment, forest); if (preconditions.length > 0) { // This bytecode has one or more preconditions which // need to be asserted. Therefore, for each, create @@ -495,10 +485,9 @@ protected Pair> transform(CodeForest.Index parent, for (int i = 0; i != preconditions.length; ++i) { Pair p = preconditions[i]; Expr vc = buildVerificationCondition( - p.second(), branch, environment, block); + p.second(), branch, environment, forest); wyalFile.add(wyalFile.new Assert(p.first(), vc, - toWycsAttributes(block - .attributes(branch.pc())))); + VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); } // We need to fork the branch here, because it must // have @@ -508,13 +497,13 @@ protected Pair> transform(CodeForest.Index parent, } } // - transform(unit, block, branch); + unitGen.transform(code, forest, branch); branch.goTo(pc.next()); worklist.push(branch); } } } - + joinAll(exitBranches); joinAll(fallThruBranches); VcBranch fallThru = null; @@ -523,8 +512,7 @@ protected Pair> transform(CodeForest.Index parent, fallThru = fallThruBranches.get(0); } else if (fallThruBranches.size() > 1) { // Should be unreachable. Sanity check for now. - internalFailure("unreacahble code reached", filename, - block.attributes(parent)); + internalFailure("unreacahble code reached", filename, forest.get(parent).attributes()); } return new Pair>(fallThru, exitBranches); @@ -593,219 +581,7 @@ private void joinAll(ArrayList branches) { // Done. } - /** - * Generate verification conditions to enforce the necessary preconditions - * for a given bytecode. For example, to protect against division by zero or - * an out-of-bounds access. - * - * @param code - * @param branch - * @param branches - * @param block - */ - public Pair[] getPreconditions(Code.Unit code, VcBranch branch, - Type[] environment, AttributedCodeBlock block) { - // - try { - switch (code.opcode()) { - case Code.OPCODE_div: - case Code.OPCODE_rem: - return divideByZeroCheck((Codes.BinaryOperator) code, branch); - case Code.OPCODE_indexof: - return indexOutOfBoundsChecks((Codes.IndexOf) code, branch); - case Code.OPCODE_listgen: - return arrayGeneratorChecks((Codes.ArrayGenerator) code, branch); - case Code.OPCODE_update: - return updateChecks((Codes.Update) code, branch); - case Code.OPCODE_invokefn: - case Code.OPCODE_invokemd: - return preconditionCheck((Codes.Invoke) code, branch, environment, block); - } - return new Pair[0]; - } catch (Exception e) { - internalFailure(e.getMessage(), filename, e); - return null; // deadcode - } - } - - /** - * Generate preconditions to protected against a possible divide by zero. - * This essentially boils down to ensureing the divisor is non-zero. - * - * @param binOp - * --- The division or remainder bytecode - * @param branch - * --- The branch the division is on. - * @return - */ - public Pair[] divideByZeroCheck(Codes.BinaryOperator binOp, VcBranch branch) { - Expr rhs = branch.read(binOp.operand(1)); - Value zero; - if (binOp.type(0) instanceof Type.Int) { - zero = Value.Integer(BigInteger.ZERO); - } else { - zero = Value.Decimal(BigDecimal.ZERO); - } - Expr.Constant constant = new Expr.Constant(zero, rhs.attributes()); - return new Pair[] { - new Pair("division by zero", new Expr.Binary(Expr.Binary.Op.NEQ, rhs, constant, rhs.attributes())) }; - } - - /** - * Generate preconditions necessary to protect against an out-of-bounds - * access. For lists, this means ensuring the index is non-negative and less - * than the list length. - * - * @param code - * --- The indexOf bytecode - * @param branch - * --- The branch the bytecode is on. - * @return - */ - public Pair[] indexOutOfBoundsChecks(Codes.IndexOf code, VcBranch branch) { - if (code.type(0) instanceof Type.EffectiveArray) { - Expr src = branch.read(code.operand(0)); - Expr idx = branch.read(code.operand(1)); - Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), - idx.attributes()); - Expr length = new Expr.Unary(Expr.Unary.Op.LENGTHOF, src, - idx.attributes()); - return new Pair[] { - new Pair("index out of bounds (negative)", new Expr.Binary( - Expr.Binary.Op.GTEQ, idx, zero, idx.attributes())), - new Pair("index out of bounds (not less than length)", - new Expr.Binary(Expr.Binary.Op.LT, idx, length, - idx.attributes())), }; - } else { - // FIXME: should do something here! At a minimum, generate a warning - // that this has not been implemented yet. - return new Pair[0]; - } - } - - /** - * Generate preconditions necessary to protect against a negative array - * size. - * - * @param code - * --- The array generator bytecode - * @param branch - * --- The branch the bytecode is on. - * @return - */ - public Pair[] arrayGeneratorChecks(Codes.ArrayGenerator code, VcBranch branch) { - Expr idx = branch.read(code.operand(1)); - Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), - idx.attributes()); - return new Pair[] { - new Pair("negative length possible", new Expr.Binary( - Expr.Binary.Op.GTEQ, idx, zero, idx.attributes())) - }; - } - /** - * Generate preconditions necessary to ensure the preconditions for a method - * or method invocation are met. - * - * @param code - * --- The invoke bytecode - * @param branch - * --- The branch on which the invocation is on. - * @param block - * --- The containing block of code. - * @return - * @throws Exception - */ - public Pair[] preconditionCheck(Codes.Invoke code, VcBranch branch, - Type[] environment, AttributedCodeBlock block) throws Exception { - ArrayList> preconditions = new ArrayList<>(); - // - // First, check for any potentially constrained types. - // - List attributes = block.attributes(branch.pc()); - List code_type_params = code.type(0).params(); - int[] code_operands = code.operands(); - for (int i = 0; i != code_operands.length; ++i) { - Type t = code_type_params.get(i); - if (containsNominal(t, attributes)) { - int operand = code_operands[i]; - Type rawType = expand(environment[operand], attributes); - Expr rawTest = new Expr.Is(branch.read(operand), convert(rawType, attributes)); - Expr nominalTest = new Expr.Is(branch.read(operand), convert(t, attributes)); - preconditions.add(new Pair("type invariant not satisfied (argument " + i + ")", - new Expr.Binary(Expr.Binary.Op.IMPLIES, rawTest, nominalTest))); - } - } - // - List requires = findPrecondition(code.name, code.type(0), block, branch); - // - if (requires.size() > 0) { - // First, read out the operands from the branch - Expr[] operands = new Expr[code_operands.length]; - for (int i = 0; i != code_operands.length; ++i) { - operands[i] = branch.read(code_operands[i]); - } - // To check the pre-condition holds after the method, we - // simply called the corresponding pre-condition macros. - String prefix = code.name.name() + "_requires_"; - - Expr argument = operands.length == 1 ? operands[0] : new Expr.Nary(Expr.Nary.Op.TUPLE, operands); - for (int i = 0; i != requires.size(); ++i) { - Expr precondition = new Expr.Invoke(prefix + i, code.name.module(), Collections.EMPTY_LIST, argument); - preconditions.add(new Pair("precondition not satisfied", precondition)); - - } - } - return preconditions.toArray(new Pair[preconditions.size()]); - } - - /** - * Ensure all preconditions for an update bytecode are met. For example, - * that any list updates are within bounds, etc. - * - * @param code - * --- The update bytecode. - * @param branch - * --- The branch containing the update bytecode. - * @return - */ - public Pair[] updateChecks(Codes.Update code, VcBranch branch) { - ArrayList> preconditions = new ArrayList>(); - - Expr src = branch.read(code.target(0)); - - for (Codes.LVal lval : code) { - if (lval instanceof Codes.ArrayLVal) { - Codes.ArrayLVal lv = (Codes.ArrayLVal) lval; - Expr idx = branch.read(lv.indexOperand); - Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), - idx.attributes()); - Expr length = new Expr.Unary(Expr.Unary.Op.LENGTHOF, src, - idx.attributes()); - preconditions.add(new Pair("index out of bounds (negative)", - new Expr.Binary(Expr.Binary.Op.GTEQ, idx, zero, idx - .attributes()))); - preconditions.add(new Pair( - "index out of bounds (not less than length)", - new Expr.Binary(Expr.Binary.Op.LT, idx, length, idx - .attributes()))); - src = new Expr.IndexOf(src, idx); - } else if (lval instanceof Codes.RecordLVal) { - Codes.RecordLVal lv = (Codes.RecordLVal) lval; - ArrayList fields = new ArrayList(lv.rawType() - .fields().keySet()); - Collections.sort(fields); - Expr index = new Expr.Constant(Value.Integer(BigInteger - .valueOf(fields.indexOf(lv.field)))); - src = new Expr.IndexOf(src, index); - } else { - // FIXME: need to implement dereference operations. - } - } - - return preconditions.toArray(new Pair[preconditions.size()]); - } - // =============================================================================== // Control Bytecodes // =============================================================================== @@ -841,9 +617,8 @@ public Pair[] updateChecks(Codes.Update code, VcBranch branch) { */ protected List transform(Codes.Loop code, VcBranch branch, Type[] environment, Map labels, - AttributedCodeBlock block) { - return transformLoopHelper(code, branch, environment, labels, block) - .second(); + CodeForest forest) { + return transformLoopHelper(code, branch, environment, labels, forest).second(); } /** @@ -876,17 +651,17 @@ protected List transform(Codes.Loop code, VcBranch branch, */ protected List transform(Codes.Quantify code, VcBranch branch, boolean isInvariant, Type[] environment, - Map labels, AttributedCodeBlock block) { + Map labels, CodeForest forest) { // Write an arbitrary value to the index operand. This is necessary to // ensure that there is something there if it is used within the loop // body. - branch.havoc(code.indexOperand); + branch.havoc(code.indexOperand()); // VcBranch original = branch.fork(); branch = branch.fork(); // This represents a quantifier looop Pair> p = transformQuantifierHelper(code, - branch, isInvariant, environment, labels, block); + branch, isInvariant, environment, labels, forest); return extractQuantifiers(code, original, p.first(), p.second()); } @@ -906,15 +681,15 @@ protected List transform(Codes.Quantify code, VcBranch branch, protected List extractQuantifiers(Codes.Quantify code, VcBranch root, VcBranch fallThru, List exitBranches) { // First, setup some helper variables for use in the remainder. - SyntacticType elementType = convert(Type.T_INT, + SyntacticType elementType = utils.convert(Type.T_INT, Collections.EMPTY_LIST); - Expr index = root.read(code.indexOperand); + Expr index = root.read(code.indexOperand()); TypePattern pattern = new TypePattern.Leaf(elementType, (Expr.Variable) index); Expr lowerBound = new Expr.Binary(Expr.Binary.Op.LTEQ, - root.read(code.startOperand), index); + root.read(code.startOperand()), index); Expr upperBound = new Expr.Binary(Expr.Binary.Op.LT, index, - root.read(code.endOperand)); + root.read(code.endOperand())); Expr range = new Expr.Binary(Expr.Binary.Op.AND, lowerBound, upperBound); ArrayList qBranches = new ArrayList(); // Second, deal with the universally quantified fall-thru branch. We @@ -962,20 +737,19 @@ protected List extractQuantifiers(Codes.Quantify code, protected Pair> transformQuantifierHelper( Codes.Loop code, VcBranch branch, boolean isInvariant, Type[] environment, Map labels, - AttributedCodeBlock block) { + CodeForest forest) { // The loopPc gives the block index of the loop bytecode. CodeForest.Index loopPc = branch.pc(); // This is the easy case, as there is no loop invariant. Therefore, // we just havoc modified variables at the beginning and then allow // branches to exit the loop as normal. Branches which reach the end // of the loop body are returned to be universally quantified - havocVariables(code.modifiedOperands, branch); + havocVariables(code.modifiedOperands(), branch); VcBranch activeBranch = branch.fork(); // Now, run through loop body. This will produce several kinds of // branch. Those which have terminated or branched out of the loop body, // and those which have reached the end of the loop body. ). - return transform(loopPc, 0, activeBranch, false, isInvariant, - environment, labels, block); + return transform(code.block(),0,loopPc, activeBranch, false, isInvariant, environment, labels, forest); } /** @@ -999,30 +773,28 @@ protected Pair> transformQuantifierHelper( * location information. * @return */ - protected Pair> transformLoopHelper( - Codes.Loop code, VcBranch branch, Type[] environment, - Map labels, AttributedCodeBlock block) { + protected Pair> transformLoopHelper(Codes.Loop code, VcBranch branch, Type[] environment, + Map labels, CodeForest forest) { // The loopPc gives the block index of the loop bytecode. CodeForest.Index loopPc = branch.pc(); - int invariantOffset = getInvariantOffset(code); + int invariantOffset = getInvariantOffset(code,forest); // First thing we need to do is determine whether or not this loop has a // loop invariant, as this affects how we will approach it. if (invariantOffset == -1) { - return transformLoopWithoutInvariant(code, branch, environment, - labels, block); + return transformLoopWithoutInvariant(code, branch, environment, labels, forest); } else { // Determine how many invariant blocks there are, as there might be // more than one. In the case that there is more than one, they are // assumed to be arranged consecutively one after the other. + CodeForest.Block block = forest.get(code.block()); int numberOfInvariants = 0; - for (int i = invariantOffset; i < code.size() - && code.get(i) instanceof Codes.Invariant; ++i) { + for (int i = invariantOffset; i < block.size() + && block.get(i).first() instanceof Codes.Invariant; ++i) { numberOfInvariants = numberOfInvariants+1; } // - CodeForest.Index firstInvariantPc = new CodeForest.Index(loopPc, - invariantOffset); + CodeForest.Index firstInvariantPc = new CodeForest.Index(code.block(), invariantOffset); String invariantMacroPrefix = method.name() + "_loopinvariant_"; // FIXME: this is a hack to determine which variables should be @@ -1040,15 +812,14 @@ protected Pair> transformLoopHelper( } // *** END *** for(int i=0;i!=numberOfInvariants;++i) { - buildInvariantMacro(firstInvariantPc.next(i), variables, environment, block); + buildInvariantMacro(firstInvariantPc.next(i), variables, environment, forest); } // This is the harder case as we must account for the loop invariant // properly. To do this, we allow the loop to execute upto the loop // invariant using the current branch state. At this point, we havoc // modified variables and then assume the loop invariant, before // running through the loop until the invariant is reached again. - Pair> p = transform(loopPc, 0, branch, - true, false, environment, labels, block); + Pair> p = transform(code.block(), 0, loopPc, branch, true, false, environment, labels, forest); // At this point, any branch which has terminated or branched out of // the loop represents a true execution path. Any branch which has // failed corresponds to ensuring the loop invariant on entry. @@ -1061,25 +832,21 @@ protected Pair> transformLoopHelper( // current branch state. for (int i = 0; i != numberOfInvariants; ++i) { CodeForest.Index invariantPc = firstInvariantPc.next(i); - String invariantMacroName = invariantMacroPrefix - + invariantPc.toString().replace(".", "_"); - Expr.Invoke invariant = buildInvariantCall(activeBranch, - invariantMacroName, variables); - Expr vc = buildVerificationCondition(invariant, activeBranch, - environment, block); - wyalFile.add(wyalFile.new Assert( - "loop invariant does not hold on entry", vc, - toWycsAttributes(block.attributes(invariantPc)))); + String invariantMacroName = invariantMacroPrefix + invariantPc.toString().replace(":", "_"); + Expr.Invoke invariant = buildInvariantCall(activeBranch, invariantMacroName, variables); + Expr vc = buildVerificationCondition(invariant, activeBranch, environment, forest); + wyalFile.add(wyalFile.new Assert("loop invariant does not hold on entry", vc, + VcUtils.toWycsAttributes(forest.get(invariantPc).attributes()))); } // Assume invariant holds for inductive case. To this, we first // havoc all modified variables to ensure that information about // them is not carried forward from before the loop. Then, we assume // the invariant macro holds in the current branch state. - havocVariables(code.modifiedOperands, activeBranch); + havocVariables(code.modifiedOperands(), activeBranch); for (int i = 0; i != numberOfInvariants; ++i) { CodeForest.Index invariantPc = firstInvariantPc.next(i); String invariantMacroName = invariantMacroPrefix - + invariantPc.toString().replace(".", "_"); + + invariantPc.toString().replace(":", "_"); Expr.Invoke invariant = buildInvariantCall(activeBranch, invariantMacroName, variables); activeBranch.assume(invariant); @@ -1089,8 +856,8 @@ protected Pair> transformLoopHelper( // Branches which prematurely exit the loop are passed into the list // of exit branches. These are valid as they only have information // from the loop invariant. - p = transform(loopPc, invariantOffset + numberOfInvariants, - activeBranch, true, false, environment, labels, block); + p = transform(code.block(), invariantOffset + numberOfInvariants, loopPc, activeBranch, true, false, + environment, labels, forest); activeBranch = p.first(); exitBranches.addAll(p.second()); // Reestablish loop invariant. To do this, we generate a @@ -1099,13 +866,13 @@ protected Pair> transformLoopHelper( for (int i = 0; i != numberOfInvariants; ++i) { CodeForest.Index invariantPc = firstInvariantPc.next(i); String invariantMacroName = invariantMacroPrefix - + invariantPc.toString().replace(".", "_"); + + invariantPc.toString().replace(":", "_"); Expr.Invoke invariant = buildInvariantCall(activeBranch, invariantMacroName, variables); Expr vc = buildVerificationCondition(invariant, activeBranch, - environment, block); + environment, forest); wyalFile.add(wyalFile.new Assert("loop invariant not restored", - vc, toWycsAttributes(block.attributes(invariantPc)))); + vc, VcUtils.toWycsAttributes(forest.get(invariantPc).attributes()))); } // Reposition fall-through activeBranch.goTo(loopPc.next()); @@ -1135,14 +902,14 @@ protected Pair> transformLoopHelper( */ protected Pair> transformLoopWithoutInvariant( Codes.Loop code, VcBranch branch, Type[] environment, - Map labels, AttributedCodeBlock block) { + Map labels, CodeForest forest) { CodeForest.Index loopPc = branch.pc(); // This is the easy case, as there is no loop invariant. Therefore, // we just havoc modified variables at the beginning and then allow // branches to exit the loop as normal. Branches which reach the end // of the loop body can be discarded as they represent correct // execution through the loop. - havocVariables(code.modifiedOperands, branch); + havocVariables(code.modifiedOperands(), branch); VcBranch fallThru = branch.fork(); VcBranch activeBranch = branch.fork(); // Now, run through loop body. This will produce several kinds of @@ -1150,8 +917,8 @@ protected Pair> transformLoopWithoutInvariant( // and those which have reached the end of the loop body. All branches // in the former case go straight onto the list of returned branches. // Those in the latter case are discarded (as discussed above). - Pair> p = transform(loopPc, 0, activeBranch, - false, false, environment, labels, block); + Pair> p = transform(code.block(), 0, loopPc, activeBranch, false, false, environment, labels, + forest); fallThru.goTo(loopPc.next()); return new Pair>(fallThru, p.second()); } @@ -1175,7 +942,8 @@ protected Pair> transformLoopWithoutInvariant( * The enclosing code block */ private void buildInvariantMacro(CodeForest.Index invariantPC, - boolean[] variables, Type[] environment, AttributedCodeBlock block) { + boolean[] variables, Type[] environment, CodeForest forest) { + Codes.Invariant code = (Codes.Invariant) forest.get(invariantPC).first(); // FIXME: we don't need to include all variables, only those which are // "active". ArrayList types = new ArrayList(); @@ -1186,9 +954,9 @@ private void buildInvariantMacro(CodeForest.Index invariantPC, types.add(null); } } - String pc = invariantPC.toString().replace(".", "_"); - buildMacroBlock(method.name() + "_loopinvariant_" + pc, invariantPC, - block, types, true); + String pc = invariantPC.block() + "_" + invariantPC.offset(); + CodeForest.Index root = new CodeForest.Index(code.block(),0); + buildMacroBlock(method.name() + "_loopinvariant_" + pc, root, forest, types, true); } /** @@ -1228,9 +996,10 @@ protected Expr.Invoke buildInvariantCall(VcBranch branch, String name, * @param branch * @return */ - private int getInvariantOffset(Codes.Loop loop) { - for (int i = 0; i != loop.size(); ++i) { - if (loop.get(i) instanceof Codes.Invariant) { + private int getInvariantOffset(Codes.Loop loop, CodeForest forest) { + CodeForest.Block block = forest.get(loop.block()); + for (int i = 0; i != block.size(); ++i) { + if (block.get(i).first() instanceof Codes.Invariant) { return i; } } @@ -1281,17 +1050,17 @@ public void havocVariables(int[] variables, VcBranch branch) { * The list of branches currently being managed. */ protected List transform(Codes.If code, VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, CodeForest forest) { // First, clone and register the true branch VcBranch trueBranch = branch.fork(); VcBranch falseBranch = branch.fork(); // Second assume the condition on each branch - Expr.Binary trueTest = buildTest(code.op, code.operand(0), code.operand(1), code.type(0), block, trueBranch); + Expr.Binary trueTest = buildTest(code.op, code.operand(0), code.operand(1), code.type(0), forest, trueBranch); trueBranch.assume(trueTest); - falseBranch.assume(invert(trueTest)); + falseBranch.assume(utils.invert(trueTest)); // Third, dispatch branches to their targets falseBranch.goTo(branch.pc().next()); - trueBranch.goTo(labels.get(code.target)); + trueBranch.goTo(labels.get(code.destination())); // Finally, return the branches ArrayList exitBranches = new ArrayList(); exitBranches.add(trueBranch); @@ -1318,17 +1087,17 @@ protected List transform(Codes.If code, VcBranch branch, * The list of branches currently being managed. */ protected List transform(Codes.IfIs code, VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, CodeForest forest) { ArrayList exitBranches = new ArrayList(); // In this case, both branches are reachable. // First, clone and register the branch VcBranch falseBranch = branch.fork(); VcBranch trueBranch = branch.fork(); // Second add appropriate runtime type tests - List attributes = block.attributes(branch.pc()); - Collection wycsAttributes = toWycsAttributes(attributes); - SyntacticType trueType = convert(code.rightOperand, attributes); - SyntacticType falseType = new SyntacticType.Negation(convert(code.rightOperand, + List attributes = forest.get(branch.pc()).attributes(); + Collection wycsAttributes = VcUtils.toWycsAttributes(attributes); + SyntacticType trueType = utils.convert(code.rightOperand(), attributes); + SyntacticType falseType = new SyntacticType.Negation(utils.convert(code.rightOperand(), attributes), wycsAttributes); trueBranch.assume(new Expr.Is(branch.read(code.operand(0)), trueType, wycsAttributes)); @@ -1336,7 +1105,7 @@ protected List transform(Codes.IfIs code, VcBranch branch, wycsAttributes)); // Finally dispatch the branches falseBranch.goTo(branch.pc().next()); - trueBranch.goTo(labels.get(code.target)); + trueBranch.goTo(labels.get(code.destination())); // exitBranches.add(trueBranch); exitBranches.add(falseBranch); @@ -1359,7 +1128,7 @@ protected List transform(Codes.IfIs code, VcBranch branch, * The list of branches currently being managed. */ protected List transform(Codes.Switch code, VcBranch branch, - Map labels, AttributedCodeBlock block) { + Map labels, CodeForest forest) { ArrayList exitBranches = new ArrayList(); VcBranch defaultBranch = branch.fork(); @@ -1375,15 +1144,14 @@ protected List transform(Codes.Switch code, VcBranch branch, // Second, on the new branch we need assume that the variable being // switched on matches the given value. Expr src = branch.read(code.operand(0)); - Expr constant = new Expr.Constant( - convert(caseValue, block, branch), - toWycsAttributes(block.attributes(branch.pc()))); + Expr constant = new Expr.Constant(utils.convert(caseValue, forest, branch), + VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())); caseBranch.assume(new Expr.Binary(Expr.Binary.Op.EQ, src, constant, - toWycsAttributes(block.attributes(branch.pc())))); + VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); // Third, on the default branch we can assume that the variable // being switched is *not* the given value. defaultBranch.assume(new Expr.Binary(Expr.Binary.Op.NEQ, src, - constant, toWycsAttributes(block.attributes(branch.pc())))); + constant, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); // Finally, dispatch branch caseBranch.goTo(labels.get(code.branches.get(i).second())); exitBranches.add(caseBranch); @@ -1427,15 +1195,15 @@ protected List transform(Codes.Switch code, VcBranch branch, protected Pair> transform( Codes.AssertOrAssume code, boolean isAssert, VcBranch branch, Type[] environment, Map labels, - AttributedCodeBlock block) { + CodeForest forest) { int start = wyalFile.declarations().size(); // First, transform the given branch through the assert or assume block. // This will produce one or more exit branches, some of which may have // reached failed states and need to be turned into verification // conditions (for asserts only). CodeForest.Index pc = branch.pc(); - Pair> p = transform(pc, 0, branch, false, - true, environment, labels, block); + Pair> p = transform(code.block(), 0, pc, branch, false, true, environment, labels, + forest); List exitBranches = p.second(); // Second, examine the list of exit branches and decide what to do with // them. In the case of a failing branch then we need to generate an @@ -1481,8 +1249,8 @@ protected Pair> transform( * The list of branches currently being managed. */ protected void transform(Codes.Goto code, final VcBranch branch, - Map labels, AttributedCodeBlock block) { - branch.goTo(labels.get(code.target)); + Map labels, CodeForest forest) { + branch.goTo(labels.get(code.destination())); } /** @@ -1502,7 +1270,7 @@ protected void transform(Codes.Goto code, final VcBranch branch, * The list of branches currently being managed. */ protected void transform(Codes.Fail code, VcBranch branch, - AttributedCodeBlock block) { + CodeForest forest) { // Update status of this branch to failed. This simply indicates that // this branch's location should be unreachable and, hence, we need a // verification condition to enforce this. @@ -1531,448 +1299,8 @@ protected void transform(Codes.Return code, VcBranch branch) { branch.setState(VcBranch.State.TERMINATED); } - // =============================================================================== - // Unit Bytecodes - // =============================================================================== - - /** - * Dispatch transform over unit bytecodes. Each unit bytecode is guaranteed - * to continue afterwards, and not to fork any new branches. - * - * @param code - * The bytecode being transformed over. - * @param block - * The root block being iterated over. - * @param branch - * The branch on entry to the bytecode. - */ - protected void transform(Code.Unit code, AttributedCodeBlock block, - VcBranch branch) { - try { - if (code instanceof Codes.LengthOf) { - transformUnary(Expr.Unary.Op.LENGTHOF, (Codes.LengthOf) code, - branch, block); - } else if (code instanceof Codes.BinaryOperator) { - Codes.BinaryOperator bc = (Codes.BinaryOperator) code; - transformBinary(binaryOperatorMap[bc.kind.ordinal()], bc, - branch, block); - } else if (code instanceof Codes.ArrayGenerator) { - transform((Codes.ArrayGenerator) code, block, branch); - } else if (code instanceof Codes.NewArray) { - transformNary(Expr.Nary.Op.ARRAY, (Codes.NewArray) code, branch, block); - } else if (code instanceof Codes.NewRecord) { - transformNary(Expr.Nary.Op.TUPLE, (Codes.NewRecord) code, branch, block); - } else if (code instanceof Codes.Convert) { - transform((Codes.Convert) code, block, branch); - } else if (code instanceof Codes.Const) { - transform((Codes.Const) code, block, branch); - } else if (code instanceof Codes.Debug) { - // skip - } else if (code instanceof Codes.FieldLoad) { - transform((Codes.FieldLoad) code, block, branch); - } else if (code instanceof Codes.IndirectInvoke) { - transform((Codes.IndirectInvoke) code, block, branch); - } else if (code instanceof Codes.Invoke) { - transform((Codes.Invoke) code, block, branch); - } else if (code instanceof Codes.Invert) { - transform((Codes.Invert) code, block, branch); - } else if (code instanceof Codes.Label) { - // skip - } else if (code instanceof Codes.IndexOf) { - transform((Codes.IndexOf) code, block, branch); - } else if (code instanceof Codes.Move) { - transform((Codes.Move) code, block, branch); - } else if (code instanceof Codes.Assign) { - transform((Codes.Assign) code, block, branch); - } else if (code instanceof Codes.Update) { - transform((Codes.Update) code, block, branch); - } else if (code instanceof Codes.UnaryOperator) { - transform((Codes.UnaryOperator) code, block, branch); - } else if (code instanceof Codes.Dereference) { - transform((Codes.Dereference) code, block, branch); - } else if (code instanceof Codes.Nop) { - // skip - } else if (code instanceof Codes.NewObject) { - transform((Codes.NewObject) code, block, branch); - } else if (code instanceof Codes.Lambda) { - transform((Codes.Lambda) code, block, branch); - } else { - internalFailure("unknown: " + code.getClass().getName(), - filename, block.attributes(branch.pc())); - } - } catch (InternalFailure e) { - throw e; - } catch (SyntaxError e) { - throw e; - } catch (Throwable e) { - internalFailure(e.getMessage(), filename, e, - block.attributes(branch.pc())); - } - } - - protected void transform(Codes.Assign code, AttributedCodeBlock block, - VcBranch branch) { - for (int i = 0; i != code.operands().length; ++i) { - branch.write(code.target(i), branch.read(code.operand(i))); - } - } - - /** - * Maps binary bytecodes into expression opcodes. - */ - private static Expr.Binary.Op[] binaryOperatorMap = { Expr.Binary.Op.ADD, - Expr.Binary.Op.SUB, - Expr.Binary.Op.MUL, - Expr.Binary.Op.DIV, - Expr.Binary.Op.REM, - null, - null, // bitwise or - null, // bitwise xor - null, // bitwise and - null, // left shift - null // right shift - }; - - protected void transform(Codes.Convert code, AttributedCodeBlock block, VcBranch branch) { - Collection attributes = toWycsAttributes(block.attributes(branch.pc())); - Expr result = branch.read(code.operand(0)); - SyntacticType type = convert(code.result, block.attributes(branch.pc())); - branch.write(code.target(0), new Expr.Cast(type, result, attributes)); - } - - protected void transform(Codes.Const code, AttributedCodeBlock block, VcBranch branch) { - Value val = convert(code.constant, block, branch); - branch.write(code.target(), new Expr.Constant(val, toWycsAttributes(block.attributes(branch.pc())))); - } - - protected void transform(Codes.Debug code, AttributedCodeBlock block, - VcBranch branch) { - // do nout - } - - protected void transform(Codes.Dereference code, AttributedCodeBlock block, VcBranch branch) { - branch.havoc(code.target(0)); - } - - protected void transform(Codes.FieldLoad code, AttributedCodeBlock block, VcBranch branch) { - ArrayList fields = new ArrayList(code.type(0).fields().keySet()); - Collections.sort(fields); - Expr src = branch.read(code.operand(0)); - Expr index = new Expr.Constant(Value.Integer(BigInteger.valueOf(fields.indexOf(code.field)))); - Expr result = new Expr.IndexOf(src, index, toWycsAttributes(block.attributes(branch.pc()))); - branch.write(code.target(0), result); - } - - protected void transform(Codes.IndirectInvoke code, - AttributedCodeBlock block, VcBranch branch) { - for(int target : code.targets()) { - branch.havoc(target); - } - } - - protected void transform(Codes.Invoke code, AttributedCodeBlock block, - VcBranch branch) throws Exception { - Collection attributes = block - .attributes(branch.pc()); - Collection wyccAttributes = toWycsAttributes(attributes); - int[] code_operands = code.operands(); - int[] targets = code.targets(); - - if (targets.length > 0) { - // Need to assume the post-condition holds. - Expr[] operands = new Expr[code_operands.length]; - for (int i = 0; i != code_operands.length; ++i) { - operands[i] = branch.read(code_operands[i]); - } - Expr argument = operands.length == 1 ? operands[0] : new Expr.Nary( - Expr.Nary.Op.TUPLE, operands,wyccAttributes); - branch.write(code.targets()[0], new Expr.Invoke(code.name.name(), - code.name.module(), Collections.EMPTY_LIST, argument, - wyccAttributes)); - - // This is a potential fix for #488, although it doesn't work - // FIXME: needs to updated to handle multiple returns as well - if (containsNominal(code.type(0).returns().get(0), attributes)) { - // This is required to handle the implicit constraints implied - // by a nominal type. See #488. - Expr nominalTest = new Expr.Is(branch.read(code.targets()[0]), - convert(code.type(0).returns().get(0), attributes)); - branch.assume(nominalTest); - } - - // Here, we must find the name of the corresponding postcondition so - // that we can assume it. - List ensures = findPostcondition(code.name, code.type(0), block, branch); - - if (ensures.size() > 0) { - // To assume the post-condition holds after the method, we - // simply called the corresponding post-condition macros. - Expr[] arguments = new Expr[operands.length + targets.length]; - System.arraycopy(operands, 0, arguments, 0, operands.length); - for(int i=0;i!=targets.length;++i) { - arguments[operands.length+i] = branch.read(targets[i]); - } - String prefix = code.name.name() + "_ensures_"; - for (int i = 0; i != ensures.size(); ++i) { - Expr.Invoke macro = new Expr.Invoke(prefix + i, - code.name.module(), Collections.EMPTY_LIST, - new Expr.Nary(Expr.Nary.Op.TUPLE, arguments)); - branch.assume(macro); - } - } - } - } - - protected void transform(Codes.Invert code, AttributedCodeBlock block, VcBranch branch) { - branch.havoc(code.target(0)); - } - - protected void transform(Codes.IndexOf code, AttributedCodeBlock block, VcBranch branch) { - Expr src = branch.read(code.operand(0)); - Expr idx = branch.read(code.operand(1)); - branch.write(code.target(0), new Expr.IndexOf(src, idx, toWycsAttributes(block.attributes(branch.pc())))); - } - - protected void transform(Codes.ArrayGenerator code, AttributedCodeBlock block, VcBranch branch) { - Collection wyilAttributes = block.attributes(branch.pc()); - Collection attributes = toWycsAttributes(wyilAttributes); - Expr element = branch.read(code.operand(0)); - Expr count = branch.read(code.operand(1)); - branch.havoc(code.target(0)); - Expr arg = new Expr.Nary(Expr.Nary.Op.TUPLE, new Expr[] { branch.read(code.target(0)), element, count }, - attributes); - ArrayList generics = new ArrayList(); - generics.add(convert(code.type(0).element(),wyilAttributes)); - Expr.Invoke macro = new Expr.Invoke("generate", Trie.fromString("wycs/core/Array"), - generics, arg); - branch.assume(macro); - } - - protected void transform(Codes.Lambda code, AttributedCodeBlock block, VcBranch branch) { - // TODO: implement lambdas somehow? - branch.havoc(code.target(0)); - } - - protected void transform(Codes.Move code, VcBranch branch) { - branch.write(code.target(0), branch.read(code.operand(0))); - } - - protected void transform(Codes.NewObject code, AttributedCodeBlock block, VcBranch branch) { - branch.havoc(code.target(0)); - } - - protected void transform(Codes.Nop code, AttributedCodeBlock block, - VcBranch branch) { - // do nout - } - - protected void transform(Codes.UnaryOperator code, AttributedCodeBlock block, VcBranch branch) { - switch (code.kind) { - case NEG: - transformUnary(Expr.Unary.Op.NEG, code, branch, block); - break; - default: - branch.havoc(code.target(0)); - } - } - - protected void transform(Codes.Update code, AttributedCodeBlock block, VcBranch branch) { - Expr result = branch.read(code.result()); - Expr oldSource = branch.read(code.target(0)); - Expr newSource = branch.havoc(code.target(0)); - updateHelper(code.iterator(), oldSource, newSource, result, branch, block); - } - - protected void updateHelper(Iterator iter, Expr oldSource, Expr newSource, Expr result, VcBranch branch, - AttributedCodeBlock block) { - Collection attributes = toWycsAttributes(block.attributes(branch.pc())); - if (!iter.hasNext()) { - branch.assume(new Expr.Binary(Expr.Binary.Op.EQ, newSource, result, attributes)); - } else { - Codes.LVal lv = iter.next(); - if (lv instanceof Codes.RecordLVal) { - Codes.RecordLVal rlv = (Codes.RecordLVal) lv; - ArrayList fields = new ArrayList(rlv.rawType().fields().keySet()); - Collections.sort(fields); - int index = fields.indexOf(rlv.field); - for (int i = 0; i != fields.size(); ++i) { - Expr j = new Expr.Constant(Value.Integer(BigInteger.valueOf(i))); - Expr oldS = new Expr.IndexOf(oldSource, j, attributes); - Expr newS = new Expr.IndexOf(newSource, j, attributes); - if (i != index) { - branch.assume(new Expr.Binary(Expr.Binary.Op.EQ, oldS, newS, attributes)); - } else { - updateHelper(iter, oldS, newS, result, branch, block); - } - } - } else if (lv instanceof Codes.ArrayLVal) { - Codes.ArrayLVal rlv = (Codes.ArrayLVal) lv; - Expr index = branch.read(rlv.indexOperand); - Expr oldS = new Expr.IndexOf(oldSource, index, attributes); - Expr newS = new Expr.IndexOf(newSource, index, attributes); - updateHelper(iter, oldS, newS, result, branch, block); - Expr arg = new Expr.Nary(Expr.Nary.Op.TUPLE, new Expr[] { oldSource, newSource, index }, attributes); - ArrayList generics = new ArrayList(); - generics.add(convert(rlv.rawType().element(),Collections.EMPTY_LIST)); - Expr.Invoke macro = new Expr.Invoke("update", Trie.fromString("wycs/core/Array"), generics, arg); - branch.assume(macro); - } - } - } - /** - * Havoc an "expression". - * - * @param source - * @param branch - * @param block - * @return - */ - private Expr havoc(Expr source, VcBranch branch, AttributedCodeBlock block) { - if(source instanceof Expr.Variable) { - Expr.Variable v = (Expr.Variable) source; - int register = determineRegister(v.name,branch.prefixes()); - branch.havoc(register); - return branch.read(register); - } else { - // TODO: Must implement the other cases. At the moment, I'm not sure - // the best way to do this though. - } - // It should be impossible to reach here. - internalFailure("unreachable code", filename); - return null; - } - - - /** - * Transform an assignable unary bytecode using a given target operator. - * This must read the operand and then create the appropriate target - * expression. Finally, the result of the bytecode must be written back to - * the enclosing branch. - * - * @param operator - * --- The target operator - * @param code - * --- The bytecode being translated - * @param branch - * --- The enclosing branch - */ - protected void transformUnary(Expr.Unary.Op operator, Code.AbstractBytecode code, VcBranch branch, - AttributedCodeBlock block) { - Expr lhs = branch.read(code.operand(0)); - branch.write(code.target(0), new Expr.Unary(operator, lhs, toWycsAttributes(block.attributes(branch.pc())))); - } - - /** - * Transform an assignable binary bytecode using a given target operator. - * This must read both operands and then create the appropriate target - * expression. Finally, the result of the bytecode must be written back to - * the enclosing branch. - * - * @param operator - * --- The target operator - * @param code - * --- The bytecode being translated - * @param branch - * --- The enclosing branch - */ - protected void transformBinary(Expr.Binary.Op operator, Code.AbstractBytecode code, VcBranch branch, - AttributedCodeBlock block) { - Expr lhs = branch.read(code.operand(0)); - Expr rhs = branch.read(code.operand(1)); - - if (operator != null) { - branch.write(code.target(0), - new Expr.Binary(operator, lhs, rhs, toWycsAttributes(block.attributes(branch.pc())))); - } else { - // In this case, we have a binary operator which we don't know how - // to translate into WyCS. Therefore, we need to invalidate the - // target register to signal this. - branch.havoc(code.target(0)); - } - } - - /** - * Transform an assignable nary bytecode using a given target operator. This - * must read all operands and then create the appropriate target expression. - * Finally, the result of the bytecode must be written back to the enclosing - * branch. - * - * @param operator - * --- The target operator - * @param code - * --- The bytecode being translated - * @param branch - * --- The enclosing branch - */ - protected void transformNary(Expr.Nary.Op operator, Code.AbstractBytecode code, VcBranch branch, - AttributedCodeBlock block) { - int[] code_operands = code.operands(); - Expr[] vals = new Expr[code_operands.length]; - for (int i = 0; i != vals.length; ++i) { - vals[i] = branch.read(code_operands[i]); - } - branch.write(code.target(0), new Expr.Nary(operator, vals, toWycsAttributes(block.attributes(branch.pc())))); - } - - /** - * Find the precondition associated with a given function or method. This - * maybe contained in the same file, or in a different file. This may - * require loading that file in memory to access this information. - * - * @param name - * --- Fully qualified name of function - * @param fun - * --- Type of fucntion. - * @param block - * --- Enclosing block (for debugging purposes). - * @param branch - * --- Enclosing branch (for debugging purposes). - * @return - * @throws Exception - */ - protected List findPrecondition(NameID name, Type.FunctionOrMethod fun, - AttributedCodeBlock block, VcBranch branch) throws Exception { - Path.Entry e = builder.project().get(name.module(), WyilFile.ContentType); - if (e == null) { - syntaxError(errorMessage(ErrorMessages.RESOLUTION_ERROR, name.module().toString()), filename, - block.attributes(branch.pc())); - } - WyilFile m = e.read(); - WyilFile.FunctionOrMethod method = m.functionOrMethod(name.name(), fun); - - return method.precondition(); - } - - /** - * Find the postcondition associated with a given function or method. This - * maybe contained in the same file, or in a different file. This may - * require loading that file in memory to access this information. - * - * @param name - * --- Fully qualified name of function - * @param fun - * --- Type of fucntion. - * @param block - * --- Enclosing block (for debugging purposes). - * @param branch - * --- Enclosing branch (for debugging purposes). - * @return - * @throws Exception - */ - protected List findPostcondition(NameID name, Type.FunctionOrMethod fun, - AttributedCodeBlock block, VcBranch branch) throws Exception { - Path.Entry e = builder.project().get(name.module(), WyilFile.ContentType); - if (e == null) { - syntaxError(errorMessage(ErrorMessages.RESOLUTION_ERROR, name.module().toString()), filename, - block.attributes(branch.pc())); - } - WyilFile m = e.read(); - WyilFile.FunctionOrMethod method = m.functionOrMethod(name.name(), fun); - return method.postcondition(); - } - /** * Construct a macro with a given name representing a block of code. The * macro can then be called elsewhere as a predicate. For example, a macro @@ -1990,12 +1318,11 @@ protected List findPostcondition(NameID name, Type.Function * @return */ protected void buildMacroBlock(String name, CodeForest.Index root, - AttributedCodeBlock block, List types, boolean isInvariant) { + CodeForest forest, List types, boolean isInvariant) { int start = wyalFile.declarations().size(); // first, generate a branch for traversing the external block. - VcBranch master = new VcBranch( - Math.max(block.numSlots(), types.size()), null); + VcBranch master = new VcBranch(Math.max(forest.numRegisters(), types.size()), root, null); Type[] environment = new Type[types.size()]; ArrayList declarations = new ArrayList(); @@ -2007,7 +1334,7 @@ protected void buildMacroBlock(String name, CodeForest.Index root, Expr.Variable v = new Expr.Variable("r" + i); master.write(i, v); // FIXME: what attributes to pass into convert? - declarations.add(new TypePattern.Leaf(convert(type, + declarations.add(new TypePattern.Leaf(utils.convert(type, Collections.EMPTY_LIST), v)); } } @@ -2025,8 +1352,7 @@ protected void buildMacroBlock(String name, CodeForest.Index root, // At this point, we are guaranteed exactly one branch because there // is only ever one exit point from a pre-/post-condition. - List exitBranches = transform(master, root, isInvariant, - environment, block); + List exitBranches = transform(master, root, isInvariant, environment, forest); // Remove any verification conditions that were generated when // processing this block. // FIXME: this is something of a hack for now. A better solution would @@ -2049,42 +1375,7 @@ protected void buildMacroBlock(String name, CodeForest.Index root, internalFailure("unreachable code", filename); } - /** - * Construct a function with a given name representing a block of code. The - * function can then be called elsewhere as an uninterpreted function. - * - * @param name - * --- the nameto give to the generated macro. - * @param params - * --- parameter types to use. - * @param ret - * --- return type to use - * @return - */ - protected void buildFunctionBlock(String name, List params, List returns) { - - TypePattern.Leaf[] parameterPatterns = new TypePattern.Leaf[params.size()]; - // second, set initial environment - for (int i = 0; i != params.size(); ++i) { - Expr.Variable v = new Expr.Variable("r" + i); - // FIXME: what attributes to pass into convert? - parameterPatterns[i] = new TypePattern.Leaf(convert(params.get(i), - Collections.EMPTY_LIST), v); - } - TypePattern.Leaf[] returnPatterns = new TypePattern.Leaf[returns.size()]; - // second, set initial environment - for (int i = 0; i != returns.size(); ++i) { - Expr.Variable v = new Expr.Variable("r" + i); - returnPatterns[i] = new TypePattern.Leaf(convert(returns.get(i), - Collections.EMPTY_LIST), v); - } - // Construct the type declaration for the new block macro - TypePattern from = new TypePattern.Tuple(parameterPatterns); - TypePattern to = new TypePattern.Tuple(returnPatterns); - - wyalFile.add(wyalFile.new Function(name, Collections.EMPTY_LIST, from, - to, null)); - } + /** * Construct a verification condition which asserts a given expression on @@ -2106,7 +1397,7 @@ protected void buildFunctionBlock(String name, List params, List ret * @return */ protected Expr buildVerificationCondition(Expr assertion, VcBranch branch, - Type[] environment, AttributedCodeBlock block, Expr... extraAssumptions) { + Type[] environment, CodeForest forest, Expr... extraAssumptions) { // First construct the assertion which forms the basis of the // verification condition. The assertion must be shown to hold assuming // the assumptions did. Therefore, we construct an implication to @@ -2118,7 +1409,7 @@ protected Expr buildVerificationCondition(Expr assertion, VcBranch branch, } assertion = new Expr.Binary(Expr.Binary.Op.IMPLIES, assumptions, - assertion, toWycsAttributes(block.attributes(branch.pc()))); + assertion, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())); // Next, we determine the set of used variables within the assertion. // This is done to minimise the number of variables present in the final @@ -2149,10 +1440,10 @@ protected Expr buildVerificationCondition(Expr assertion, VcBranch branch, // WyAL. type = Type.T_ANY; } else { - int reg = determineRegister(var, branch.prefixes()); + int reg = VcUtils.determineRegister(var, branch.prefixes()); type = environment[reg]; } - SyntacticType t = convert(type, block.attributes(branch.pc())); + SyntacticType t = utils.convert(type, forest.get(branch.pc()).attributes()); Expr.Variable v = new Expr.Variable(var); vars.add(new TypePattern.Leaf(t, v)); } @@ -2171,49 +1462,6 @@ protected Expr buildVerificationCondition(Expr assertion, VcBranch branch, } } - /** - * Determine the originating register number for this variable. This is made - * difficult because of variable versioning. All variable names and versions - * are encoded into a string of the form "n$v", where n is the variable name - * (A.K.A. the prefix) and "v" is the version. - * - * @param variable - * @return - */ - private static int determineRegister(String variable, String[] prefixes) { - // First determine the variable name (i.e. the prefix). - int dollarIndex = variable.indexOf('$'); - String prefix; - if (dollarIndex != -1) { - // In this case, the variable name was of the form "n$v" where n is - // the name, and v is the version. We don't need the version here, - // so strip it off. - prefix = variable.substring(0, dollarIndex); - } else { - // In this case, no version is given and, hence, there is nothing to - // strip off. - prefix = variable; - } - // Now, check whether this is a raw register identifier, or a named - // variable identifier. - if(prefix.startsWith("r%")) { - // This is a raw register identifier. Therefore, we can extract the - // register number directly. - return Integer.parseInt(prefix.substring(2)); - } else { - // This is a named varaible identifier. Therefore, we need to look - // through the known list of named variable prefixes to see whether - // or not we can find it (which we should be able to do). - for (int i = 0; i != prefixes.length; ++i) { - if (prefix.equals(prefixes[i])) { - return i; - } - } - // Should be impossible to get here. - throw new RuntimeException( - "Unreachable code reached whilst looking for: " + variable); - } - } /** *

@@ -2307,70 +1555,7 @@ private Expr generateAssumptionsHelper(VcBranch b, VcBranch end) { } } } - - // private Expr generateAssumptions(VcBranch branch) { - // // First, flattern the branch graph into a topological ordering - // List branches = flattern(branch); - // - // // Second, initialise the node data - // HashMap data = new HashMap(); - // for (int i = 0; i != branches.size(); ++i) { - // VcBranch b = branches.get(i); - // data.put(b, new Node()); - // } - // - // // Third, initialise parent counts - // for (int i = 0; i != branches.size(); ++i) { - // VcBranch b = branches.get(i); - // for(VcBranch parent : b.parents()) { - // data.get(parent).count = 0; - // } - // } - // } - // - // private class Node { - // public Expr constraint; - // public int count; - // } - // - // private List flattern(VcBranch root) { - // HashSet visited = new HashSet(); - // ArrayList branches = new ArrayList(); - // // Now, perform a depth-first search of the branch graph adding branches - // // in reverse topological order. - // flattern(root,visited,branches); - // // The depth-first search we just conducted loaded all branches into the - // // list in reverse topological order. For sanity, we just put them - // // around the right way here. Technically we don't need to do this, but - // // it just simplifies the remainder of the algorithm. - // Collections.reverse(branches); - // return branches; - // } - // - // /** - // * Perform depth-first traversal of the branch graph, storing visited - // nodes - // * in reverse topological order. - // * - // * @param b - // * --- Branch currently being visited. - // * @param visited - // * --- Set of previously visited branches. - // * @param branches - // * --- reverse topogolical order being constructed. - // */ - // private void flattern(VcBranch b, HashSet visited, - // List branches) { - // if (!visited.contains(b)) { - // // this branch has not already been visited. - // visited.add(b); - // for (VcBranch parent : b.parents()) { - // flattern(parent, visited, branches); - // } - // branches.add(b); - // } - // } - + /** * Generate a formula representing a condition from an conditional bytecode. * @@ -2380,7 +1565,7 @@ private Expr generateAssumptionsHelper(VcBranch b, VcBranch end) { * @return */ private Expr.Binary buildTest(Codes.Comparator cop, int leftOperand, - int rightOperand, Type type, AttributedCodeBlock block, + int rightOperand, Type type, CodeForest forest, VcBranch branch) { Expr lhs = branch.read(leftOperand); Expr rhs = branch.read(rightOperand); @@ -2406,268 +1591,13 @@ private Expr.Binary buildTest(Codes.Comparator cop, int leftOperand, break; default: internalFailure("unknown comparator (" + cop + ")", filename, - block.attributes(branch.pc())); + forest.get(branch.pc()).attributes()); return null; } return new Expr.Binary(op, lhs, rhs, - toWycsAttributes(block.attributes(branch.pc()))); + VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())); } - /** - * Generate the logically inverted expression corresponding to a given - * comparator. For example, inverting "<=" gives ">", inverting "==" gives - * "!=", etc. - * - * @param test - * --- the binary comparator being inverted. - * @return - */ - private Expr invert(Expr.Binary test) { - Expr.Binary.Op op; - switch (test.op) { - case EQ: - op = Expr.Binary.Op.NEQ; - break; - case NEQ: - op = Expr.Binary.Op.EQ; - break; - case GTEQ: - op = Expr.Binary.Op.LT; - break; - case GT: - op = Expr.Binary.Op.LTEQ; - break; - case LTEQ: - op = Expr.Binary.Op.GT; - break; - case LT: - op = Expr.Binary.Op.GTEQ; - break; - default: - wycc.lang.SyntaxError.internalFailure("unknown comparator (" - + test.op + ")", filename, test); - return null; - } - - return new Expr.Binary(op, test.leftOperand, test.rightOperand, - test.attributes()); - } - - /** - * Convert a WyIL constant into its equivalent WyCS constant. In some cases, - * this is a direct translation. In other cases, WyIL constants are encoded - * using more primitive WyCS values. - * - * @param c - * --- The WyIL constant to be converted. - * @param block - * --- The block within which this conversion is taking place - * (for debugging purposes) - * @param branch - * --- The branch within which this conversion is taking place - * (for debugging purposes) - * @return - */ - public Value convert(Constant c, AttributedCodeBlock block, VcBranch branch) { - if (c instanceof Constant.Null) { - return wycs.core.Value.Null; - } else if (c instanceof Constant.Bool) { - Constant.Bool cb = (Constant.Bool) c; - return wycs.core.Value.Bool(cb.value); - } else if (c instanceof Constant.Byte) { - Constant.Byte cb = (Constant.Byte) c; - return wycs.core.Value.Integer(BigInteger.valueOf(cb.value)); - } else if (c instanceof Constant.Integer) { - Constant.Integer cb = (Constant.Integer) c; - return wycs.core.Value.Integer(cb.value); - } else if (c instanceof Constant.Array) { - Constant.Array cb = (Constant.Array) c; - List cb_values = cb.values; - ArrayList items = new ArrayList(); - for (int i = 0; i != cb_values.size(); ++i) { - items.add(convert(cb_values.get(i), block, branch)); - } - return Value.Array(items); - } else if (c instanceof Constant.Record) { - Constant.Record rb = (Constant.Record) c; - - // NOTE:: records are currently translated into WyCS as tuples, - // where - // each field is allocated a slot based on an alphabetical sorting - // of field names. It's unclear at this stage whether or not that is - // a general solution. In particular, it would seem to be brokwn for - // type testing. - - ArrayList fields = new ArrayList(rb.values.keySet()); - Collections.sort(fields); - ArrayList values = new ArrayList(); - for (String field : fields) { - values.add(convert(rb.values.get(field), block, branch)); - } - return wycs.core.Value.Tuple(values); - } else { - internalFailure("unknown constant encountered (" + c + ")", - filename, block.attributes(branch.pc())); - return null; - } - } - - private SyntacticType convert(List types, AttributedCodeBlock block, - VcBranch branch) { - ArrayList ntypes = new ArrayList(); - for (int i = 0; i != types.size(); ++i) { - ntypes.add(convert(types.get(i), block.attributes(branch.pc()))); - } - return new SyntacticType.Tuple(ntypes); - } - - /** - * Convert a WyIL type into its equivalent WyCS type. In some cases, this is - * a direct translation. In other cases, WyIL constants are encoded using - * more primitive WyCS types. - * - * @param t - * --- The WyIL type to be converted. - * @param attributes - * --- The attributes associated with the point of this - * conversion. These are used for debugging purposes to associate - * any errors generated with a source line. - * @return - */ - private SyntacticType convert(Type t, - Collection attributes) { - // FIXME: this is fundamentally broken in the case of recursive types. - // See Issue #298. - if (t instanceof Type.Any) { - return new SyntacticType.Any(toWycsAttributes(attributes)); - } else if (t instanceof Type.Void) { - return new SyntacticType.Void(toWycsAttributes(attributes)); - } else if (t instanceof Type.Null) { - return new SyntacticType.Null(toWycsAttributes(attributes)); - } else if (t instanceof Type.Bool) { - return new SyntacticType.Bool(toWycsAttributes(attributes)); - } else if (t instanceof Type.Byte) { - // FIXME: implement SyntacticType.Byte - // return new SyntacticType.Byte(attributes(branch)); - return new SyntacticType.Int(toWycsAttributes(attributes)); - } else if (t instanceof Type.Int) { - return new SyntacticType.Int(toWycsAttributes(attributes)); - } else if (t instanceof Type.Array) { - Type.Array lt = (Type.Array) t; - SyntacticType element = convert(lt.element(), attributes); - // ugly. - return new SyntacticType.List(element); - } else if (t instanceof Type.Record) { - Type.Record rt = (Type.Record) t; - HashMap fields = rt.fields(); - ArrayList names = new ArrayList(fields.keySet()); - ArrayList elements = new ArrayList(); - Collections.sort(names); - for (int i = 0; i != names.size(); ++i) { - String field = names.get(i); - elements.add(convert(fields.get(field), attributes)); - } - return new SyntacticType.Tuple(elements); - } else if (t instanceof Type.Reference) { - // FIXME: how to translate this?? - return new SyntacticType.Any(); - } else if (t instanceof Type.Union) { - Type.Union tu = (Type.Union) t; - HashSet tu_elements = tu.bounds(); - ArrayList elements = new ArrayList(); - for (Type te : tu_elements) { - elements.add(convert(te, attributes)); - } - return new SyntacticType.Union(elements); - } else if (t instanceof Type.Negation) { - Type.Negation nt = (Type.Negation) t; - SyntacticType element = convert(nt.element(), attributes); - return new SyntacticType.Negation(element); - } else if (t instanceof Type.FunctionOrMethod) { - Type.FunctionOrMethod ft = (Type.FunctionOrMethod) t; - return new SyntacticType.Any(); - } else if (t instanceof Type.Nominal) { - Type.Nominal nt = (Type.Nominal) t; - NameID nid = nt.name(); - ArrayList names = new ArrayList(); - for (String pc : nid.module()) { - names.add(pc); - } - names.add(nid.name()); - return new SyntacticType.Nominal(names, - toWycsAttributes(attributes)); - } else { - internalFailure("unknown type encountered (" - + t.getClass().getName() + ")", filename, attributes); - return null; - } - } - - /** - * Make an objective assessment as to whether a type may include an - * invariant or not. The purpose here is reduce the number of verification - * conditions generated with respect to constrained types. The algorithm is - * currently very simple. It essentially looks to see whether or not the - * type contains a nominal component. If so, the answer is "yes", otherwise - * the answer is "no". - * - * @return - */ - private boolean containsNominal(Type t, - Collection attributes) { - // FIXME: this is fundamentally broken in the case of recursive types. - // See Issue #298. - if (t instanceof Type.Any || t instanceof Type.Void - || t instanceof Type.Null || t instanceof Type.Bool - || t instanceof Type.Byte || t instanceof Type.Int) { - return false; - } else if (t instanceof Type.Array) { - Type.Array lt = (Type.Array) t; - return containsNominal(lt.element(), attributes); - } else if (t instanceof Type.Record) { - Type.Record rt = (Type.Record) t; - for (Type field : rt.fields().values()) { - if (containsNominal(field, attributes)) { - return true; - } - } - return false; - } else if (t instanceof Type.Reference) { - Type.Reference lt = (Type.Reference) t; - return containsNominal(lt.element(), attributes); - } else if (t instanceof Type.Union) { - Type.Union tu = (Type.Union) t; - for (Type te : tu.bounds()) { - if (containsNominal(te, attributes)) { - return true; - } - } - return false; - } else if (t instanceof Type.Negation) { - Type.Negation nt = (Type.Negation) t; - return containsNominal(nt.element(), attributes); - } else if (t instanceof Type.FunctionOrMethod) { - Type.FunctionOrMethod ft = (Type.FunctionOrMethod) t; - for (Type pt : ft.params()) { - if (containsNominal(pt, attributes)) { - return true; - } - } - for (Type pt : ft.returns()) { - if (containsNominal(pt, attributes)) { - return true; - } - } - return false; - } else if (t instanceof Type.Nominal) { - return true; - } else { - internalFailure("unknown type encountered (" - + t.getClass().getName() + ")", filename, attributes); - return false; - } - } - private Type expand(Type t, Collection attributes) { try { return expander.getUnderlyingType(t); @@ -2685,57 +1615,4 @@ private static List append(List xs, List ys) { rs.addAll(ys); return rs; } - - private static Type[] toArray(List xs) { - Type[] ts = new Type[xs.size()]; - for (int i = 0; i != xs.size(); ++i) { - ts[i] = xs.get(i); - } - return ts; - } - - /** - * Convert a list of WyIL attributes into a corresponding list of - * WycsAttributes. Note that, in some cases, no conversion is possible and - * such attributes are silently dropped. - * - * @param branch - * @return - */ - private static Collection toWycsAttributes( - Collection wyilAttributes) { - ArrayList wycsAttributes = new ArrayList(); - // iterate each attribute and convert those which can be convered. - for (wyil.lang.Attribute attr : wyilAttributes) { - if (attr instanceof wyil.attributes.SourceLocation) { - wyil.attributes.SourceLocation l = (wyil.attributes.SourceLocation) attr; - wycsAttributes.add(new wycc.lang.Attribute.Source(l.start(), l - .end(), 0)); - } - } - return wycsAttributes; - } - - /** - * Returns the prefix array which gives the names of all registers declared - * in a given block. - * - * @param d - * @return - */ - private static Pair parseRegisterDeclarations( - VariableDeclarations rds) { - if (rds != null) { - String[] prefixes = new String[rds.size()]; - Type[] types = new Type[rds.size()]; - for (int i = 0; i != prefixes.length; ++i) { - VariableDeclarations.Declaration d = rds.get(i); - prefixes[i] = d.name(); - types[i] = d.type(); - } - return new Pair<>(prefixes, types); - } else { - return null; - } - } } diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java new file mode 100644 index 0000000000..ddb3217879 --- /dev/null +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -0,0 +1,669 @@ +package wyil.builders; + +import static wyil.util.ErrorMessages.errorMessage; +import static wyil.util.ErrorMessages.internalFailure; +import static wyil.util.ErrorMessages.syntaxError; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import wybs.lang.Builder; +import wycc.lang.NameID; +import wycc.util.Pair; +import wycc.util.ResolveError; +import wycs.core.Value; +import wycs.syntax.Expr; +import wycs.syntax.SyntacticType; +import wycs.syntax.TypePattern; +import wycs.syntax.WyalFile; +import wycs.syntax.WyalFile.Function; +import wyfs.lang.Path; +import wyil.attributes.VariableDeclarations; +import wyil.lang.Code; +import wyil.lang.CodeForest; +import wyil.lang.Codes; +import wyil.lang.Constant; +import wyil.lang.Type; +import wyil.lang.WyilFile; +import wyil.util.ErrorMessages; +import wyil.util.TypeExpander; + +public class VcUtils { + private final String filename; + private final Builder builder; + private final TypeExpander expander; + + public VcUtils(String filename, Builder builder, TypeExpander expander) { + this.filename = filename; + this.builder = builder; + this.expander = expander; + } + + /** + * Convert a WyIL constant into its equivalent WyCS constant. In some cases, + * this is a direct translation. In other cases, WyIL constants are encoded + * using more primitive WyCS values. + * + * @param c + * --- The WyIL constant to be converted. + * @param block + * --- The block within which this conversion is taking place + * (for debugging purposes) + * @param branch + * --- The branch within which this conversion is taking place + * (for debugging purposes) + * @return + */ + public Value convert(Constant c, CodeForest forest, VcBranch branch) { + if (c instanceof Constant.Null) { + return wycs.core.Value.Null; + } else if (c instanceof Constant.Bool) { + Constant.Bool cb = (Constant.Bool) c; + return wycs.core.Value.Bool(cb.value); + } else if (c instanceof Constant.Byte) { + Constant.Byte cb = (Constant.Byte) c; + return wycs.core.Value.Integer(BigInteger.valueOf(cb.value)); + } else if (c instanceof Constant.Integer) { + Constant.Integer cb = (Constant.Integer) c; + return wycs.core.Value.Integer(cb.value); + } else if (c instanceof Constant.Array) { + Constant.Array cb = (Constant.Array) c; + List cb_values = cb.values; + ArrayList items = new ArrayList(); + for (int i = 0; i != cb_values.size(); ++i) { + items.add(convert(cb_values.get(i), forest, branch)); + } + return Value.Array(items); + } else if (c instanceof Constant.Record) { + Constant.Record rb = (Constant.Record) c; + + // NOTE:: records are currently translated into WyCS as tuples, + // where + // each field is allocated a slot based on an alphabetical sorting + // of field names. It's unclear at this stage whether or not that is + // a general solution. In particular, it would seem to be brokwn for + // type testing. + + ArrayList fields = new ArrayList(rb.values.keySet()); + Collections.sort(fields); + ArrayList values = new ArrayList(); + for (String field : fields) { + values.add(convert(rb.values.get(field), forest, branch)); + } + return wycs.core.Value.Tuple(values); + } else { + internalFailure("unknown constant encountered (" + c + ")", + filename, forest.get(branch.pc()).attributes()); + return null; + } + } + + /** + * Construct a function prototype with a given name and type. The function + * can then be called elsewhere as an uninterpreted function. The function + * doesn't have a body but is used as a name to be referred to from + * assertions. + * + * @param wyalFile + * --- the file onto which this function is created. + * @param name + * --- the nameto give to the generated macro. + * @param params + * --- parameter types to use. + * @param returns + * --- return types to use + * @return + */ + public void createFunctionPrototype(WyalFile wyalFile, String name, List params, List returns) { + + TypePattern.Leaf[] parameterPatterns = new TypePattern.Leaf[params.size()]; + // second, set initial environment + for (int i = 0; i != params.size(); ++i) { + Expr.Variable v = new Expr.Variable("r" + i); + // FIXME: what attributes to pass into convert? + parameterPatterns[i] = new TypePattern.Leaf(convert(params.get(i), + Collections.EMPTY_LIST), v); + } + TypePattern.Leaf[] returnPatterns = new TypePattern.Leaf[returns.size()]; + // second, set initial environment + for (int i = 0; i != returns.size(); ++i) { + Expr.Variable v = new Expr.Variable("r" + i); + returnPatterns[i] = new TypePattern.Leaf(convert(returns.get(i), + Collections.EMPTY_LIST), v); + } + // Construct the type declaration for the new block macro + TypePattern from = new TypePattern.Tuple(parameterPatterns); + TypePattern to = new TypePattern.Tuple(returnPatterns); + + wyalFile.add(wyalFile.new Function(name, Collections.EMPTY_LIST, from, to, null)); + } + + /** + * Convert a WyIL type into its equivalent WyCS type. In some cases, this is + * a direct translation. In other cases, WyIL constants are encoded using + * more primitive WyCS types. + * + * @param t + * --- The WyIL type to be converted. + * @param attributes + * --- The attributes associated with the point of this + * conversion. These are used for debugging purposes to associate + * any errors generated with a source line. + * @return + */ + public SyntacticType convert(Type t, Collection attributes) { + // FIXME: this is fundamentally broken in the case of recursive types. + // See Issue #298. + if (t instanceof Type.Any) { + return new SyntacticType.Any(toWycsAttributes(attributes)); + } else if (t instanceof Type.Void) { + return new SyntacticType.Void(toWycsAttributes(attributes)); + } else if (t instanceof Type.Null) { + return new SyntacticType.Null(toWycsAttributes(attributes)); + } else if (t instanceof Type.Bool) { + return new SyntacticType.Bool(toWycsAttributes(attributes)); + } else if (t instanceof Type.Byte) { + // FIXME: implement SyntacticType.Byte + // return new SyntacticType.Byte(attributes(branch)); + return new SyntacticType.Int(toWycsAttributes(attributes)); + } else if (t instanceof Type.Int) { + return new SyntacticType.Int(toWycsAttributes(attributes)); + } else if (t instanceof Type.Array) { + Type.Array lt = (Type.Array) t; + SyntacticType element = convert(lt.element(), attributes); + // ugly. + return new SyntacticType.List(element); + } else if (t instanceof Type.Record) { + Type.Record rt = (Type.Record) t; + HashMap fields = rt.fields(); + ArrayList names = new ArrayList(fields.keySet()); + ArrayList elements = new ArrayList(); + Collections.sort(names); + for (int i = 0; i != names.size(); ++i) { + String field = names.get(i); + elements.add(convert(fields.get(field), attributes)); + } + return new SyntacticType.Tuple(elements); + } else if (t instanceof Type.Reference) { + // FIXME: how to translate this?? + return new SyntacticType.Any(); + } else if (t instanceof Type.Union) { + Type.Union tu = (Type.Union) t; + HashSet tu_elements = tu.bounds(); + ArrayList elements = new ArrayList(); + for (Type te : tu_elements) { + elements.add(convert(te, attributes)); + } + return new SyntacticType.Union(elements); + } else if (t instanceof Type.Negation) { + Type.Negation nt = (Type.Negation) t; + SyntacticType element = convert(nt.element(), attributes); + return new SyntacticType.Negation(element); + } else if (t instanceof Type.FunctionOrMethod) { + Type.FunctionOrMethod ft = (Type.FunctionOrMethod) t; + return new SyntacticType.Any(); + } else if (t instanceof Type.Nominal) { + Type.Nominal nt = (Type.Nominal) t; + NameID nid = nt.name(); + ArrayList names = new ArrayList(); + for (String pc : nid.module()) { + names.add(pc); + } + names.add(nid.name()); + return new SyntacticType.Nominal(names, + toWycsAttributes(attributes)); + } else { + internalFailure("unknown type encountered (" + t.getClass().getName() + ")", filename, attributes); + return null; + } + } + + /** + * Convert a list of WyIL attributes into a corresponding list of + * WycsAttributes. Note that, in some cases, no conversion is possible and + * such attributes are silently dropped. + * + * @param branch + * @return + */ + public static Collection toWycsAttributes(Collection wyilAttributes) { + ArrayList wycsAttributes = new ArrayList(); + // iterate each attribute and convert those which can be convered. + for (wyil.lang.Attribute attr : wyilAttributes) { + if (attr instanceof wyil.attributes.SourceLocation) { + wyil.attributes.SourceLocation l = (wyil.attributes.SourceLocation) attr; + wycsAttributes.add(new wycc.lang.Attribute.Source(l.start(), l.end(), 0)); + } + } + return wycsAttributes; + } + + /** + * Generate the logically inverted expression corresponding to a given + * comparator. For example, inverting "<=" gives ">", inverting "==" gives + * "!=", etc. + * + * @param test + * --- the binary comparator being inverted. + * @return + */ + public Expr invert(Expr.Binary test) { + Expr.Binary.Op op; + switch (test.op) { + case EQ: + op = Expr.Binary.Op.NEQ; + break; + case NEQ: + op = Expr.Binary.Op.EQ; + break; + case GTEQ: + op = Expr.Binary.Op.LT; + break; + case GT: + op = Expr.Binary.Op.LTEQ; + break; + case LTEQ: + op = Expr.Binary.Op.GT; + break; + case LT: + op = Expr.Binary.Op.GTEQ; + break; + default: + wycc.lang.SyntaxError.internalFailure("unknown comparator (" + + test.op + ")", filename, test); + return null; + } + + return new Expr.Binary(op, test.leftOperand, test.rightOperand, + test.attributes()); + } + + + /** + * Make an objective assessment as to whether a type may include an + * invariant or not. The purpose here is reduce the number of verification + * conditions generated with respect to constrained types. The algorithm is + * currently very simple. It essentially looks to see whether or not the + * type contains a nominal component. If so, the answer is "yes", otherwise + * the answer is "no". + * + * @return + */ + public boolean containsNominal(Type t, + Collection attributes) { + // FIXME: this is fundamentally broken in the case of recursive types. + // See Issue #298. + if (t instanceof Type.Any || t instanceof Type.Void + || t instanceof Type.Null || t instanceof Type.Bool + || t instanceof Type.Byte || t instanceof Type.Int) { + return false; + } else if (t instanceof Type.Array) { + Type.Array lt = (Type.Array) t; + return containsNominal(lt.element(), attributes); + } else if (t instanceof Type.Record) { + Type.Record rt = (Type.Record) t; + for (Type field : rt.fields().values()) { + if (containsNominal(field, attributes)) { + return true; + } + } + return false; + } else if (t instanceof Type.Reference) { + Type.Reference lt = (Type.Reference) t; + return containsNominal(lt.element(), attributes); + } else if (t instanceof Type.Union) { + Type.Union tu = (Type.Union) t; + for (Type te : tu.bounds()) { + if (containsNominal(te, attributes)) { + return true; + } + } + return false; + } else if (t instanceof Type.Negation) { + Type.Negation nt = (Type.Negation) t; + return containsNominal(nt.element(), attributes); + } else if (t instanceof Type.FunctionOrMethod) { + Type.FunctionOrMethod ft = (Type.FunctionOrMethod) t; + for (Type pt : ft.params()) { + if (containsNominal(pt, attributes)) { + return true; + } + } + for (Type pt : ft.returns()) { + if (containsNominal(pt, attributes)) { + return true; + } + } + return false; + } else if (t instanceof Type.Nominal) { + return true; + } else { + internalFailure("unknown type encountered (" + + t.getClass().getName() + ")", filename, attributes); + return false; + } + } + + /** + * Generate verification conditions to enforce the necessary preconditions + * for a given bytecode. For example, to protect against division by zero or + * an out-of-bounds access. + * + * @param code + * @param branch + * @param branches + * @param block + */ + public Pair[] getPreconditions(Code code, VcBranch branch, + Type[] environment, CodeForest forest) { + // + try { + switch (code.opcode()) { + case Code.OPCODE_div: + case Code.OPCODE_rem: + return divideByZeroCheck((Codes.BinaryOperator) code, branch); + case Code.OPCODE_indexof: + return indexOutOfBoundsChecks((Codes.IndexOf) code, branch); + case Code.OPCODE_arrygen: + return arrayGeneratorChecks((Codes.ArrayGenerator) code, branch); + case Code.OPCODE_update: + return updateChecks((Codes.Update) code, branch); + case Code.OPCODE_invoke: + return preconditionCheck((Codes.Invoke) code, branch, environment, forest); + } + return new Pair[0]; + } catch (Exception e) { + internalFailure(e.getMessage(), filename, e); + return null; // deadcode + } + } + + /** + * Generate preconditions to protected against a possible divide by zero. + * This essentially boils down to ensureing the divisor is non-zero. + * + * @param binOp + * --- The division or remainder bytecode + * @param branch + * --- The branch the division is on. + * @return + */ + public Pair[] divideByZeroCheck(Codes.BinaryOperator binOp, VcBranch branch) { + Expr rhs = branch.read(binOp.operand(1)); + Value zero; + if (binOp.type(0) instanceof Type.Int) { + zero = Value.Integer(BigInteger.ZERO); + } else { + zero = Value.Decimal(BigDecimal.ZERO); + } + Expr.Constant constant = new Expr.Constant(zero, rhs.attributes()); + return new Pair[] { + new Pair("division by zero", new Expr.Binary(Expr.Binary.Op.NEQ, rhs, constant, rhs.attributes())) }; + } + + /** + * Generate preconditions necessary to protect against an out-of-bounds + * access. For lists, this means ensuring the index is non-negative and less + * than the list length. + * + * @param code + * --- The indexOf bytecode + * @param branch + * --- The branch the bytecode is on. + * @return + */ + public Pair[] indexOutOfBoundsChecks(Codes.IndexOf code, VcBranch branch) { + if (code.type(0) instanceof Type.EffectiveArray) { + Expr src = branch.read(code.operand(0)); + Expr idx = branch.read(code.operand(1)); + Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), + idx.attributes()); + Expr length = new Expr.Unary(Expr.Unary.Op.LENGTHOF, src, + idx.attributes()); + return new Pair[] { + new Pair("index out of bounds (negative)", new Expr.Binary( + Expr.Binary.Op.GTEQ, idx, zero, idx.attributes())), + new Pair("index out of bounds (not less than length)", + new Expr.Binary(Expr.Binary.Op.LT, idx, length, + idx.attributes())), }; + } else { + // FIXME: should do something here! At a minimum, generate a warning + // that this has not been implemented yet. + return new Pair[0]; + } + } + + /** + * Generate preconditions necessary to protect against a negative array + * size. + * + * @param code + * --- The array generator bytecode + * @param branch + * --- The branch the bytecode is on. + * @return + */ + public Pair[] arrayGeneratorChecks(Codes.ArrayGenerator code, VcBranch branch) { + Expr idx = branch.read(code.operand(1)); + Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), + idx.attributes()); + return new Pair[] { + new Pair("negative length possible", new Expr.Binary( + Expr.Binary.Op.GTEQ, idx, zero, idx.attributes())) + }; + } + + /** + * Generate preconditions necessary to ensure the preconditions for a method + * or method invocation are met. + * + * @param code + * --- The invoke bytecode + * @param branch + * --- The branch on which the invocation is on. + * @param block + * --- The containing block of code. + * @return + * @throws Exception + */ + public Pair[] preconditionCheck(Codes.Invoke code, VcBranch branch, + Type[] environment, CodeForest forest) throws Exception { + ArrayList> preconditions = new ArrayList<>(); + // + // First, check for any potentially constrained types. + // + List attributes = forest.get(branch.pc()).attributes(); + List code_type_params = code.type(0).params(); + int[] code_operands = code.operands(); + for (int i = 0; i != code_operands.length; ++i) { + Type t = code_type_params.get(i); + if (containsNominal(t, attributes)) { + int operand = code_operands[i]; + Type rawType = expand(environment[operand], attributes); + Expr rawTest = new Expr.Is(branch.read(operand), convert(rawType, attributes)); + Expr nominalTest = new Expr.Is(branch.read(operand), convert(t, attributes)); + preconditions.add(new Pair("type invariant not satisfied (argument " + i + ")", + new Expr.Binary(Expr.Binary.Op.IMPLIES, rawTest, nominalTest))); + } + } + // + int numPreconditions = countPreconditions(code.name, code.type(0), forest, branch); + // + if (numPreconditions > 0) { + // First, read out the operands from the branch + Expr[] operands = new Expr[code_operands.length]; + for (int i = 0; i != code_operands.length; ++i) { + operands[i] = branch.read(code_operands[i]); + } + // To check the pre-condition holds after the method, we + // simply called the corresponding pre-condition macros. + String prefix = code.name.name() + "_requires_"; + + Expr argument = operands.length == 1 ? operands[0] : new Expr.Nary(Expr.Nary.Op.TUPLE, operands); + for (int i = 0; i < numPreconditions; ++i) { + Expr precondition = new Expr.Invoke(prefix + i, code.name.module(), Collections.EMPTY_LIST, argument); + preconditions.add(new Pair("precondition not satisfied", precondition)); + + } + } + return preconditions.toArray(new Pair[preconditions.size()]); + } + + /** + * Ensure all preconditions for an update bytecode are met. For example, + * that any array updates are within bounds, etc. + * + * @param code + * --- The update bytecode. + * @param branch + * --- The branch containing the update bytecode. + * @return + */ + public Pair[] updateChecks(Codes.Update code, VcBranch branch) { + ArrayList> preconditions = new ArrayList>(); + + Expr src = branch.read(code.target(0)); + + for (Codes.LVal lval : code) { + if (lval instanceof Codes.ArrayLVal) { + Codes.ArrayLVal lv = (Codes.ArrayLVal) lval; + Expr idx = branch.read(lv.indexOperand); + Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), + idx.attributes()); + Expr length = new Expr.Unary(Expr.Unary.Op.LENGTHOF, src, + idx.attributes()); + preconditions.add(new Pair("index out of bounds (negative)", + new Expr.Binary(Expr.Binary.Op.GTEQ, idx, zero, idx + .attributes()))); + preconditions.add(new Pair( + "index out of bounds (not less than length)", + new Expr.Binary(Expr.Binary.Op.LT, idx, length, idx + .attributes()))); + src = new Expr.IndexOf(src, idx); + } else if (lval instanceof Codes.RecordLVal) { + Codes.RecordLVal lv = (Codes.RecordLVal) lval; + ArrayList fields = new ArrayList(lv.rawType() + .fields().keySet()); + Collections.sort(fields); + Expr index = new Expr.Constant(Value.Integer(BigInteger + .valueOf(fields.indexOf(lv.field)))); + src = new Expr.IndexOf(src, index); + } else { + // FIXME: need to implement dereference operations. + } + } + + return preconditions.toArray(new Pair[preconditions.size()]); + } + + + /** + * Find the precondition associated with a given function or method. This + * maybe contained in the same file, or in a different file. This may + * require loading that file in memory to access this information. + * + * @param name + * --- Fully qualified name of function + * @param fun + * --- Type of fucntion. + * @param block + * --- Enclosing block (for debugging purposes). + * @param branch + * --- Enclosing branch (for debugging purposes). + * @return + * @throws Exception + */ + public int countPreconditions(NameID name, Type.FunctionOrMethod fun, + CodeForest forest, VcBranch branch) throws Exception { + Path.Entry e = builder.project().get(name.module(), WyilFile.ContentType); + if (e == null) { + syntaxError(errorMessage(ErrorMessages.RESOLUTION_ERROR, name.module().toString()), filename, + forest.get(branch.pc()).attributes()); + } + WyilFile m = e.read(); + WyilFile.FunctionOrMethod method = m.functionOrMethod(name.name(), fun); + + return method.preconditions().length; + } + + /** + * Determine the originating register number for this variable. This is made + * difficult because of variable versioning. All variable names and versions + * are encoded into a string of the form "n$v", where n is the variable name + * (A.K.A. the prefix) and "v" is the version. + * + * @param variable + * @return + */ + public static int determineRegister(String variable, String[] prefixes) { + // First determine the variable name (i.e. the prefix). + int dollarIndex = variable.indexOf('$'); + String prefix; + if (dollarIndex != -1) { + // In this case, the variable name was of the form "n$v" where n is + // the name, and v is the version. We don't need the version here, + // so strip it off. + prefix = variable.substring(0, dollarIndex); + } else { + // In this case, no version is given and, hence, there is nothing to + // strip off. + prefix = variable; + } + // Now, check whether this is a raw register identifier, or a named + // variable identifier. + if(prefix.startsWith("r%")) { + // This is a raw register identifier. Therefore, we can extract the + // register number directly. + return Integer.parseInt(prefix.substring(2)); + } else { + // This is a named varaible identifier. Therefore, we need to look + // through the known list of named variable prefixes to see whether + // or not we can find it (which we should be able to do). + for (int i = 0; i != prefixes.length; ++i) { + if (prefix.equals(prefixes[i])) { + return i; + } + } + // Should be impossible to get here. + throw new RuntimeException( + "Unreachable code reached whilst looking for: " + variable); + } + } + + private Type expand(Type t, Collection attributes) { + try { + return expander.getUnderlyingType(t); + } catch (ResolveError re) { + internalFailure(re.getMessage(), filename, attributes); + } catch (IOException re) { + internalFailure(re.getMessage(), filename, attributes); + } + return null; // dead-code + } + + /** + * Returns the prefix array which gives the names of all registers declared + * in a given block. + * + * @param d + * @return + */ + public static Pair parseRegisterDeclarations(CodeForest forest) { + List regs = forest.registers(); + String[] prefixes = new String[regs.size()]; + Type[] types = new Type[regs.size()]; + for (int i = 0; i != prefixes.length; ++i) { + CodeForest.Register d = regs.get(i); + prefixes[i] = d.name(); + types[i] = d.type(); + } + return new Pair<>(prefixes, types); + } +} diff --git a/modules/wyil/src/wyil/builders/Wyil2WyalBuilder.java b/modules/wyil/src/wyil/builders/Wyil2WyalBuilder.java index de144ac0c5..e912032192 100644 --- a/modules/wyil/src/wyil/builders/Wyil2WyalBuilder.java +++ b/modules/wyil/src/wyil/builders/Wyil2WyalBuilder.java @@ -31,9 +31,7 @@ import wybs.lang.Build; import wybs.lang.Builder; import wyfs.lang.Path; -import wyil.attributes.SourceLocationMap; import wyil.lang.*; -import wyil.util.AttributedCodeBlock; import wycc.util.Logger; import wycc.util.Pair; import wycs.syntax.Expr; diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 4a5849df86..6698f3cf17 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -1066,7 +1066,7 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob return Codes.IndexOf((Type.EffectiveArray)types[0],targets[0],operands[0],operands[1]); } }; - schemas[Code.OPCODE_listgen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Code.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.ArrayGenerator((Type.Array) types[0], targets[0], operands[0], operands[1]); } @@ -1075,7 +1075,7 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Nary Assignables // ========================================================================= - schemas[Code.OPCODE_newlist] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + schemas[Code.OPCODE_newarray] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.NewArray((Type.Array) types[0], targets[0], operands); } diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index aa0e206f27..682cd2260f 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -339,7 +339,6 @@ public boolean equals(Object o) { public static final int OPCODE_return = UNARY_OPERATOR+1; public static final int OPCODE_ifis = UNARY_OPERATOR+2; public static final int OPCODE_switch = UNARY_OPERATOR+3; - public static final int OPCODE_throw = UNARY_OPERATOR+4; // ========================================================================= // Unary Assignables @@ -386,14 +385,14 @@ public boolean equals(Object o) { public static final int OPCODE_lshr = BINARY_ASSIGNABLE+8; public static final int OPCODE_rshr = BINARY_ASSIGNABLE+9; public static final int OPCODE_indexof = BINARY_ASSIGNABLE+10; - public static final int OPCODE_listgen = BINARY_ASSIGNABLE+11; + public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+11; // ========================================================================= // Nary Assignables // ========================================================================= public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+12; - public static final int OPCODE_newlist = NARY_ASSIGNABLE+0; + public static final int OPCODE_newarray = NARY_ASSIGNABLE+0; public static final int OPCODE_newrecord = NARY_ASSIGNABLE+1; public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; public static final int OPCODE_indirectinvoke = NARY_ASSIGNABLE+3; diff --git a/modules/wyil/src/wyil/lang/CodeForest.java b/modules/wyil/src/wyil/lang/CodeForest.java index 47fbdb6cc0..c5598b1975 100644 --- a/modules/wyil/src/wyil/lang/CodeForest.java +++ b/modules/wyil/src/wyil/lang/CodeForest.java @@ -188,6 +188,14 @@ public int hashCode() { public Index next() { return new Index(block,offset+1); } + + public Index next(int i) { + return new Index(block,offset+i); + } + + public String toString() { + return block + ":" + offset; + } } /** diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index daef2a3850..e28c8df4fd 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -1244,6 +1244,10 @@ private IfIs(Type type, int leftOperand, Type rightOperand, String target) { public int opcode() { return OPCODE_ifis; } + + public Type rightOperand() { + return type(1); + } public IfIs relabel(Map labels) { String nlabel = labels.get(destination()); @@ -1642,7 +1646,7 @@ private ArrayGenerator(Type.Array type, int target, int element, int count) { } public int opcode() { - return OPCODE_listgen; + return OPCODE_arrygen; } @Override @@ -1886,6 +1890,10 @@ public boolean equals(Object o) { return false; } + public int[] modifiedOperands() { + return targets(); + } + @Override public Loop clone(int[] nTargets, int[] nOperands) { return new Loop(nTargets, block, nOperands); @@ -2305,7 +2313,7 @@ private NewArray(Type.Array type, int target, int[] operands) { } public int opcode() { - return OPCODE_newlist; + return OPCODE_newarray; } @Override diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 92e83528ed..9d36723a13 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -42,7 +42,6 @@ import wyil.attributes.SourceLocation; import wyil.lang.*; import wyil.lang.Constant; -import wyil.util.AttributedCodeBlock; import wyil.util.TypeExpander; import static wyil.util.ErrorMessages.internalFailure; import wyjc.util.WyjcBuildTask; @@ -92,11 +91,6 @@ public class Wyil2JavaBuilder implements Builder { */ protected String filename; - /** - * Root of block being translated - */ - protected AttributedCodeBlock rootBlock; - /** * Type of enclosing class being generated */ @@ -367,11 +361,11 @@ private ClassFile.Method build(WyilFile.Type td) { translateInvariantTest(falseBranch, td.type(), 0, 1, constants, bytecodes); // Second, generate code for invariant (if applicable). - AttributedCodeBlock invariant = td.invariant(); - if (invariant != null) { - invariant = patchInvariantBlock(falseBranch, invariant); - translate(invariant, 1, bytecodes); - } +// CodeForest invariant = td.invariant(); +// if (invariant.numBlocks() > 0) { +// invariant = patchInvariantBlock(falseBranch, invariant); +// translate(invariant, 1, bytecodes); +// } bytecodes.add(new Bytecode.LoadConst(true)); bytecodes.add(new Bytecode.Return(new JvmType.Bool())); bytecodes.add(new Bytecode.Label(falseBranch)); @@ -390,30 +384,30 @@ private ClassFile.Method build(WyilFile.Type td) { * bytecodes are replaced with returning false, and all return bytecodes are * replaced with returning true. */ - private AttributedCodeBlock patchInvariantBlock(String falseBranch, - AttributedCodeBlock block) { - AttributedCodeBlock copy = new AttributedCodeBlock(block.bytecodes(), - block.attributes()); - patchInvariantBlockHelper(falseBranch, copy); - return copy; - } - - private void patchInvariantBlockHelper(String falseBranch, CodeForest block) { - for (int i = 0; i != block.size(); ++i) { - // This is still a valid index - Code c = block.get(i); - - if (c instanceof Codes.Return) { - // first patch point - block.set(i, Codes.Nop); - } else if (c instanceof Codes.Fail) { - // second patch point - block.set(i, Codes.Goto(falseBranch)); - } else if (c instanceof Code.Compound) { - patchInvariantBlockHelper(falseBranch, (Code.Compound) c); - } - } - } +// private AttributedCodeBlock patchInvariantBlock(String falseBranch, +// CodeForest.Block forest) { +// AttributedCodeBlock copy = new AttributedCodeBlock(block.bytecodes(), +// block.attributes()); +// patchInvariantBlockHelper(falseBranch, copy); +// return copy; +// } +// +// private void patchInvariantBlockHelper(String falseBranch, CodeForest block) { +// for (int i = 0; i != block.size(); ++i) { +// // This is still a valid index +// Code c = block.get(i); +// +// if (c instanceof Codes.Return) { +// // first patch point +// block.set(i, Codes.Nop); +// } else if (c instanceof Codes.Fail) { +// // second patch point +// block.set(i, Codes.Goto(falseBranch)); +// } else if (c instanceof Code.Compound) { +// patchInvariantBlockHelper(falseBranch, (Code.Compound) c); +// } +// } +// } private List build(WyilFile.FunctionOrMethod method) { ArrayList methods = new ArrayList(); @@ -515,8 +509,8 @@ private ClassFile.Method translate(WyilFile.FunctionOrMethod method) { lineNumbers = new ArrayList(); ArrayList bytecodes = new ArrayList(); - AttributedCodeBlock block = method.body(); - translate(block, block.numSlots(), bytecodes); + CodeForest forest = method.code(); + translate(method.body(), forest.numRegisters(), forest, bytecodes); jasm.attributes.Code code = new jasm.attributes.Code(bytecodes, Collections.EMPTY_LIST, cm); if (!lineNumbers.isEmpty()) { @@ -537,10 +531,8 @@ private ClassFile.Method translate(WyilFile.FunctionOrMethod method) { * @param bytecodes * --- list to insert bytecodes into * */ - private void translate(AttributedCodeBlock blk, int freeSlot, - ArrayList bytecodes) { - rootBlock = blk; - translate(null, blk, freeSlot, bytecodes); + private void translate(int blk, int freeSlot, CodeForest forest, ArrayList bytecodes) { + translate(new CodeForest.Index(blk, 0), freeSlot, forest, bytecodes); } /** @@ -557,19 +549,17 @@ private void translate(AttributedCodeBlock blk, int freeSlot, * @param bytecodes * List of bytecodes being accumulated */ - private void translate(CodeForest.Index parentIndex, CodeForest block, - int freeSlot, ArrayList bytecodes) { - + private void translate(CodeForest.Index pc, int freeSlot, CodeForest forest, ArrayList bytecodes) { + CodeForest.Block block = forest.get(pc.block()); for (int i = 0; i != block.size(); ++i) { - CodeForest.Index index = new CodeForest.Index(parentIndex, i); - SourceLocation loc = rootBlock.attribute(index, - SourceLocation.class); + CodeForest.Index index = new CodeForest.Index(pc.block(), i); + SourceLocation loc = forest.get(index).attribute(SourceLocation.class); if (loc != null) { // FIXME: figure our how to get line number! // lineNumbers.add(new // LineNumberTable.Entry(bytecodes.size(),loc.line)); } - freeSlot = translate(index, block.get(i), freeSlot, bytecodes); + freeSlot = translate(index, block.get(i).code(), freeSlot, forest, bytecodes); } } @@ -578,9 +568,9 @@ private void translate(CodeForest.Index parentIndex, CodeForest block, * bytecodes. The bytecode index is given to help with debugging (i.e. to * extract attributes associated with the given bytecode). * - * @param index + * @param pc * The index of the WyIL bytecode being translated in the - * rootBlock. + * forest. * @param code * The WyIL bytecode being translated. * @param freeSlot @@ -589,86 +579,83 @@ private void translate(CodeForest.Index parentIndex, CodeForest block, * The list of bytecodes being accumulated * @return */ - private int translate(CodeForest.Index index, Code code, int freeSlot, + private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest forest, ArrayList bytecodes) { try { if (code instanceof Codes.BinaryOperator) { - translate(index, (Codes.BinaryOperator) code, freeSlot, - bytecodes); + translate(pc, (Codes.BinaryOperator) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Convert) { - translate(index, (Codes.Convert) code, freeSlot, bytecodes); + translate(pc, (Codes.Convert) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Const) { - translate(index, (Codes.Const) code, freeSlot, bytecodes); + translate(pc, (Codes.Const) code, freeSlot, forest,bytecodes); } else if (code instanceof Codes.Debug) { - translate(index, (Codes.Debug) code, freeSlot, bytecodes); + translate(pc, (Codes.Debug) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.AssertOrAssume) { - translate(index, (Codes.AssertOrAssume) code, freeSlot, + translate(pc, (Codes.AssertOrAssume) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Fail) { - translate(index, (Codes.Fail) code, freeSlot, bytecodes); + translate(pc, (Codes.Fail) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.FieldLoad) { - translate(index, (Codes.FieldLoad) code, freeSlot, bytecodes); + translate(pc, (Codes.FieldLoad) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Quantify) { - freeSlot = translate(index, (Codes.Quantify) code, freeSlot, - bytecodes); + freeSlot = translate(pc, (Codes.Quantify) code, freeSlot, + forest, bytecodes); } else if (code instanceof Codes.Goto) { - translate(index, (Codes.Goto) code, freeSlot, bytecodes); + translate(pc, (Codes.Goto) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.If) { - translateIfGoto(index, (Codes.If) code, freeSlot, bytecodes); + translateIfGoto(pc, (Codes.If) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.IfIs) { - translate(index, (Codes.IfIs) code, freeSlot, bytecodes); + translate(pc, (Codes.IfIs) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.IndirectInvoke) { - translate(index, (Codes.IndirectInvoke) code, freeSlot, - bytecodes); + translate(pc, (Codes.IndirectInvoke) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Invoke) { - translate(index, (Codes.Invoke) code, freeSlot, bytecodes); + translate(pc, (Codes.Invoke) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Invert) { - translate(index, (Codes.Invert) code, freeSlot, bytecodes); + translate(pc, (Codes.Invert) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Label) { - translate(index, (Codes.Label) code, freeSlot, bytecodes); + translate(pc, (Codes.Label) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.ArrayGenerator) { - translate(index, (Codes.ArrayGenerator) code, freeSlot, bytecodes); + translate(pc, (Codes.ArrayGenerator) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Lambda) { - translate(index, (Codes.Lambda) code, freeSlot, bytecodes); + translate(pc, (Codes.Lambda) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.LengthOf) { - translate(index, (Codes.LengthOf) code, freeSlot, bytecodes); + translate(pc, (Codes.LengthOf) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.IndexOf) { - translate(index, (Codes.IndexOf) code, freeSlot, bytecodes); + translate(pc, (Codes.IndexOf) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Assign) { - translate(index, (Codes.Assign) code, freeSlot, bytecodes); + translate(pc, (Codes.Assign) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Loop) { - translate(index, (Codes.Loop) code, freeSlot, bytecodes); + translate(pc, (Codes.Loop) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Move) { - translate(index, (Codes.Move) code, freeSlot, bytecodes); + translate(pc, (Codes.Move) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Update) { - translate(index, (Codes.Update) code, freeSlot, bytecodes); + translate(pc, (Codes.Update) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.NewArray) { - translate(index, (Codes.NewArray) code, freeSlot, bytecodes); + translate(pc, (Codes.NewArray) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.NewRecord) { - translate(index, (Codes.NewRecord) code, freeSlot, bytecodes); + translate(pc, (Codes.NewRecord) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.UnaryOperator) { - translate(index, (Codes.UnaryOperator) code, freeSlot, - bytecodes); + translate(pc, (Codes.UnaryOperator) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Dereference) { - translate(index, (Codes.Dereference) code, freeSlot, bytecodes); + translate(pc, (Codes.Dereference) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Return) { - translate(index, (Codes.Return) code, freeSlot, bytecodes); + translate(pc, (Codes.Return) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Nop) { // do nothing } else if (code instanceof Codes.Switch) { - translate(index, (Codes.Switch) code, freeSlot, bytecodes); + translate(pc, (Codes.Switch) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.NewObject) { - translate(index, (Codes.NewObject) code, freeSlot, bytecodes); + translate(pc, (Codes.NewObject) code, freeSlot, forest, bytecodes); } else { internalFailure("unknown wyil code encountered (" + code + ")", filename, - rootBlock.attribute(index, SourceLocation.class)); + forest.get(pc).attribute(SourceLocation.class)); } } catch (Exception ex) { internalFailure(ex.getMessage(), filename, ex, - rootBlock.attribute(index, SourceLocation.class)); + forest.get(pc).attribute(SourceLocation.class)); } return freeSlot; @@ -676,20 +663,17 @@ private int translate(CodeForest.Index index, Code code, int freeSlot, private void translate(CodeForest.Index index, Codes.AssertOrAssume c, - int freeSlot, ArrayList bytecodes) { + int freeSlot, CodeForest forest, ArrayList bytecodes) { + CodeForest.Index pc = new CodeForest.Index(c.block(), 0); if(c instanceof Codes.Invariant) { // essentially a no-op for now - } else if(c instanceof Codes.Assert) { - Codes.Assert ca = (Codes.Assert) c; - translate(index, (CodeForest) c, freeSlot, bytecodes); } else { - Codes.Assume ca = (Codes.Assume) c; - translate(index, (CodeForest) c, freeSlot, bytecodes); + translate(pc, freeSlot, forest, bytecodes); } } private void translate(CodeForest.Index index, Codes.Const c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { Constant constant = c.constant; JvmType jt = convertUnderlyingType(constant.type()); @@ -706,16 +690,18 @@ private void translate(CodeForest.Index index, Codes.Const c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(), jt)); } - private void translate(CodeForest.Index index, Codes.Convert c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Convert c, int freeSlot, CodeForest forest, + ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); - addCoercion(c.type(0), c.result, freeSlot, constants, bytecodes); - bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.result))); + addCoercion(c.type(0), c.result(), freeSlot, constants, bytecodes); + bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.result()))); } - private void translate(CodeForest.Index index, Codes.Update code, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Update code, int freeSlot, CodeForest forest, + ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(code.target(0), convertUnderlyingType(code.type(0)))); translateUpdate(code.iterator(), code, bytecodes); - bytecodes.add(new Bytecode.Store(code.target(0), convertUnderlyingType(code.afterType))); + bytecodes.add(new Bytecode.Store(code.target(0), convertUnderlyingType(code.afterType()))); } /** @@ -856,7 +842,7 @@ private void translateUpdate(Codes.ReferenceLVal lval, Iterator iter } private void translate(CodeForest.Index index, Codes.Return c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { JvmType jt = null; int[] operands = c.operands(); if(operands.length == 1) { @@ -871,7 +857,7 @@ private void translate(CodeForest.Index index, Codes.Return c, int freeSlot, } private void translate(CodeForest.Index index, Codes.Switch c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { ArrayList> cases = new ArrayList(); boolean canUseSwitchBytecode = true; @@ -907,23 +893,22 @@ private void translate(CodeForest.Index index, Codes.Switch c, int freeSlot, String target = p.second(); translate(value, freeSlot, bytecodes); bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); - translateIfGoto(index, value.type(), Codes.Comparator.EQ, - target, freeSlot + 1, bytecodes); + translateIfGoto(index, value.type(), Codes.Comparator.EQ, target, freeSlot + 1, forest, bytecodes); } bytecodes.add(new Bytecode.Goto(c.defaultTarget)); } } - private void translateIfGoto(CodeForest.Index index, Codes.If code, int freeSlot, ArrayList bytecodes) { + private void translateIfGoto(CodeForest.Index index, Codes.If code, int freeSlot, CodeForest forest, + ArrayList bytecodes) { JvmType jt = convertUnderlyingType(code.type(0)); bytecodes.add(new Bytecode.Load(code.operand(0), jt)); bytecodes.add(new Bytecode.Load(code.operand(1), jt)); - translateIfGoto(index, code.type(0), code.op, code.target, freeSlot, bytecodes); + translateIfGoto(index, code.type(0), code.op, code.destination(), freeSlot, forest, bytecodes); } - private void translateIfGoto(CodeForest.Index index, Type c_type, - Codes.Comparator cop, String target, int freeSlot, - ArrayList bytecodes) { + private void translateIfGoto(CodeForest.Index index, Type c_type, Codes.Comparator cop, String target, int freeSlot, + CodeForest forest, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c_type); // Just use the Object.equals() method, followed @@ -998,7 +983,7 @@ private void translateIfGoto(CodeForest.Index index, Type c_type, } default: internalFailure("unknown if condition encountered", filename, - rootBlock.attribute(index, SourceLocation.class)); + forest.get(index).attribute(SourceLocation.class)); return; } @@ -1007,7 +992,7 @@ private void translateIfGoto(CodeForest.Index index, Type c_type, } private void translate(CodeForest.Index index, Codes.IfIs c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { // In this case, we're updating the type of a local variable. To // make this work, we must update the JVM type of that slot as well @@ -1036,15 +1021,14 @@ private void translate(CodeForest.Index index, Codes.IfIs c, int freeSlot, // // First, calculate the underlying and maximal consumed types, which // we'll need later. + Type targetType = c.rightOperand(); Type maximalConsumedType; Type underlyingType; try { - maximalConsumedType = expander - .getMaximallyConsumedType(c.rightOperand); - underlyingType = expander.getUnderlyingType(c.rightOperand); + maximalConsumedType = expander.getMaximallyConsumedType(targetType); + underlyingType = expander.getUnderlyingType(targetType); } catch (Exception e) { - internalFailure("error computing maximally consumed type: " - + c.rightOperand, filename, e); + internalFailure("error computing maximally consumed type: " + targetType, filename, e); return; } @@ -1070,8 +1054,8 @@ private void translate(CodeForest.Index index, Codes.IfIs c, int freeSlot, // Fourth handle constrained types by invoking a function which will // execute any and all constraints associated with the type. For // recursive types, this may result in recursive calls. - translateInvariantTest(falseLabel, c.rightOperand, c.operand(0), freeSlot, constants, bytecodes); - bytecodes.add(new Bytecode.Goto(c.target)); + translateInvariantTest(falseLabel, targetType, c.operand(0), freeSlot, constants, bytecodes); + bytecodes.add(new Bytecode.Goto(c.destination())); // Finally, construct false branch and retype the variable on the false // branch to ensure it has the most precise type we know at this point. bytecodes.add(new Bytecode.Label(falseLabel)); @@ -1224,49 +1208,42 @@ private void translateInvariantTest(String falseTarget, Type type, } } - private void translate(CodeForest.Index index, Codes.Loop c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Loop c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - // Allocate header label for loop String loopHeader = freshLabel(); bytecodes.add(new Bytecode.Label(loopHeader)); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(index, (CodeForest) c, freeSlot, bytecodes); + translate(new CodeForest.Index(index.block(), 0), freeSlot, forest, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); } private int translate(CodeForest.Index index, Codes.Quantify c, int freeSlot, - ArrayList bytecodes) { - - bytecodes.add(new Bytecode.Load(c.startOperand,WHILEYINT)); - bytecodes.add(new Bytecode.Load(c.endOperand,WHILEYINT)); + CodeForest forest, ArrayList bytecodes) { + bytecodes.add(new Bytecode.Load(c.startOperand(), WHILEYINT)); + bytecodes.add(new Bytecode.Load(c.endOperand(), WHILEYINT)); JvmType.Function ftype = new JvmType.Function(WHILEYARRAY, WHILEYINT, WHILEYINT); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "range", ftype, - Bytecode.InvokeMode.STATIC)); + bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "range", ftype, Bytecode.InvokeMode.STATIC)); ftype = new JvmType.Function(JAVA_UTIL_ITERATOR); - bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_COLLECTION, "iterator", - ftype, Bytecode.InvokeMode.INTERFACE)); + bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_COLLECTION, "iterator", ftype, Bytecode.InvokeMode.INTERFACE)); bytecodes.add(new Bytecode.Store(freeSlot, JAVA_UTIL_ITERATOR)); String loopHeader = freshLabel(); String loopExit = freshLabel(); bytecodes.add(new Bytecode.Label(loopHeader)); ftype = new JvmType.Function(T_BOOL); bytecodes.add(new Bytecode.Load(freeSlot, JAVA_UTIL_ITERATOR)); - bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext", ftype, - Bytecode.InvokeMode.INTERFACE)); + bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext", ftype, Bytecode.InvokeMode.INTERFACE)); bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, loopExit)); bytecodes.add(new Bytecode.Load(freeSlot, JAVA_UTIL_ITERATOR)); ftype = new JvmType.Function(JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next", ftype, - Bytecode.InvokeMode.INTERFACE)); + bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next", ftype, Bytecode.InvokeMode.INTERFACE)); addReadConversion(Type.T_INT, bytecodes); - bytecodes.add(new Bytecode.Store(c.indexOperand, - convertUnderlyingType(Type.T_INT))); + bytecodes.add(new Bytecode.Store(c.indexOperand(), convertUnderlyingType(Type.T_INT))); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(index, (CodeForest) c, freeSlot + 1, bytecodes); + translate(new CodeForest.Index(index.block(), 0), freeSlot + 1, forest, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); bytecodes.add(new Bytecode.Label(loopExit)); @@ -1274,38 +1251,38 @@ private int translate(CodeForest.Index index, Codes.Quantify c, int freeSlot, return freeSlot; } - private void translate(CodeForest.Index index, Codes.Goto c, int freeSlot, + private void translate(CodeForest.Index index, Codes.Goto c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - bytecodes.add(new Bytecode.Goto(c.target)); + bytecodes.add(new Bytecode.Goto(c.destination())); } private void translate(CodeForest.Index index, Codes.Label c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Label(c.label)); } private void translate(CodeForest.Index index, Codes.Debug c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { JvmType.Function ftype = new JvmType.Function(T_VOID, WHILEYARRAY); bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "print", ftype, Bytecode.InvokeMode.STATIC)); } private void translate(CodeForest.Index index, Codes.Assign c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.Load(c.operand(0), jt)); bytecodes.add(new Bytecode.Store(c.target(0), jt)); } private void translate(CodeForest.Index index, Codes.Move c, int freeSlot, - ArrayList bytecodes) { + CodeForest forest, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.Load(c.operand(0), jt)); bytecodes.add(new Bytecode.Store(c.target(0), jt)); } - private void translate(CodeForest.Index index, Codes.ArrayGenerator c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.ArrayGenerator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType elementType = convertUnderlyingType(c.type(0).element()); bytecodes.add(new Bytecode.Load(c.operand(0), elementType)); addWriteConversion(c.type(0).element(), bytecodes); @@ -1315,7 +1292,7 @@ private void translate(CodeForest.Index index, Codes.ArrayGenerator c, int freeS bytecodes.add(new Bytecode.Store(c.target(0), WHILEYARRAY)); } - private void translate(CodeForest.Index index, Codes.LengthOf c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.LengthOf c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType((Type) c.type(0)))); JvmType.Clazz ctype = JAVA_LANG_OBJECT; JvmType.Function ftype = new JvmType.Function(WHILEYINT); @@ -1323,7 +1300,7 @@ private void translate(CodeForest.Index index, Codes.LengthOf c, int freeSlot, A bytecodes.add(new Bytecode.Store(c.target(0), WHILEYINT)); } - private void translate(CodeForest.Index index, Codes.IndexOf c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.IndexOf c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); @@ -1332,7 +1309,7 @@ private void translate(CodeForest.Index index, Codes.IndexOf c, int freeSlot, Ar bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.type(0).element()))); } - private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.LoadConst("runtime fault encountered")); @@ -1341,7 +1318,7 @@ private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, Array bytecodes.add(new Bytecode.Throw()); } - private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYRECORD)); bytecodes.add(new Bytecode.LoadConst(c.field)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYRECORD, JAVA_LANG_STRING); @@ -1350,8 +1327,8 @@ private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.fieldType()))); } - private void translate(CodeForest.Index index, Codes.BinaryOperator c, - int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeSlot, CodeForest forest, + ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); @@ -1425,13 +1402,13 @@ private void translate(CodeForest.Index index, Codes.BinaryOperator c, break; default: internalFailure("unknown binary expression encountered", filename, - rootBlock.attribute(index, SourceLocation.class)); + forest.get(index).attribute(SourceLocation.class)); } bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeForest.Index index, Codes.Invert c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Invert c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.Load(c.operand(0), type)); JvmType.Function ftype = new JvmType.Function(type); @@ -1439,8 +1416,8 @@ private void translate(CodeForest.Index index, Codes.Invert c, int freeSlot, Arr bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeForest.Index index, Codes.UnaryOperator c, - int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.UnaryOperator c, int freeSlot, CodeForest forest, + ArrayList bytecodes) { JvmType srcType = convertUnderlyingType(c.type(0)); JvmType targetType = null; String name = null; @@ -1456,7 +1433,7 @@ private void translate(CodeForest.Index index, Codes.UnaryOperator c, bytecodes.add(new Bytecode.Store(c.target(0), targetType)); } - private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.New(WHILEYOBJECT)); bytecodes.add(new Bytecode.Dup(WHILEYOBJECT)); @@ -1467,8 +1444,8 @@ private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeForest.Index index, Codes.Dereference c, - int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Dereference c, int freeSlot, CodeForest forest, + ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT); bytecodes.add(new Bytecode.Load(c.operand(0), type)); @@ -1479,7 +1456,7 @@ private void translate(CodeForest.Index index, Codes.Dereference c, bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.type(0).element()))); } - protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, ArrayList bytecodes) { + protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(WHILEYARRAY)); bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); bytecodes.add(new Bytecode.LoadConst(c.operands().length)); @@ -1496,8 +1473,8 @@ protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), WHILEYARRAY)); } - private void translate(CodeForest.Index index, Codes.NewRecord code, - int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.NewRecord code, int freeSlot, CodeForest forest, + ArrayList bytecodes) { construct(WHILEYRECORD, freeSlot, bytecodes); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); @@ -1519,7 +1496,8 @@ private void translate(CodeForest.Index index, Codes.NewRecord code, bytecodes.add(new Bytecode.Store(code.target(0), WHILEYRECORD)); } - private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, CodeForest forest, + ArrayList bytecodes) { // First, build and register lambda class which calls the given function // or method. This class will extend class wyjc.runtime.WyLambda. @@ -1582,7 +1560,8 @@ private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, Arr bytecodes.add(new Bytecode.Store(c.target(0), clazz)); } - private void translate(CodeForest.Index index, Codes.Invoke c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.Invoke c, int freeSlot, CodeForest forest, + ArrayList bytecodes) { for (int i = 0; i != c.operands().length; ++i) { int register = c.operands()[i]; @@ -1609,7 +1588,7 @@ private void translate(CodeForest.Index index, Codes.Invoke c, int freeSlot, Arr } } - private void translate(CodeForest.Index index, Codes.IndirectInvoke c, int freeSlot, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Codes.IndirectInvoke c, int freeSlot, CodeForest forest, ArrayList bytecodes) { Type.FunctionOrMethod ft = c.type(0); JvmType.Clazz owner = (JvmType.Clazz) convertUnderlyingType(ft); bytecodes.add(new Bytecode.Load(c.reference(), convertUnderlyingType(ft))); @@ -1634,7 +1613,7 @@ private void translate(CodeForest.Index index, Codes.IndirectInvoke c, int freeS // Multiple return values, which must be encoded into an object // array. internalFailure("multiple returns not supported", filename, - rootBlock.attribute(index, SourceLocation.class)); + forest.get(index).attribute(SourceLocation.class)); } } From e10801194d0625c4ad648e8139af4d8cf83e4539 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Fri, 18 Mar 2016 11:55:26 +1300 Subject: [PATCH 08/43] WyJC: Update WyJC This updates WyJC to now correctly compile code and pass tests. There seems to have been some problem with tests that were also failing in the previous release. See #602 This also updates the RuntimeValidTests so that it now searches in the tests directory automatically to find test cases. This means that we no longer have to maintain the long list of test cases and make sure they are in sync, etc. --- modules/wyil/src/wyil/io/WyilFilePrinter.java | 10 +- modules/wyil/src/wyil/lang/CodeForest.java | 9 + modules/wyil/src/wyil/lang/Codes.java | 8 +- modules/wyil/src/wyil/util/TypeExpander.java | 2 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 178 +- modules/wyjc/src/wyjc/runtime/WyLambda.java | 2 + .../src/wyjc/testing/RuntimeValidTests.java | 2490 +---------------- 7 files changed, 223 insertions(+), 2476 deletions(-) mode change 100755 => 100644 modules/wyjc/src/wyjc/testing/RuntimeValidTests.java diff --git a/modules/wyil/src/wyil/io/WyilFilePrinter.java b/modules/wyil/src/wyil/io/WyilFilePrinter.java index e58b1a53ed..38081ce9c8 100755 --- a/modules/wyil/src/wyil/io/WyilFilePrinter.java +++ b/modules/wyil/src/wyil/io/WyilFilePrinter.java @@ -124,13 +124,19 @@ public void apply(WyilFile module) throws IOException { String t_str; t_str = t.toString(); writeModifiers(td.modifiers(),out); - out.println("type " + td.name() + " : " + t_str); + out.println("type " + td.name() + " : " + t_str); + CodeForest forest = td.invariant(); + for(int i=0;i!=forest.numRoots();++i) { + out.println("where:"); + write(0, forest.getRoot(i), forest, out); + out.println(); + } out.println(); } for(FunctionOrMethod md : module.functionOrMethods()) { write(md,out); - out.println(); + out.println(); } out.flush(); } diff --git a/modules/wyil/src/wyil/lang/CodeForest.java b/modules/wyil/src/wyil/lang/CodeForest.java index c5598b1975..3d52f95259 100644 --- a/modules/wyil/src/wyil/lang/CodeForest.java +++ b/modules/wyil/src/wyil/lang/CodeForest.java @@ -31,6 +31,15 @@ public CodeForest() { this(Collections.EMPTY_LIST); } + public CodeForest(CodeForest forest) { + this.registers = new ArrayList(forest.registers); + this.roots = new ArrayList(forest.roots); + this.blocks = new ArrayList(); + for(int i=0;i!=forest.blocks.size();++i) { + this.blocks.add(new Block(forest.blocks.get(i))); + } + } + public CodeForest(List registers) { this.registers = new ArrayList(registers); this.roots = new ArrayList(); diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index e28c8df4fd..fc4ca0553b 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -2038,8 +2038,7 @@ public LVal next() { iter = rec.fields().get(field); return new RecordLVal(rec, field); } else { - throw new IllegalArgumentException( - "Invalid type for Update"); + throw new IllegalArgumentException("Invalid type for Update: " + iter); } } @@ -2173,7 +2172,7 @@ public Type rhs() { int fieldIndex = 0; for (int i = 0; i != level(); ++i) { - if (Type.isSubtype(Type.Reference(Type.T_ANY), iter)) { + if (iter instanceof Type.Reference) { Type.Reference proc = Type.effectiveReference(iter); iter = proc.element(); } else if (iter instanceof Type.EffectiveArray) { @@ -2184,8 +2183,7 @@ public Type rhs() { String field = fields.get(fieldIndex++); iter = rec.fields().get(field); } else { - throw new IllegalArgumentException( - "Invalid type for Update"); + throw new IllegalArgumentException("Invalid type for Update: " + iter); } } return iter; diff --git a/modules/wyil/src/wyil/util/TypeExpander.java b/modules/wyil/src/wyil/util/TypeExpander.java index 56c3fcaea0..d04b421634 100644 --- a/modules/wyil/src/wyil/util/TypeExpander.java +++ b/modules/wyil/src/wyil/util/TypeExpander.java @@ -154,7 +154,7 @@ public int getTypeHelper(Type type, boolean maximallyConsumed, // At this point, need to find the corresponding declatation. WyilFile mi = project.get(nid.module(),WyilFile.ContentType).read(); WyilFile.Type td = mi.type(nid.name()); - if(maximallyConsumed && td.invariant() != null) { + if(maximallyConsumed && td.invariant().numBlocks() > 0) { // In this specially case, we have a constrained type // and we are attempting to compute the maximally // consumed type. This type is not fully consumed as it diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 9d36723a13..6d3b192de6 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -353,19 +353,17 @@ private ClassFile.Method build(WyilFile.Type td) { ClassFile.Method cm = new ClassFile.Method(td.name() + "$typeof", funType, modifiers); ArrayList bytecodes = new ArrayList(); - // First, generate code for testing elements of type (if any) String falseBranch = freshLabel(); // FIXME: this is inefficient in cases where there are no invariants in // component types (e.g. there are no component types). - translateInvariantTest(falseBranch, td.type(), 0, 1, constants, - bytecodes); + translateInvariantTest(falseBranch, td.type(), 0, 1, constants, bytecodes); // Second, generate code for invariant (if applicable). -// CodeForest invariant = td.invariant(); -// if (invariant.numBlocks() > 0) { -// invariant = patchInvariantBlock(falseBranch, invariant); -// translate(invariant, 1, bytecodes); -// } + // FIXME: use of patchInvariantBlock is not ideal + CodeForest invariant = patchInvariantBlock(falseBranch, td.invariant()); + for(int i=0;i!=invariant.numRoots();++i) { + translate(invariant.getRoot(i), 1, invariant, bytecodes); + } bytecodes.add(new Bytecode.LoadConst(true)); bytecodes.add(new Bytecode.Return(new JvmType.Bool())); bytecodes.add(new Bytecode.Label(falseBranch)); @@ -376,38 +374,38 @@ private ClassFile.Method build(WyilFile.Type td) { new ArrayList(), cm); cm.attributes().add(code); return cm; - } /** - * Construct a modified version of the invariant block whereby all fail - * bytecodes are replaced with returning false, and all return bytecodes are - * replaced with returning true. + * Update the invariant block to replace fail and return statements with + * goto's and nops (respectively). + * + * @param falseBranch + * @param forest */ -// private AttributedCodeBlock patchInvariantBlock(String falseBranch, -// CodeForest.Block forest) { -// AttributedCodeBlock copy = new AttributedCodeBlock(block.bytecodes(), -// block.attributes()); -// patchInvariantBlockHelper(falseBranch, copy); -// return copy; -// } -// -// private void patchInvariantBlockHelper(String falseBranch, CodeForest block) { -// for (int i = 0; i != block.size(); ++i) { -// // This is still a valid index -// Code c = block.get(i); -// -// if (c instanceof Codes.Return) { -// // first patch point -// block.set(i, Codes.Nop); -// } else if (c instanceof Codes.Fail) { -// // second patch point -// block.set(i, Codes.Goto(falseBranch)); -// } else if (c instanceof Code.Compound) { -// patchInvariantBlockHelper(falseBranch, (Code.Compound) c); -// } -// } -// } + private CodeForest patchInvariantBlock(String falseBranch, CodeForest forest) { + CodeForest nForest = new CodeForest(forest); + for(int i=0;i!=forest.numBlocks();++i) { + patchInvariantBlockHelper(falseBranch, nForest.get(i)); + } + return nForest; + } + + private void patchInvariantBlockHelper(String falseBranch, CodeForest.Block block) { + for (int i = 0; i != block.size(); ++i) { + // This is still a valid index + CodeForest.Entry e = block.get(i); + Code c = e.code(); + + if (c instanceof Codes.Return) { + // first patch point + block.set(i, Codes.Nop); + } else if (c instanceof Codes.Fail) { + // second patch point + block.set(i, Codes.Goto(falseBranch)); + } + } + } private List build(WyilFile.FunctionOrMethod method) { ArrayList methods = new ArrayList(); @@ -1031,7 +1029,6 @@ private void translate(CodeForest.Index index, Codes.IfIs c, int freeSlot, internalFailure("error computing maximally consumed type: " + targetType, filename, e); return; } - // The false label will determine the destination where the variable // will be retyped on the false branch begins. String falseLabel = freshLabel(); @@ -1100,24 +1097,18 @@ protected void translateTypeTest(String falseTarget, Type test, } } - private void translateInvariantTest(String falseTarget, Type type, - int rootSlot, int freeSlot, - HashMap constants, - ArrayList bytecodes) { + private void translateInvariantTest(String falseTarget, Type type, int rootSlot, int freeSlot, + HashMap constants, ArrayList bytecodes) { // JvmType underlyingType = convertUnderlyingType(type); // if (type instanceof Type.Nominal) { Type.Nominal c = (Type.Nominal) type; Path.ID mid = c.name().module(); - JvmType.Clazz owner = new JvmType.Clazz(mid.parent().toString() - .replace('/', '.'), mid.last()); - JvmType.Function fnType = new JvmType.Function(new JvmType.Bool(), - convertUnderlyingType(c)); - bytecodes.add(new Bytecode.Load(rootSlot, - convertUnderlyingType(type))); - bytecodes.add(new Bytecode.Invoke(owner, c.name().name() - + "$typeof", fnType, Bytecode.InvokeMode.STATIC)); + JvmType.Clazz owner = new JvmType.Clazz(mid.parent().toString().replace('/', '.'), mid.last()); + JvmType.Function fnType = new JvmType.Function(new JvmType.Bool(), convertUnderlyingType(c)); + bytecodes.add(new Bytecode.Load(rootSlot, convertUnderlyingType(type))); + bytecodes.add(new Bytecode.Invoke(owner, c.name().name() + "$typeof", fnType, Bytecode.InvokeMode.STATIC)); bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, falseTarget)); } else if (type instanceof Type.Leaf) { // Do nout @@ -1125,22 +1116,16 @@ private void translateInvariantTest(String falseTarget, Type type, Type.Reference rt = (Type.Reference) type; JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT); bytecodes.add(new Bytecode.Load(rootSlot, underlyingType)); - bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype, - Bytecode.InvokeMode.VIRTUAL)); + bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype, Bytecode.InvokeMode.VIRTUAL)); addReadConversion(rt.element(), bytecodes); - bytecodes.add(new Bytecode.Store(freeSlot, convertUnderlyingType(rt - .element()))); - translateInvariantTest(falseTarget, rt.element(), freeSlot, - freeSlot + 1, constants, bytecodes); + bytecodes.add(new Bytecode.Store(freeSlot, convertUnderlyingType(rt.element()))); + translateInvariantTest(falseTarget, rt.element(), freeSlot, freeSlot + 1, constants, bytecodes); } else if (type instanceof Type.EffectiveArray) { Type.EffectiveArray ts = (Type.EffectiveArray) type; - Triple loopLabels = translateLoopBegin( - bytecodes, rootSlot, freeSlot); + Triple loopLabels = translateLoopBegin(bytecodes, rootSlot, freeSlot); addReadConversion(ts.element(), bytecodes); - bytecodes.add(new Bytecode.Store(freeSlot + 1, - convertUnderlyingType(ts.element()))); - translateInvariantTest(falseTarget, ts.element(), freeSlot + 1, - freeSlot + 2, constants, bytecodes); + bytecodes.add(new Bytecode.Store(freeSlot + 1, convertUnderlyingType(ts.element()))); + translateInvariantTest(falseTarget, ts.element(), freeSlot + 1, freeSlot + 2, constants, bytecodes); translateLoopEnd(bytecodes, loopLabels); } else if (type instanceof Type.Record) { Type.Record tt = (Type.Record) type; @@ -1153,15 +1138,11 @@ private void translateInvariantTest(String falseTarget, Type type, JvmType underlyingFieldType = convertUnderlyingType(fieldType); bytecodes.add(new Bytecode.Load(rootSlot, underlyingType)); bytecodes.add(new Bytecode.LoadConst(field)); - JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, - JAVA_LANG_STRING); - bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "get", ftype, - Bytecode.InvokeMode.VIRTUAL)); + JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_STRING); + bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "get", ftype, Bytecode.InvokeMode.VIRTUAL)); addReadConversion(fieldType, bytecodes); - bytecodes - .add(new Bytecode.Store(freeSlot, underlyingFieldType)); - translateInvariantTest(falseTarget, fieldType, freeSlot, - freeSlot + 1, constants, bytecodes); + bytecodes.add(new Bytecode.Store(freeSlot, underlyingFieldType)); + translateInvariantTest(falseTarget, fieldType, freeSlot, freeSlot + 1, constants, bytecodes); } } else if (type instanceof Type.FunctionOrMethod) { // FIXME: this is clearly a bug. However, it's not completely @@ -1171,8 +1152,7 @@ private void translateInvariantTest(String falseTarget, Type type, } else if (type instanceof Type.Negation) { Type.Reference rt = (Type.Reference) type; String trueTarget = freshLabel(); - translateInvariantTest(trueTarget, rt.element(), rootSlot, - freeSlot, constants, bytecodes); + translateInvariantTest(trueTarget, rt.element(), rootSlot, freeSlot, constants, bytecodes); bytecodes.add(new Bytecode.Goto(falseTarget)); bytecodes.add(new Bytecode.Label(trueTarget)); } else if (type instanceof Type.Union) { @@ -1182,17 +1162,12 @@ private void translateInvariantTest(String falseTarget, Type type, try { Type underlyingBound = expander.getUnderlyingType(bound); String nextLabel = freshLabel(); - bytecodes.add(new Bytecode.Load(rootSlot, - convertUnderlyingType(type))); - translateTypeTest(nextLabel, underlyingBound, constants, - bytecodes); - bytecodes.add(new Bytecode.Load(rootSlot, - convertUnderlyingType(type))); + bytecodes.add(new Bytecode.Load(rootSlot, convertUnderlyingType(type))); + translateTypeTest(nextLabel, underlyingBound, constants, bytecodes); + bytecodes.add(new Bytecode.Load(rootSlot, convertUnderlyingType(type))); addReadConversion(bound, bytecodes); - bytecodes.add(new Bytecode.Store(freeSlot, - convertUnderlyingType(bound))); - translateInvariantTest(nextLabel, bound, freeSlot, - freeSlot + 1, constants, bytecodes); + bytecodes.add(new Bytecode.Store(freeSlot, convertUnderlyingType(bound))); + translateInvariantTest(nextLabel, bound, freeSlot, freeSlot + 1, constants, bytecodes); bytecodes.add(new Bytecode.Goto(trueLabel)); bytecodes.add(new Bytecode.Label(nextLabel)); } catch (ResolveError e) { @@ -1215,7 +1190,7 @@ private void translate(CodeForest.Index index, Codes.Loop c, int freeSlot, CodeF bytecodes.add(new Bytecode.Label(loopHeader)); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(new CodeForest.Index(index.block(), 0), freeSlot, forest, bytecodes); + translate(new CodeForest.Index(c.block(), 0), freeSlot, forest, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); } @@ -1243,7 +1218,7 @@ private int translate(CodeForest.Index index, Codes.Quantify c, int freeSlot, bytecodes.add(new Bytecode.Store(c.indexOperand(), convertUnderlyingType(Type.T_INT))); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(new CodeForest.Index(index.block(), 0), freeSlot + 1, forest, bytecodes); + translate(new CodeForest.Index(c.block(), 0), freeSlot + 1, forest, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); bytecodes.add(new Bytecode.Label(loopExit)); @@ -1501,8 +1476,9 @@ private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, Cod // First, build and register lambda class which calls the given function // or method. This class will extend class wyjc.runtime.WyLambda. + Type.FunctionOrMethod lamType = (Type.FunctionOrMethod) c.type(0); int lambda_id = lambdas.size(); - lambdas.add(buildLambda(c.name, c.type(0), lambda_id)); + lambdas.add(buildLambda(c.name, lamType, lambda_id)); // Second, create and duplicate new lambda object. This will then stay // on the stack (whilst the parameters are constructed) until the @@ -1518,34 +1494,28 @@ private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, Cod // this as an argument to the lambda class constructor; otherwise, we // just pass null. To do this, we first check whether or not a binding // is required. - boolean hasBinding = false; - - for (int operand : c.operands()) { - if (operand != Codes.NULL_REG) { - hasBinding = true; - break; - } - } - - if (hasBinding) { - // Yes, binding is required. - bytecodes.add(new Bytecode.LoadConst(c.operands().length)); + int[] c_operands = c.operands(); + + if (c_operands.length > 0) { + // Yes, binding is required. + int numParams = lamType.params().size(); + int numBlanks = numParams - c_operands.length; + bytecodes.add(new Bytecode.LoadConst(numParams)); bytecodes.add(new Bytecode.New(JAVA_LANG_OBJECT_ARRAY)); - for (int i = 0; i != c.operands().length; ++i) { - bytecodes.add(new Bytecode.Dup(JAVA_LANG_OBJECT_ARRAY)); + for(int i=0;i IGNORED = new HashMap(); + + static { + IGNORED.put("Coercion_Valid_8", "#406"); + IGNORED.put("Complex_Valid_3", "Issue ???"); + IGNORED.put("ConstrainedIntersection_Valid_1", "unknown"); + IGNORED.put("ConstrainedNegation_Valid_1", "#342"); + IGNORED.put("ConstrainedNegation_Valid_2", "#342"); + IGNORED.put("Contractive_Valid_1", "Issue ???"); + IGNORED.put("DoWhile_Valid_4", "unknown"); + IGNORED.put("FunctionRef_Valid_2", "Issue ???"); + IGNORED.put("FunctionRef_Valid_13", "#555"); + IGNORED.put("Import_Valid_4", "#492"); + IGNORED.put("Import_Valid_5", "#492"); + IGNORED.put("Intersection_Valid_1", "Issue ???"); + IGNORED.put("Intersection_Valid_2", "Issue ???"); + IGNORED.put("ListAccess_Valid_6", "Issue ???"); + IGNORED.put("ListAccess_Valid_7", "Issue ???"); + IGNORED.put("NegationType_Valid_3", "Issue ???"); + IGNORED.put("OpenRecord_Valid_11", "#585"); + IGNORED.put("RecordCoercion_Valid_1", "#564"); + IGNORED.put("RecordSubtype_Valid_1", "Issue ???"); + IGNORED.put("RecordSubtype_Valid_2", "Issue ???"); + IGNORED.put("RecursiveType_Valid_12", "#339"); + IGNORED.put("RecursiveType_Valid_22", "#339"); + IGNORED.put("RecursiveType_Valid_28", "#364"); + IGNORED.put("RecursiveType_Valid_3", "#406"); + IGNORED.put("RecursiveType_Valid_4", "#406"); + IGNORED.put("RecursiveType_Valid_5", "#18"); + IGNORED.put("Reference_Valid_6", "#553"); + IGNORED.put("TypeEquals_Valid_23", "Issue ???"); + IGNORED.put("TypeEquals_Valid_36", "Issue ???"); + IGNORED.put("TypeEquals_Valid_37", "Issue ???"); + IGNORED.put("TypeEquals_Valid_38", "Issue ???"); + IGNORED.put("TypeEquals_Valid_41", "Issue ???"); + IGNORED.put("While_Valid_15", "unknown"); + + // Fails and was not listed as test case before parameterizing + IGNORED.put("DoWhile_Valid_7", "unknown"); + IGNORED.put("Function_Valid_11", "unknown"); + IGNORED.put("Function_Valid_15", "unknown"); + IGNORED.put("While_Valid_48", "unknown"); + + // Fails and was not annotated with @Test before parameterizing + IGNORED.put("While_Valid_7", "unknown"); + } - /** - * The path to the Whiley RunTime (WyRT) library. This contains the Whiley - * standard library, which includes various helper functions, etc. - */ - private static String WYRT_PATH; + /** + * The directory containing the source files for each test case. Every test + * corresponds to a file in this directory. + */ + public final static String WHILEY_SRC_DIR = "../../tests/valid".replace('/', File.separatorChar); - /** + /** * The path to the Whiley-2-Java class files. These are needed so we can * access the JVM compiled version of the Whiley standard library, along * with additional runtime support classes. */ - private static final String WYJC_CLASS_DIR="../../modules/wyjc/src/"; + private static final String WYJC_CLASS_DIR="../../modules/wyjc/src/".replace('/', File.separatorChar); - private static final String WYBS_CLASS_DIR="../../modules/wybs/src/"; + private static final String WYBS_CLASS_DIR="../../modules/wybs/src/".replace('/', File.separatorChar); + + private static final String WYRL_CLASS_DIR="../../lib/wyrl-v0.4.4.jar".replace('/', File.separatorChar); - private static final String WYRL_CLASS_DIR="../../lib/wyrl-v0.4.4.jar"; + /** + * The directory where compiler libraries are stored. This is necessary + * since it will contain the Whiley Runtime. + */ + public final static String WYC_LIB_DIR = "../../lib/".replace('/', File.separatorChar); + /** + * The path to the Whiley RunTime (WyRT) library. This contains the Whiley + * standard library, which includes various helper functions, etc. + */ + private static String WYRT_PATH; - static { + static { - // The purpose of this is to figure out what the proper name for the - // wyrt file is. Since there can be multiple versions of this file, - // we're not sure which one to pick. + // The purpose of this is to figure out what the proper name for the + // wyrt file is. Since there can be multiple versions of this file, + // we're not sure which one to pick. - File file = new File(WYC_LIB_DIR); - for(String f : file.list()) { - if(f.startsWith("wyrt-v")) { - WYRT_PATH = WYC_LIB_DIR + f; - } - } - } + File file = new File(WYC_LIB_DIR); + for(String f : file.list()) { + if(f.startsWith("wyrt-v")) { + WYRT_PATH = WYC_LIB_DIR + f; + } + } + } - // ====================================================================== - // Test Harness - // ====================================================================== + // ====================================================================== + // Test Harness + // ====================================================================== - /** + /** * Compile a syntactically invalid test case with verification enabled. The * expectation is that compilation should fail with an error and, hence, the * test fails if compilation does not. @@ -195,2339 +263,33 @@ public String CLASSPATH(String... components) { } return r; } - - // ====================================================================== - // Tests - // ====================================================================== - @Test - public void Access_Valid_1() { - runTest("Access_Valid_1"); - } - - @Test - public void Access_Valid_2() { - runTest("Access_Valid_2"); - } - - @Test - public void Assume_Valid_1() { - runTest("Assume_Valid_1"); - } - - @Test - public void Assume_Valid_2() { - runTest("Assume_Valid_2"); - } - - @Test - public void BoolAssign_Valid_1() { - runTest("BoolAssign_Valid_1"); - } - - @Test - public void BoolAssign_Valid_2() { - runTest("BoolAssign_Valid_2"); - } - - @Test - public void BoolAssign_Valid_3() { - runTest("BoolAssign_Valid_3"); - } - - @Test - public void BoolAssign_Valid_4() { - runTest("BoolAssign_Valid_4"); - } - - @Test - public void BoolAssign_Valid_5() { - runTest("BoolAssign_Valid_5"); - } - - @Test - public void BoolAssign_Valid_6() { - runTest("BoolAssign_Valid_6"); - } - - @Test - public void BoolFun_Valid_1() { - runTest("BoolFun_Valid_1"); - } - - @Test - public void BoolIfElse_Valid_1() { - runTest("BoolIfElse_Valid_1"); - } - - @Test - public void BoolIfElse_Valid_2() { - runTest("BoolIfElse_Valid_2"); - } - - @Test - public void BoolList_Valid_1() { - runTest("BoolList_Valid_1"); - } - - @Test - public void BoolList_Valid_2() { - runTest("BoolList_Valid_2"); - } - - @Test - public void BoolList_Valid_3() { - runTest("BoolList_Valid_3"); - } - - @Test - public void BoolRecord_Valid_1() { - runTest("BoolRecord_Valid_1"); - } - - @Test - public void BoolRecord_Valid_2() { - runTest("BoolRecord_Valid_2"); - } - - @Test - public void BoolRequires_Valid_1() { - runTest("BoolRequires_Valid_1"); - } - - @Test - public void BoolReturn_Valid_1() { - runTest("BoolReturn_Valid_1"); - } - - @Test - public void Byte_Valid_1() { - runTest("Byte_Valid_1"); - } - - @Test - public void Byte_Valid_2() { - runTest("Byte_Valid_2"); - } - - @Test - public void Byte_Valid_3() { - runTest("Byte_Valid_3"); - } - - @Test - public void Byte_Valid_4() { - runTest("Byte_Valid_4"); - } - - @Test - public void Byte_Valid_5() { - runTest("Byte_Valid_5"); - } - - @Test - public void Byte_Valid_6() { - runTest("Byte_Valid_6"); - } - - @Test - public void Byte_Valid_7() { - runTest("Byte_Valid_7"); - } - - @Test - public void Byte_Valid_8() { - runTest("Byte_Valid_8"); - } - - @Test - public void Byte_Valid_9() { - runTest("Byte_Valid_9"); - } - @Test - public void Cast_Valid_5() { - runTest("Cast_Valid_5"); - } - - @Test - public void Coercion_Valid_2() { - runTest("Coercion_Valid_2"); - } - - @Test - public void Coercion_Valid_3() { - runTest("Coercion_Valid_3"); - } - - @Test - public void Coercion_Valid_7() { - runTest("Coercion_Valid_7"); - } - - @Ignore("#406") @Test - public void Coercion_Valid_8() { - runTest("Coercion_Valid_8"); - } - - @Test - public void Coercion_Valid_9() { - runTest("Coercion_Valid_9"); - } - - @Test - public void Complex_Valid_1() { - runTest("Complex_Valid_1"); - } - - @Test - public void Complex_Valid_2() { - runTest("Complex_Valid_2"); - } - - @Ignore("#339") @Test - public void Complex_Valid_3() { - runTest("Complex_Valid_3"); - } - - @Test - public void Complex_Valid_4() { - runTest("Complex_Valid_4"); - } - - @Test - public void Complex_Valid_5() { - runTest("Complex_Valid_5"); - } - - @Test - public void Complex_Valid_6() { - runTest("Complex_Valid_6"); - } - - @Test - public void Complex_Valid_7() { - runTest("Complex_Valid_7"); - } - - @Test - public void Complex_Valid_8() { - runTest("Complex_Valid_8"); - } - - @Test - public void Constant_Valid_3() { - runTest("Constant_Valid_3"); - } - - @Test - public void ConstrainedInt_Valid_1() { - runTest("ConstrainedInt_Valid_1"); - } - - @Test - public void ConstrainedInt_Valid_10() { - runTest("ConstrainedInt_Valid_10"); - } - - @Test - public void ConstrainedInt_Valid_12() { - runTest("ConstrainedInt_Valid_12"); - } - - @Test - public void ConstrainedInt_Valid_13() { - runTest("ConstrainedInt_Valid_13"); - } - - @Test - public void ConstrainedInt_Valid_15() { - runTest("ConstrainedInt_Valid_15"); - } - - @Test - public void ConstrainedInt_Valid_16() { - runTest("ConstrainedInt_Valid_16"); - } - - @Test - public void ConstrainedInt_Valid_17() { - runTest("ConstrainedInt_Valid_17"); - } - - @Test - public void ConstrainedInt_Valid_18() { - runTest("ConstrainedInt_Valid_18"); - } - - @Test - public void ConstrainedInt_Valid_19() { - runTest("ConstrainedInt_Valid_19"); - } - - @Test - public void ConstrainedInt_Valid_2() { - runTest("ConstrainedInt_Valid_2"); - } - - @Test - public void ConstrainedInt_Valid_20() { - runTest("ConstrainedInt_Valid_20"); - } - - @Test - public void ConstrainedInt_Valid_21() { - runTest("ConstrainedInt_Valid_21"); - } - - @Test - public void ConstrainedInt_Valid_22() { - runTest("ConstrainedInt_Valid_22"); - } - - @Test - public void ConstrainedInt_Valid_23() { - runTest("ConstrainedInt_Valid_23"); - } - - @Test - public void ConstrainedInt_Valid_24() { - runTest("ConstrainedInt_Valid_24"); - } - - @Test - public void ConstrainedInt_Valid_3() { - runTest("ConstrainedInt_Valid_3"); - } - - @Test - public void ConstrainedInt_Valid_4() { - runTest("ConstrainedInt_Valid_4"); - } - - @Test - public void ConstrainedInt_Valid_5() { - runTest("ConstrainedInt_Valid_5"); - } - - @Test - public void ConstrainedInt_Valid_6() { - runTest("ConstrainedInt_Valid_6"); - } - - @Test - public void ConstrainedInt_Valid_8() { - runTest("ConstrainedInt_Valid_8"); - } - - @Ignore("Not implemented") @Test - public void ConstrainedIntersection_Valid_1() { - runTest("ConstrainedIntersection_Valid_1"); - } - @Test - public void ConstrainedList_Valid_1() { - runTest("ConstrainedList_Valid_1"); - } - - @Test - public void ConstrainedList_Valid_11() { - runTest("ConstrainedList_Valid_11"); - } + // ====================================================================== + // Tests + // ====================================================================== - @Test - public void ConstrainedList_Valid_12() { - runTest("ConstrainedList_Valid_12"); - } - - @Test - public void ConstrainedList_Valid_14() { - runTest("ConstrainedList_Valid_14"); - } - - @Test - public void ConstrainedList_Valid_15() { - runTest("ConstrainedList_Valid_15"); - } - - @Test - public void ConstrainedList_Valid_16() { - runTest("ConstrainedList_Valid_16"); - } - - @Test - public void ConstrainedList_Valid_17() { - runTest("ConstrainedList_Valid_17"); - } - - @Test - public void ConstrainedList_Valid_18() { - runTest("ConstrainedList_Valid_18"); - } - - @Test - public void ConstrainedList_Valid_19() { - runTest("ConstrainedList_Valid_19"); - } - - @Test - public void ConstrainedList_Valid_2() { - runTest("ConstrainedList_Valid_2"); - } - - @Test - public void ConstrainedList_Valid_20() { - runTest("ConstrainedList_Valid_20"); - } - - @Test - public void ConstrainedList_Valid_21() { - runTest("ConstrainedList_Valid_21"); - } - - @Test - public void ConstrainedList_Valid_22() { - runTest("ConstrainedList_Valid_22"); - } - - @Test - public void ConstrainedList_Valid_23() { - runTest("ConstrainedList_Valid_23"); - } - - @Test - public void ConstrainedList_Valid_25() { - runTest("ConstrainedList_Valid_25"); - } - - @Test - public void ConstrainedList_Valid_26() { - runTest("ConstrainedList_Valid_26"); - } - - @Test - public void ConstrainedList_Valid_27() { - runTest("ConstrainedList_Valid_27"); - } - - @Test - public void ConstrainedList_Valid_28() { - runTest("ConstrainedList_Valid_28"); - } - - - @Test - public void ConstrainedList_Valid_3() { - runTest("ConstrainedList_Valid_3"); - } - - @Test - public void ConstrainedList_Valid_4() { - runTest("ConstrainedList_Valid_4"); - } - - @Test - public void ConstrainedList_Valid_5() { - runTest("ConstrainedList_Valid_5"); - } - - @Test - public void ConstrainedList_Valid_6() { - runTest("ConstrainedList_Valid_6"); - } - - @Test - public void ConstrainedList_Valid_7() { - runTest("ConstrainedList_Valid_7"); - } - - @Test - public void ConstrainedList_Valid_8() { - runTest("ConstrainedList_Valid_8"); - } - - @Test - public void ConstrainedList_Valid_9() { - runTest("ConstrainedList_Valid_9"); - } - - @Ignore("#342")@Test - public void ConstrainedNegation_Valid_1() { - runTest("ConstrainedNegation_Valid_1"); - } - - @Ignore("#342")@Test - public void ConstrainedNegation_Valid_2() { - runTest("ConstrainedNegation_Valid_2"); - } - - @Test - public void ConstrainedRecord_Valid_1() { - runTest("ConstrainedRecord_Valid_1"); - } - - @Test - public void ConstrainedRecord_Valid_2() { - runTest("ConstrainedRecord_Valid_2"); - } - - @Test - public void ConstrainedRecord_Valid_3() { - runTest("ConstrainedRecord_Valid_3"); - } - - @Test - public void ConstrainedRecord_Valid_4() { - runTest("ConstrainedRecord_Valid_4"); - } - - @Test - public void ConstrainedRecord_Valid_5() { - runTest("ConstrainedRecord_Valid_5"); - } - - @Test - public void ConstrainedRecord_Valid_6() { - runTest("ConstrainedRecord_Valid_6"); - } - - @Test - public void ConstrainedRecord_Valid_8() { - runTest("ConstrainedRecord_Valid_8"); - } - - @Test - public void ConstrainedRecord_Valid_9() { - runTest("ConstrainedRecord_Valid_9"); - } - - @Test - public void ConstrainedRecord_Valid_10() { - runTest("ConstrainedRecord_Valid_10"); - } - - @Test - public void ConstrainedReference_Valid_1() { - runTest("ConstrainedReference_Valid_1"); - } - - @Test - public void ConstrainedUnion_Valid_1() { - runTest("ConstrainedUnion_Valid_1"); - } - - @Ignore("Issue ???") @Test - public void Contractive_Valid_1() { - runTest("Contractive_Valid_1"); - } - - @Test - public void Contractive_Valid_2() { - runTest("Contractive_Valid_2"); - } - - @Test - public void Define_Valid_1() { - runTest("Define_Valid_1"); - } - - @Test - public void Define_Valid_2() { - runTest("Define_Valid_2"); - } - - @Test - public void Define_Valid_3() { - runTest("Define_Valid_3"); - } - - @Test - public void Define_Valid_4() { - runTest("Define_Valid_4"); - } - - @Test - public void DoWhile_Valid_1() { - runTest("DoWhile_Valid_1"); - } - - @Test - public void DoWhile_Valid_2() { - runTest("DoWhile_Valid_2"); - } - - @Test - public void DoWhile_Valid_3() { - runTest("DoWhile_Valid_3"); - } - - @Ignore("???") @Test - public void DoWhile_Valid_4() { - runTest("DoWhile_Valid_4"); - } - - @Test - public void DoWhile_Valid_5() { - runTest("DoWhile_Valid_5"); - } - - @Test - public void DoWhile_Valid_6() { - runTest("DoWhile_Valid_6"); - } - - @Test - public void EffectiveList_Valid_1() { - runTest("EffectiveList_Valid_1"); - } - - @Test - public void Ensures_Valid_1() { - runTest("Ensures_Valid_1"); - } - - @Test - public void Ensures_Valid_2() { - runTest("Ensures_Valid_2"); - } - - @Test - public void Ensures_Valid_3() { - runTest("Ensures_Valid_3"); - } - - @Test - public void Ensures_Valid_4() { - runTest("Ensures_Valid_4"); - } - - @Test - public void Ensures_Valid_5() { - runTest("Ensures_Valid_5"); - } - - @Test - public void Ensures_Valid_6() { - runTest("Ensures_Valid_6"); - } - - @Test - public void Ensures_Valid_7() { - runTest("Ensures_Valid_7"); - } - - @Test - public void Ensures_Valid_8() { - runTest("Ensures_Valid_8"); - } - - @Test - public void FunctionRef_Valid_1() { - runTest("FunctionRef_Valid_1"); - } - - @Ignore("Issue ???") @Test - public void FunctionRef_Valid_2() { - runTest("FunctionRef_Valid_2"); - } - - @Ignore("???") @Test - public void FunctionRef_Valid_3() { - runTest("FunctionRef_Valid_3"); - } - - @Test - public void FunctionRef_Valid_4() { - runTest("FunctionRef_Valid_4"); - } - - @Test - public void FunctionRef_Valid_5() { - runTest("FunctionRef_Valid_5"); - } - - @Test - public void FunctionRef_Valid_6() { - runTest("FunctionRef_Valid_6"); - } - - @Test - public void FunctionRef_Valid_7() { - runTest("FunctionRef_Valid_7"); - } - - @Test - public void FunctionRef_Valid_8() { - runTest("FunctionRef_Valid_8"); - } - - @Test - public void FunctionRef_Valid_9() { - runTest("FunctionRef_Valid_9"); - } - - @Test - public void FunctionRef_Valid_10() { - runTest("FunctionRef_Valid_10"); - } - - @Test - public void FunctionRef_Valid_11() { - runTest("FunctionRef_Valid_11"); - } - - @Test - public void FunctionRef_Valid_12() { - runTest("FunctionRef_Valid_12"); - } - - @Ignore("#555") @Test - public void FunctionRef_Valid_13() { - runTest("FunctionRef_Valid_13"); - } - - @Test - public void Function_Valid_1() { - runTest("Function_Valid_1"); - } - - @Test - public void Function_Valid_12() { - runTest("Function_Valid_12"); - } - - @Test - public void Function_Valid_13() { - runTest("Function_Valid_13"); - } - - @Test - public void Function_Valid_14() { - runTest("Function_Valid_14"); - } - - @Test - public void Function_Valid_16() { - runTest("Function_Valid_16"); - } - - @Test - public void Function_Valid_17() { - runTest("Function_Valid_17"); - } - - @Test - public void Function_Valid_18() { - runTest("Function_Valid_18"); - } - - @Test - public void Function_Valid_2() { - runTest("Function_Valid_2"); - } - - @Test - public void Function_Valid_20() { - runTest("Function_Valid_20"); - } - - @Test - public void Function_Valid_21() { - runTest("Function_Valid_21"); - } - - @Test - public void Function_Valid_3() { - runTest("Function_Valid_3"); - } - - @Test - public void Function_Valid_4() { - runTest("Function_Valid_4"); - } - - @Test - public void Function_Valid_5() { - runTest("Function_Valid_5"); - } - - @Test - public void Function_Valid_6() { - runTest("Function_Valid_6"); - } - - @Test - public void Function_Valid_7() { - runTest("Function_Valid_7"); - } - - @Test - public void Function_Valid_8() { - runTest("Function_Valid_8"); - } - - @Test - public void Function_Valid_9() { - runTest("Function_Valid_9"); - } - - @Test - public void HexAssign_Valid_1() { - runTest("HexAssign_Valid_1"); - } - - @Test - public void IfElse_Valid_1() { - runTest("IfElse_Valid_1"); - } - - @Test - public void IfElse_Valid_2() { - runTest("IfElse_Valid_2"); - } - - @Test - public void IfElse_Valid_3() { - runTest("IfElse_Valid_3"); - } - - @Test - public void IfElse_Valid_4() { - runTest("IfElse_Valid_4"); - } - - @Test - public void IfElse_Valid_5() { - runTest("IfElse_Valid_5"); - } - @Test - public void Import_Valid_1() { - runTest("Import_Valid_1"); - } - - @Test - public void Import_Valid_2() { - runTest("Import_Valid_2"); - } - - @Test - public void Import_Valid_3() { - runTest("Import_Valid_3"); - } - - @Ignore("#492") @Test - public void Import_Valid_4() { - runTest("Import_Valid_4"); - } - - @Ignore("#492") @Test - public void Import_Valid_5() { - runTest("Import_Valid_5"); - } - - @Test - public void Import_Valid_6() { - runTest("Import_Valid_6"); - } - - @Test - public void IntConst_Valid_1() { - runTest("IntConst_Valid_1"); - } - - @Test - public void IntDefine_Valid_1() { - runTest("IntDefine_Valid_1"); - } - - @Test - public void IntDefine_Valid_2() { - runTest("IntDefine_Valid_2"); - } - - @Test - public void IntDiv_Valid_1() { - runTest("IntDiv_Valid_1"); - } - - @Test - public void IntDiv_Valid_3() { - runTest("IntDiv_Valid_3"); - } - - @Test - public void IntDiv_Valid_4() { - runTest("IntDiv_Valid_4"); - } - - @Test - public void IntDiv_Valid_5() { - runTest("IntDiv_Valid_5"); - } - - @Test - public void IntMul_Valid_1() { - runTest("IntMul_Valid_1"); - } - @Test - public void IntMul_Valid_2() { - runTest("IntMul_Valid_2"); - } - - @Test - public void IntOp_Valid_1() { - runTest("IntOp_Valid_1"); - } - - @Ignore("Issue ???") @Test - public void Intersection_Valid_1() { - runTest("Intersection_Valid_1"); - } - - @Ignore("Issue ???") @Test - public void Intersection_Valid_2() { - runTest("Intersection_Valid_2"); - } - - @Test - public void Lambda_Valid_1() { - runTest("Lambda_Valid_1"); - } - - @Test - public void Lambda_Valid_2() { - runTest("Lambda_Valid_2"); - } - - @Test - public void Lambda_Valid_3() { - runTest("Lambda_Valid_3"); - } - - @Test - public void Lambda_Valid_4() { - runTest("Lambda_Valid_4"); - } - - @Test - public void Lambda_Valid_5() { - runTest("Lambda_Valid_5"); - } - - @Test - public void Lambda_Valid_6() { - runTest("Lambda_Valid_6"); - } - - @Test - public void Lambda_Valid_7() { - runTest("Lambda_Valid_7"); - } - - @Test - public void Lambda_Valid_8() { - runTest("Lambda_Valid_8"); - } - - @Test - public void Lambda_Valid_9() { - runTest("Lambda_Valid_9"); - } - - @Test - public void LengthOf_Valid_1() { - runTest("LengthOf_Valid_1"); - } - - @Test - public void LengthOf_Valid_4() { - runTest("LengthOf_Valid_4"); - } - - @Test - public void LengthOf_Valid_5() { - runTest("LengthOf_Valid_5"); - } - - @Test - public void ListAccess_Valid_1() { - runTest("ListAccess_Valid_1"); - } - - @Test - public void ListAccess_Valid_3() { - runTest("ListAccess_Valid_3"); - } - - @Test - public void ListAccess_Valid_5() { - runTest("ListAccess_Valid_5"); - } - - @Ignore("Issue ???") @Test - public void ListAccess_Valid_6() { - runTest("ListAccess_Valid_6"); - } - - @Ignore("Issue ???") @Test - public void ListAccess_Valid_7() { - runTest("ListAccess_Valid_7"); - } - - @Test - public void ListAccess_Valid_8() { - runTest("ListAccess_Valid_8"); - } - - @Test - public void ListAccess_Valid_9() { - runTest("ListAccess_Valid_9"); - } - - @Test - public void ListAssign_Valid_1() { - runTest("ListAssign_Valid_1"); - } - - @Test - public void ListAssign_Valid_11() { - runTest("ListAssign_Valid_11"); - } - - @Test - public void ListAssign_Valid_2() { - runTest("ListAssign_Valid_2"); - } - - @Test - public void ListAssign_Valid_3() { - runTest("ListAssign_Valid_3"); - } - - @Test - public void ListAssign_Valid_4() { - runTest("ListAssign_Valid_4"); - } - - @Test - public void ListAssign_Valid_5() { - runTest("ListAssign_Valid_5"); - } - - @Test - public void ListAssign_Valid_6() { - runTest("ListAssign_Valid_6"); - } - - @Test - public void ListAssign_Valid_7() { - runTest("ListAssign_Valid_7"); - } - - @Test - public void ListAssign_Valid_8() { - runTest("ListAssign_Valid_8"); - } - - @Test - public void ListAssign_Valid_9() { - runTest("ListAssign_Valid_9"); - } - - @Test - public void ListEmpty_Valid_1() { - runTest("ListEmpty_Valid_1"); - } - - @Test - public void ListGenerator_Valid_1() { - runTest("ListGenerator_Valid_1"); - } - - @Test - public void ListGenerator_Valid_2() { - runTest("ListGenerator_Valid_2"); - } - - @Test - public void ListGenerator_Valid_3() { - runTest("ListGenerator_Valid_3"); - } - - @Test - public void ListGenerator_Valid_5() { - runTest("ListGenerator_Valid_5"); - } - - @Test - public void ListGenerator_Valid_6() throws IOException { - runTest("ListGenerator_Valid_6"); - } - - @Test - public void ListGenerator_Valid_7() throws IOException { - runTest("ListGenerator_Valid_7"); - } - - @Test - public void ListGenerator_Valid_8() throws IOException { - runTest("ListGenerator_Valid_8"); - } - - @Test - public void ListGenerator_Valid_9() throws IOException { - runTest("ListGenerator_Valid_9"); - } - - @Test - public void ListGenerator_Valid_10() throws IOException { - runTest("ListGenerator_Valid_10"); - } - - @Test - public void ListGenerator_Valid_11() throws IOException { - runTest("ListGenerator_Valid_11"); - } - - @Test - public void ListGenerator_Valid_12() throws IOException { - runTest("ListGenerator_Valid_12"); - } - - @Test - public void ListLength_Valid_1() { - runTest("ListLength_Valid_1"); - } - - @Test - public void ListLength_Valid_2() { - runTest("ListLength_Valid_2"); - } - - @Test - public void ListLength_Valid_3() { - runTest("ListLength_Valid_3"); - } - - @Test - public void MessageRef_Valid_1() { - runTest("MessageRef_Valid_1"); - } - - @Test - public void MessageRef_Valid_2() { - runTest("MessageRef_Valid_2"); - } - - @Test - public void MessageSend_Valid_1() { - runTest("MessageSend_Valid_1"); - } - - @Test - public void MessageSend_Valid_2() { - runTest("MessageSend_Valid_2"); - } - - @Test - public void MessageSend_Valid_3() { - runTest("MessageSend_Valid_3"); - } - - @Test - public void MessageSend_Valid_4() { - runTest("MessageSend_Valid_4"); - } - - @Test - public void MessageSend_Valid_5() { - runTest("MessageSend_Valid_5"); - } - - @Test - public void MethodCall_Valid_1() { - runTest("MethodCall_Valid_1"); - } - - @Test - public void MethodCall_Valid_2() { - runTest("MethodCall_Valid_2"); - } - - @Test - public void MethodCall_Valid_3() { - runTest("MethodCall_Valid_3"); - } - - @Test - public void MethodCall_Valid_4() { - runTest("MethodCall_Valid_4"); - } - - @Test - public void MethodRef_Valid_1() { - runTest("MethodRef_Valid_1"); - } - - @Test - public void MethodRef_Valid_2() { - runTest("MethodRef_Valid_2"); - } - - @Test - public void Method_Valid_1() { - runTest("Method_Valid_1"); - } - - @Test - public void MultiLineComment_Valid_1() { - runTest("MultiLineComment_Valid_1"); - } - - @Test - public void MultiLineComment_Valid_2() { - runTest("MultiLineComment_Valid_2"); - } - - @Test - public void NegationType_Valid_1() { - runTest("NegationType_Valid_1"); - } - - @Test - public void NegationType_Valid_2() { - runTest("NegationType_Valid_2"); - } - - @Ignore("Issue ???") @Test - public void NegationType_Valid_3() { - runTest("NegationType_Valid_3"); - } - - @Test - public void NegationType_Valid_4() { - runTest("NegationType_Valid_4"); - } - - @Test - public void OpenRecord_Valid_1() { - runTest("OpenRecord_Valid_1"); - } - - @Test - public void OpenRecord_Valid_10() { - runTest("OpenRecord_Valid_10"); - } - - @Test - public void OpenRecord_Valid_2() { - runTest("OpenRecord_Valid_2"); - } - - @Test - public void OpenRecord_Valid_3() { - runTest("OpenRecord_Valid_3"); - } - - @Test - public void OpenRecord_Valid_4() { - runTest("OpenRecord_Valid_4"); - } - - @Test - public void OpenRecord_Valid_5() { - runTest("OpenRecord_Valid_5"); - } - - @Test - public void OpenRecord_Valid_6() { - runTest("OpenRecord_Valid_6"); - } - - @Test - public void OpenRecord_Valid_9() { - runTest("OpenRecord_Valid_9"); - } - - @Test - public void ProcessAccess_Valid_1() { - runTest("ProcessAccess_Valid_1"); - } - - @Test - public void ProcessAccess_Valid_2() { - runTest("ProcessAccess_Valid_2"); - } - - @Test - public void Process_Valid_1() { - runTest("Process_Valid_1"); - } - - @Test - public void Process_Valid_10() { - runTest("Process_Valid_10"); - } - - @Test - public void Process_Valid_11() { - runTest("Process_Valid_11"); - } - - @Test - public void Process_Valid_12() { - runTest("Process_Valid_12"); - } - - @Test - public void Process_Valid_2() { - runTest("Process_Valid_2"); - } - - @Test - public void Process_Valid_3() { - runTest("Process_Valid_3"); - } - - @Test - public void Process_Valid_4() { - runTest("Process_Valid_4"); - } - - @Test - public void Process_Valid_5() { - runTest("Process_Valid_5"); - } - - @Test - public void Process_Valid_6() { - runTest("Process_Valid_6"); - } - - @Test - public void Process_Valid_7() { - runTest("Process_Valid_7"); - } - - @Test - public void Process_Valid_8() { - runTest("Process_Valid_8"); - } - - @Test - public void Process_Valid_9() { - runTest("Process_Valid_9"); - } - - @Test - public void Quantifiers_Valid_1() { - runTest("Quantifiers_Valid_1"); - } - - @Test - public void RecordAccess_Valid_1() { - runTest("RecordAccess_Valid_1"); - } - - @Test - public void RecordAssign_Valid_1() { - runTest("RecordAssign_Valid_1"); - } - - @Test - public void RecordAssign_Valid_10() { - runTest("RecordAssign_Valid_10"); - } - - @Test - public void RecordAssign_Valid_2() { - runTest("RecordAssign_Valid_2"); - } - - @Test - public void RecordAssign_Valid_3() { - runTest("RecordAssign_Valid_3"); - } - - @Test - public void RecordAssign_Valid_4() { - runTest("RecordAssign_Valid_4"); - } - - @Test - public void RecordAssign_Valid_5() { - runTest("RecordAssign_Valid_5"); - } - - @Test - public void RecordAssign_Valid_6() { - runTest("RecordAssign_Valid_6"); - } - - @Test - public void RecordAssign_Valid_7() { - runTest("RecordAssign_Valid_7"); - } - - @Test - public void RecordAssign_Valid_8() { - runTest("RecordAssign_Valid_8"); - } - - @Test - public void RecordAssign_Valid_9() { - runTest("RecordAssign_Valid_9"); - } - - @Ignore("#564") @Test - public void RecordCoercion_Valid_1() { - runTest("RecordCoercion_Valid_1"); - } - - @Test - public void RecordDefine_Valid_1() { - runTest("RecordDefine_Valid_1"); - } - - @Test - public void RecordDefine_Valid_2() { - runTest("RecordDefine_Valid_2"); - } - - @Ignore("Issue ???") @Test - public void RecordSubtype_Valid_1() { - runTest("RecordSubtype_Valid_1"); - } - - @Ignore("Issue ???") @Test - public void RecordSubtype_Valid_2() { - runTest("RecordSubtype_Valid_2"); - } - - @Test - public void RecursiveType_Valid_1() { - runTest("RecursiveType_Valid_1"); - } - - @Test - public void RecursiveType_Valid_10() { - runTest("RecursiveType_Valid_10"); - } - - @Test - public void RecursiveType_Valid_11() { - runTest("RecursiveType_Valid_11"); - } - - @Ignore("#339") @Test - public void RecursiveType_Valid_12() { - runTest("RecursiveType_Valid_12"); - } - - @Test - public void RecursiveType_Valid_13() { - runTest("RecursiveType_Valid_13"); - } - - @Test - public void RecursiveType_Valid_14() { - runTest("RecursiveType_Valid_14"); - } - - @Test - public void RecursiveType_Valid_15() { - runTest("RecursiveType_Valid_15"); - } - - @Test - public void RecursiveType_Valid_16() { - runTest("RecursiveType_Valid_16"); - } - - @Test - public void RecursiveType_Valid_17() { - runTest("RecursiveType_Valid_17"); - } - - @Test - public void RecursiveType_Valid_18() { - runTest("RecursiveType_Valid_18"); - } - - @Test - public void RecursiveType_Valid_19() { - runTest("RecursiveType_Valid_19"); - } - - @Test - public void RecursiveType_Valid_2() { - runTest("RecursiveType_Valid_2"); - } - - @Test - public void RecursiveType_Valid_20() { - runTest("RecursiveType_Valid_20"); - } - - @Test - public void RecursiveType_Valid_21() { - runTest("RecursiveType_Valid_21"); - } - - @Ignore("#339") @Test - public void RecursiveType_Valid_22() { - runTest("RecursiveType_Valid_22"); - } - - @Test - public void RecursiveType_Valid_23() { - runTest("RecursiveType_Valid_23"); - } - - @Test - public void RecursiveType_Valid_24() { - runTest("RecursiveType_Valid_24"); - } - - @Test - public void RecursiveType_Valid_25() { - runTest("RecursiveType_Valid_25"); - } - - @Test - public void RecursiveType_Valid_26() { - runTest("RecursiveType_Valid_26"); - } - - @Test - public void RecursiveType_Valid_27() { - runTest("RecursiveType_Valid_27"); - } - - @Ignore("#364") @Test - public void RecursiveType_Valid_28() { - runTest("RecursiveType_Valid_28"); - } - - @Ignore("#406") @Test - public void RecursiveType_Valid_3() { - runTest("RecursiveType_Valid_3"); - } - - @Ignore("#406") @Test - public void RecursiveType_Valid_4() { - runTest("RecursiveType_Valid_4"); - } - - @Ignore("#18") @Test - public void RecursiveType_Valid_5() { - runTest("RecursiveType_Valid_5"); - } - - @Test - public void RecursiveType_Valid_6() { - runTest("RecursiveType_Valid_6"); - } - - @Test - public void RecursiveType_Valid_7() { - runTest("RecursiveType_Valid_7"); - } - - @Test - public void RecursiveType_Valid_8() { - runTest("RecursiveType_Valid_8"); - } - - @Test - public void RecursiveType_Valid_9() { - runTest("RecursiveType_Valid_9"); - } - - @Test - public void Reference_Valid_1() { - runTest("Reference_Valid_1"); - } - - @Test - public void Reference_Valid_2() { - runTest("Reference_Valid_2"); - } - - @Test - public void Reference_Valid_3() { - runTest("Reference_Valid_3"); - } - - @Test - public void Reference_Valid_4() { - runTest("Reference_Valid_4"); - } - - @Test - public void Reference_Valid_5() { - runTest("Reference_Valid_5"); - } - - @Ignore("#553") @Test - public void Reference_Valid_6() { - runTest("Reference_Valid_6"); - } - - @Test - public void Remainder_Valid_1() { - runTest("Remainder_Valid_1"); - } - - @Test - public void Return_Valid_1() { - runTest("Return_Valid_1"); - } - - @Test - public void Requires_Valid_1() { - runTest("Requires_Valid_1"); - } - - @Test - public void Resolution_Valid_1() { - runTest("Resolution_Valid_1"); - } - - @Test - public void SingleLineComment_Valid_1() { - runTest("SingleLineComment_Valid_1"); - } - - @Test - public void Skip_Valid_1() { - runTest("Skip_Valid_1"); - } - - @Test - public void String_Valid_1() { - runTest("String_Valid_1"); - } - - @Test - public void String_Valid_2() { - runTest("String_Valid_2"); - } - - @Test - public void String_Valid_3() { - runTest("String_Valid_3"); - } - - @Test - public void String_Valid_4() { - runTest("String_Valid_4"); - } - - @Test - public void String_Valid_5() { - runTest("String_Valid_5"); - } - - @Test - public void String_Valid_6() { - runTest("String_Valid_6"); - } - - @Test - public void Subtype_Valid_1() { - runTest("Subtype_Valid_1"); - } - - @Test - public void Subtype_Valid_10() { - runTest("Subtype_Valid_10"); - } - - @Test - public void Subtype_Valid_11() { - runTest("Subtype_Valid_11"); - } - - @Test - public void Subtype_Valid_12() { - runTest("Subtype_Valid_12"); - } - - @Test - public void Subtype_Valid_13() { - runTest("Subtype_Valid_13"); - } - - @Test - public void Subtype_Valid_14() { - runTest("Subtype_Valid_14"); - } - - @Test - public void Subtype_Valid_2() { - runTest("Subtype_Valid_2"); - } - - @Test - public void Subtype_Valid_3() { - runTest("Subtype_Valid_3"); - } - - @Test - public void Subtype_Valid_4() { - runTest("Subtype_Valid_4"); - } - - @Test - public void Subtype_Valid_5() { - runTest("Subtype_Valid_5"); - } - - @Test - public void Subtype_Valid_6() { - runTest("Subtype_Valid_6"); - } - - @Test - public void Subtype_Valid_7() { - runTest("Subtype_Valid_7"); - } - - @Test - public void Subtype_Valid_8() { - runTest("Subtype_Valid_8"); - } - - @Test - public void Subtype_Valid_9() { - runTest("Subtype_Valid_9"); - } - - @Test - public void Switch_Valid_1() { - runTest("Switch_Valid_1"); - } - - @Test - public void Switch_Valid_10() { - runTest("Switch_Valid_10"); - } - - @Test - public void Switch_Valid_11() { - runTest("Switch_Valid_11"); - } - - @Test - public void Switch_Valid_12() { - runTest("Switch_Valid_12"); - } - - @Test - public void Switch_Valid_13() { - runTest("Switch_Valid_13"); - } - - @Test - public void Switch_Valid_2() { - runTest("Switch_Valid_2"); - } - - @Test - public void Switch_Valid_3() { - runTest("Switch_Valid_3"); - } - - @Test - public void Switch_Valid_4() { - runTest("Switch_Valid_4"); - } - - @Test - public void Switch_Valid_5() { - runTest("Switch_Valid_5"); - } - - @Test - public void Switch_Valid_6() { - runTest("Switch_Valid_6"); - } - - @Test - public void Switch_Valid_7() { - runTest("Switch_Valid_7"); - } - - @Test - public void Switch_Valid_8() { - runTest("Switch_Valid_8"); - } - - @Test - public void Switch_Valid_9() { - runTest("Switch_Valid_9"); - } - - @Test - public void Syntax_Valid_1() { - runTest("Syntax_Valid_1"); - } - - @Test - public void TypeEquals_Valid_1() { - runTest("TypeEquals_Valid_1"); - } - - @Test - public void TypeEquals_Valid_10() { - runTest("TypeEquals_Valid_10"); - } - - @Test - public void TypeEquals_Valid_11() { - runTest("TypeEquals_Valid_11"); - } - - @Test - public void TypeEquals_Valid_12() { - runTest("TypeEquals_Valid_12"); - } - - @Test - public void TypeEquals_Valid_14() { - runTest("TypeEquals_Valid_14"); - } - - @Test - public void TypeEquals_Valid_15() { - runTest("TypeEquals_Valid_15"); - } - - @Test - public void TypeEquals_Valid_16() { - runTest("TypeEquals_Valid_16"); - } - - @Test - public void TypeEquals_Valid_17() { - runTest("TypeEquals_Valid_17"); - } - - @Test - public void TypeEquals_Valid_18() { - runTest("TypeEquals_Valid_18"); - } - - @Test - public void TypeEquals_Valid_19() { - runTest("TypeEquals_Valid_19"); - } - - @Test - public void TypeEquals_Valid_2() { - runTest("TypeEquals_Valid_2"); - } - - @Test - public void TypeEquals_Valid_21() { - runTest("TypeEquals_Valid_21"); - } - - @Ignore("Issue ???") @Test - public void TypeEquals_Valid_23() { - runTest("TypeEquals_Valid_23"); - } - - @Test - public void TypeEquals_Valid_24() { - runTest("TypeEquals_Valid_24"); - } - - @Test - public void TypeEquals_Valid_25() { - runTest("TypeEquals_Valid_25"); - } - - @Test - public void TypeEquals_Valid_27() { - runTest("TypeEquals_Valid_27"); - } - - @Test - public void TypeEquals_Valid_28() { - runTest("TypeEquals_Valid_28"); - } - - @Test - public void TypeEquals_Valid_29() { - runTest("TypeEquals_Valid_29"); - } - - @Test - public void TypeEquals_Valid_3() { - runTest("TypeEquals_Valid_3"); - } - - @Test - public void TypeEquals_Valid_30() { - runTest("TypeEquals_Valid_30"); - } - - @Test - public void TypeEquals_Valid_31() { - runTest("TypeEquals_Valid_31"); - } - - @Test - public void TypeEquals_Valid_32() { - runTest("TypeEquals_Valid_32"); - } - - @Test - public void TypeEquals_Valid_33() { - runTest("TypeEquals_Valid_33"); - } - - @Test - public void TypeEquals_Valid_34() { - runTest("TypeEquals_Valid_34"); - } - - @Test - public void TypeEquals_Valid_35() { - runTest("TypeEquals_Valid_35"); - } - - @Ignore("Issue ???") @Test - public void TypeEquals_Valid_36() { - runTest("TypeEquals_Valid_36"); - } - - @Ignore("Issue ???") @Test - public void TypeEquals_Valid_37() { - runTest("TypeEquals_Valid_37"); - } - - @Ignore("Issue ???") @Test - public void TypeEquals_Valid_38() { - runTest("TypeEquals_Valid_38"); - } - - @Test - public void TypeEquals_Valid_39() { - runTest("TypeEquals_Valid_39"); - } - - @Test - public void TypeEquals_Valid_40() { - runTest("TypeEquals_Valid_40"); - } - - @Ignore("Issue ???") @Test - public void TypeEquals_Valid_41() { - runTest("TypeEquals_Valid_41"); - } - - @Test - public void TypeEquals_Valid_42() { - runTest("TypeEquals_Valid_42"); - } - - @Test - public void TypeEquals_Valid_43() { - runTest("TypeEquals_Valid_43"); - } - - @Test - public void TypeEquals_Valid_44() { - runTest("TypeEquals_Valid_44"); - } - - @Ignore("???") @Test - public void TypeEquals_Valid_45() { - runTest("TypeEquals_Valid_45"); - } - - @Test - public void TypeEquals_Valid_46() { - runTest("TypeEquals_Valid_46"); - } - - @Test - public void TypeEquals_Valid_47() { - runTest("TypeEquals_Valid_47"); - } - - @Test - public void TypeEquals_Valid_5() { - runTest("TypeEquals_Valid_5"); - } - - @Test - public void TypeEquals_Valid_6() { - runTest("TypeEquals_Valid_6"); - } - - @Test - public void TypeEquals_Valid_7() { - runTest("TypeEquals_Valid_7"); - } - - @Test - public void TypeEquals_Valid_9() { - runTest("TypeEquals_Valid_9"); - } - - @Test - public void UnionType_Valid_1() { - runTest("UnionType_Valid_1"); - } - - @Test - public void UnionType_Valid_10() { - runTest("UnionType_Valid_10"); - } - - @Test - public void UnionType_Valid_11() { - runTest("UnionType_Valid_11"); - } - - @Test - public void UnionType_Valid_12() { - runTest("UnionType_Valid_12"); - } - - @Test - public void UnionType_Valid_13() { - runTest("UnionType_Valid_13"); - } - - @Test - public void UnionType_Valid_14() { - runTest("UnionType_Valid_14"); - } - - @Test - public void UnionType_Valid_15() { - runTest("UnionType_Valid_15"); - } - - @Test - public void UnionType_Valid_16() { - runTest("UnionType_Valid_16"); - } - - @Test - public void UnionType_Valid_17() { - runTest("UnionType_Valid_17"); - } - - @Test - public void UnionType_Valid_18() { - runTest("UnionType_Valid_18"); - } - - @Test - public void UnionType_Valid_19() { - runTest("UnionType_Valid_19"); - } - - @Test - public void UnionType_Valid_2() { - runTest("UnionType_Valid_2"); - } - - @Test - public void UnionType_Valid_20() { - runTest("UnionType_Valid_20"); - } - - @Test - public void UnionType_Valid_21() { - runTest("UnionType_Valid_21"); - } - - @Test - public void UnionType_Valid_22() { - runTest("UnionType_Valid_22"); - } - - @Test - public void UnionType_Valid_23() { - runTest("UnionType_Valid_23"); - } - - @Test - public void UnionType_Valid_3() { - runTest("UnionType_Valid_3"); - } - - @Test - public void UnionType_Valid_4() { - runTest("UnionType_Valid_4"); - } - - @Test - public void UnionType_Valid_5() { - runTest("UnionType_Valid_5"); - } - - @Test - public void UnionType_Valid_6() { - runTest("UnionType_Valid_6"); - } - - @Test - public void UnionType_Valid_7() { - runTest("UnionType_Valid_7"); - } - - @Test - public void UnionType_Valid_8() { - runTest("UnionType_Valid_8"); - } - - @Test - public void UnionType_Valid_9() { - runTest("UnionType_Valid_9"); - } - - @Ignore("Issue ???") @Test - public void Update_Valid_1() { - runTest("Update_Valid_1"); - } - - @Test - public void Update_Valid_2() { - runTest("Update_Valid_2"); - } - - @Test - public void VarDecl_Valid_1() { - runTest("VarDecl_Valid_1"); - } - - @Test - public void VarDecl_Valid_2() { - runTest("VarDecl_Valid_2"); - } - - @Test - public void VarDecl_Valid_3() { - runTest("VarDecl_Valid_3"); - } - - @Test - public void VarDecl_Valid_4() { - runTest("VarDecl_Valid_4"); - } - - @Test - public void While_Valid_1() { - runTest("While_Valid_1"); - } - - @Test - public void While_Valid_10() { - runTest("While_Valid_10"); - } - - @Test - public void While_Valid_11() { - runTest("While_Valid_11"); - } - - @Test - public void While_Valid_12() { - runTest("While_Valid_12"); - } - - @Test - public void While_Valid_14() { - runTest("While_Valid_14"); - } - - @Ignore("unknown") @Test - public void While_Valid_15() { - runTest("While_Valid_15"); - } - - @Test - public void While_Valid_16() { - runTest("While_Valid_16"); - } - - @Test - public void While_Valid_17() { - runTest("While_Valid_17"); - } - - @Test - public void While_Valid_18() { - runTest("While_Valid_18"); - } - - @Test - public void While_Valid_19() { - runTest("While_Valid_19"); - } - - @Test - public void While_Valid_2() { - runTest("While_Valid_2"); - } - - @Test - public void While_Valid_20() { - runTest("While_Valid_20"); - } - - @Test - public void While_Valid_21() { - runTest("While_Valid_21"); - } - - @Test - public void While_Valid_22() { - runTest("While_Valid_22"); - } - - @Test - public void While_Valid_23() { - runTest("While_Valid_23"); - } - - @Test - public void While_Valid_24() { - runTest("While_Valid_24"); - } - - @Test - public void While_Valid_25() { - runTest("While_Valid_25"); - } - - @Test - public void While_Valid_26() { - runTest("While_Valid_26"); - } - - @Test - public void While_Valid_27() { - runTest("While_Valid_27"); - } - - @Test - public void While_Valid_28() { - runTest("While_Valid_28"); - } - - @Test - public void While_Valid_29() { - runTest("While_Valid_29"); - } - - @Test - public void While_Valid_30() { - runTest("While_Valid_30"); - } - - @Test - public void While_Valid_31() { - runTest("While_Valid_31"); - } - - @Test - public void While_Valid_32() { - runTest("While_Valid_32"); - } - - @Test - public void While_Valid_33() { - runTest("While_Valid_33"); - } - - @Test - public void While_Valid_34() { - runTest("While_Valid_34"); - } - - @Test - public void While_Valid_35() { - runTest("While_Valid_35"); - } - - @Test - public void While_Valid_36() { - runTest("While_Valid_36"); - } - - @Test - public void While_Valid_37() { - runTest("While_Valid_37"); - } - - - @Test - public void While_Valid_38() { - runTest("While_Valid_38"); - } - - @Test - public void While_Valid_39() { - runTest("While_Valid_39"); - } - - @Test - public void While_Valid_40() { - runTest("While_Valid_40"); - } - - @Test - public void While_Valid_41() { - runTest("While_Valid_41"); - } - - @Test - public void While_Valid_42() { - runTest("While_Valid_42"); - } - - @Test - public void While_Valid_43() { - runTest("While_Valid_43"); - } - - @Test - public void While_Valid_44() { - runTest("While_Valid_44"); - } - - @Test - public void While_Valid_45() { - runTest("While_Valid_45"); - } - - @Test - public void While_Valid_46() { - runTest("While_Valid_46"); - } - - @Test - public void While_Valid_3() { - runTest("While_Valid_3"); + // Parameter to test case is the name of the current test. + // It will be passed to the constructor by JUnit. + private final String testName; + public RuntimeValidTests(String testName) { + this.testName = testName; } - public void While_Valid_5() { - runTest("While_Valid_5"); + // Here we enumerate all available test cases. + @Parameters(name = "{0}") + public static Collection data() { + return TestUtils.findTestNames(WHILEY_SRC_DIR); } - public void While_Valid_7() { - runTest("While_Valid_7"); + // Skip ignored tests + @Before + public void beforeMethod() { + String ignored = IGNORED.get(this.testName); + Assume.assumeTrue("Test " + this.testName + " skipped: " + ignored, ignored == null); } @Test - public void While_Valid_9() { - runTest("While_Valid_9"); + public void valid() throws IOException { + runTest(this.testName); } } From bd1497f6dced3a068f60df6b194c0baedd784bd6 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Fri, 25 Mar 2016 23:47:35 +1300 Subject: [PATCH 09/43] spam stdout when running test cases on travis Travis needs to be spammed with some kind of output, otherwise our build gets terminated with "No output has been received in the last 10 minutes, this potentially indicates a stalled build or something wrong with the build itself." --- .travis.yml | 3 ++- modules/wyc/build.xml | 2 +- modules/wyc/src/wyc/testing/AllInvalidTests.java | 5 ++++- modules/wyc/src/wyc/testing/AllValidTests.java | 3 +++ modules/wyc/src/wyc/testing/AllValidVerificationTests.java | 3 +++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2203d911df..691f6b7049 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,5 @@ jdk: # script: - ant test-all + - touch running_on_travis + - ant test-all diff --git a/modules/wyc/build.xml b/modules/wyc/build.xml index d66e55b51d..7fb7a86bcf 100755 --- a/modules/wyc/build.xml +++ b/modules/wyc/build.xml @@ -47,7 +47,7 @@ - + diff --git a/modules/wyc/src/wyc/testing/AllInvalidTests.java b/modules/wyc/src/wyc/testing/AllInvalidTests.java index ff9c938f37..5aea055c2e 100755 --- a/modules/wyc/src/wyc/testing/AllInvalidTests.java +++ b/modules/wyc/src/wyc/testing/AllInvalidTests.java @@ -243,6 +243,9 @@ public void beforeMethod() { @Test public void invalid() throws IOException { + if (new File("../../running_on_travis").exists()) { + System.out.println("."); + } runTest(this.testName); } -} \ No newline at end of file +} diff --git a/modules/wyc/src/wyc/testing/AllValidTests.java b/modules/wyc/src/wyc/testing/AllValidTests.java index df219892fc..326d8368b9 100644 --- a/modules/wyc/src/wyc/testing/AllValidTests.java +++ b/modules/wyc/src/wyc/testing/AllValidTests.java @@ -211,6 +211,9 @@ public void beforeMethod() { @Test public void valid() throws IOException { + if (new File("../../running_on_travis").exists()) { + System.out.println("."); + } runTest(this.testName); } } diff --git a/modules/wyc/src/wyc/testing/AllValidVerificationTests.java b/modules/wyc/src/wyc/testing/AllValidVerificationTests.java index 1e1d83dea6..37035d75a9 100644 --- a/modules/wyc/src/wyc/testing/AllValidVerificationTests.java +++ b/modules/wyc/src/wyc/testing/AllValidVerificationTests.java @@ -296,6 +296,9 @@ public void beforeMethod() { @Test public void validVerification() throws IOException { + if (new File("../../running_on_travis").exists()) { + System.out.println("."); + } runTest(this.testName); } } From 78ff545bc25d48f994330341423d037136ae3014 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Tue, 29 Mar 2016 14:31:31 +1300 Subject: [PATCH 10/43] closes #612 --- .gitignore | 3 ++- modules/wyc/src/wyc/builder/FlowTypeChecker.java | 1 + tests/valid/Lambda_Valid_10.whiley | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/valid/Lambda_Valid_10.whiley diff --git a/.gitignore b/.gitignore index 80870180c5..35f45b77e7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,6 @@ *.smt2 /report/ /build/ +/dist/ /modules/wycs/src/wycs/core/Types.java -/modules/wycs/src/wycs/solver/Solver.java \ No newline at end of file +/modules/wycs/src/wycs/solver/Solver.java diff --git a/modules/wyc/src/wyc/builder/FlowTypeChecker.java b/modules/wyc/src/wyc/builder/FlowTypeChecker.java index 6aeb54e24e..5ac3e0cdfe 100644 --- a/modules/wyc/src/wyc/builder/FlowTypeChecker.java +++ b/modules/wyc/src/wyc/builder/FlowTypeChecker.java @@ -1583,6 +1583,7 @@ private Expr propagate(Expr.AbstractFunctionOrMethod expr, Environment environme } private Expr propagate(Expr.Lambda expr, Environment environment, Context context) throws IOException { + environment = environment.clone(); Type.FunctionOrMethod rawResultType; Type.FunctionOrMethod nomResultType; ArrayList rawParameterTypes = new ArrayList(); diff --git a/tests/valid/Lambda_Valid_10.whiley b/tests/valid/Lambda_Valid_10.whiley new file mode 100644 index 0000000000..9680b10d64 --- /dev/null +++ b/tests/valid/Lambda_Valid_10.whiley @@ -0,0 +1,6 @@ +type myfun is function(int)->(int) + +public export method test(): + myfun f = &(int x -> x) + int x = f(1) + assume x == 1 From 3afdc8afefce77512db8a905e825a4a45e933900 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Sun, 27 Mar 2016 16:58:13 +1300 Subject: [PATCH 11/43] Remove Codes.Void and Codes.NULL_REG #502 The Codes.Void bytecode makes heaps of sense but is completely unused at this stage. For simplicity it's gone for now. The Codes.NULL_REG and related constants are no longer required and have been removed. --- .../wyil/checks/DefiniteAssignmentCheck.java | 2 +- modules/wyil/src/wyil/io/WyilFileReader.java | 5 -- modules/wyil/src/wyil/lang/Code.java | 3 +- modules/wyil/src/wyil/lang/CodeUtils.java | 6 +- modules/wyil/src/wyil/lang/Codes.java | 88 ------------------- modules/wyil/src/wyil/util/Interpreter.java | 11 --- 6 files changed, 3 insertions(+), 112 deletions(-) diff --git a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java index 497d4854eb..163fdc05a4 100755 --- a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java +++ b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java @@ -189,7 +189,7 @@ public void checkUses(CodeForest.Index index, Code code, HashSet in) { if (code instanceof Code.AbstractBytecode) { Code.AbstractBytecode a = (Code.AbstractBytecode) code; for (int operand : a.operands()) { - if (operand != Codes.NULL_REG && !in.contains(operand)) { + if (!in.contains(operand)) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, forest.get(index).attribute(SourceLocation.class)); } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 6698f3cf17..b205aa11dc 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -1122,10 +1122,5 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob return Codes.Update(types[0], targets[0], operands, operand, types[1], fields); } }; - schemas[Code.OPCODE_void] = new Schema(Targets.MANY, Operands.ZERO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Void(types[0], targets); - } - }; } } diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index 682cd2260f..816c904802 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -399,8 +399,7 @@ public boolean equals(Object o) { public static final int OPCODE_lambda = NARY_ASSIGNABLE+4; public static final int OPCODE_loop = NARY_ASSIGNABLE+5; public static final int OPCODE_quantify = NARY_ASSIGNABLE+6; - public static final int OPCODE_update = NARY_ASSIGNABLE+7; - public static final int OPCODE_void = NARY_ASSIGNABLE+8; + public static final int OPCODE_update = NARY_ASSIGNABLE+7; // ========================================================================= // Bytecode Schemas diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java index 507135eef4..0a4ff4be19 100644 --- a/modules/wyil/src/wyil/lang/CodeUtils.java +++ b/modules/wyil/src/wyil/lang/CodeUtils.java @@ -20,11 +20,7 @@ public static String arrayToString(int... operands) { if (i != 0) { r = r + ", "; } - if(operands[i] == Codes.NULL_REG) { - r = r + "_"; - } else { - r = r + "%" + operands[i]; - } + r = r + "%" + operands[i]; } return r + ")"; } diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index fc4ca0553b..188de6df28 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -17,54 +17,6 @@ public abstract class Codes { - /** - * Provided to aid readability of client code. - */ - public final static int NULL_REG = -1; - /** - * Provided to aid readability of client code. - */ - public final static int REG_0 = 0; - /** - * Provided to aid readability of client code. - */ - public final static int REG_1 = 1; - /** - * Provided to aid readability of client code. - */ - public final static int REG_2 = 2; - /** - * Provided to aid readability of client code. - */ - public final static int REG_3 = 3; - /** - * Provided to aid readability of client code. - */ - public final static int REG_4 = 4; - /** - * Provided to aid readability of client code. - */ - public final static int REG_5 = 5; - /** - * Provided to aid readability of client code. - */ - public final static int REG_6 = 6; - - /** - * Provided to aid readability of client code. - */ - public final static int REG_7 = 7; - - /** - * Provided to aid readability of client code. - */ - public final static int REG_8 = 8; - - /** - * Provided to aid readability of client code. - */ - public final static int REG_9 = 9; - // =============================================================== // Bytecode Constructors // =============================================================== @@ -394,10 +346,6 @@ public static UnaryOperator UnaryOperator(Type type, int target, int operand, return new UnaryOperator(type, target, operand, uop); } - public static Void Void(Type type, int[] operands) { - return new Void(type, operands); - } - // =============================================================== // Bytecode Implementations // =============================================================== @@ -2755,42 +2703,6 @@ public String toString() { } } - /** - * The void bytecode is used to indicate that the given register(s) are no - * longer live. This is useful for communicating information to the memory - * management system about which values could in principle be collected. - * - * @author David J. Pearce - * - */ - public static class Void extends AbstractBytecode { - - private Void(Type type, int[] operands) { - super(type, Codes.NULL_REG, operands); - } - - @Override - public int opcode() { - return OPCODE_void; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return Void(type(0), nOperands); - } - - public boolean equals(Object o) { - if (o instanceof Void) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "void " + arrayToString(operands()); - } - } - // ============================================================= // Helpers // ============================================================= diff --git a/modules/wyil/src/wyil/util/Interpreter.java b/modules/wyil/src/wyil/util/Interpreter.java index a31c8e08de..ea85cc3cf4 100644 --- a/modules/wyil/src/wyil/util/Interpreter.java +++ b/modules/wyil/src/wyil/util/Interpreter.java @@ -220,8 +220,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.UnaryOperator) bytecode, frame, context); } else if (bytecode instanceof Codes.Update) { return execute((Codes.Update) bytecode, frame, context); - } else if (bytecode instanceof Codes.Void) { - return execute((Codes.Void) bytecode, frame, context); } else { throw new IllegalArgumentException("Unknown bytecode encountered: " + bytecode); @@ -1261,15 +1259,6 @@ private Constant update(Constant lhs, Iterator descriptor, } } - private Object execute(Codes.Void bytecode, Constant[] frame, - Context context) { - // This bytecode just voids out all of its operands. - for (int operand : bytecode.operands()) { - frame[operand] = null; - } - return context.pc.next(); - } - /** * Check that a given operand value matches an expected type. * From 536b1236969f11005154d12aba59590f0a299b75 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Sun, 27 Mar 2016 17:22:38 +1300 Subject: [PATCH 12/43] Replaecd Unary/Binary/Not/Invert bytecodes These bytecodes are in the process of being replaced with a single bytecode Operator. --- .../wyc/src/wyc/builder/CodeGenerator.java | 59 ++-- .../src/wyil/builders/VcExprGenerator.java | 69 ++-- modules/wyil/src/wyil/builders/VcUtils.java | 4 +- modules/wyil/src/wyil/checks/ModuleCheck.java | 10 +- modules/wyil/src/wyil/io/WyilFileReader.java | 31 +- modules/wyil/src/wyil/lang/Code.java | 36 +- modules/wyil/src/wyil/lang/Codes.java | 312 +++--------------- modules/wyil/src/wyil/util/Interpreter.java | 137 ++++---- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 79 ++--- 9 files changed, 237 insertions(+), 500 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index f4ef03ce4f..b4a679e6e6 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1885,24 +1885,25 @@ private int generate(Expr.LocalVariable expr, Environment environment, CodeFores private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - int operand = generate(expr.mhs, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); + int[] operands = new int[] { generate(expr.mhs, environment, block, forest, context) }; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; switch (expr.op) { case NEG: - block.add(Codes.UnaryOperator(expr.result().raw(), target, operand, Codes.UnaryOperatorKind.NEG), + block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.NEG), attributes(expr)); break; case INVERT: - block.add(Codes.Invert(expr.result().raw(), target, operand), attributes(expr)); + block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.INVERT), + attributes(expr)); break; case NOT: String falseLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); generateCondition(falseLabel, expr.mhs, environment, block, forest, context); - block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(expr)); + block.add(Codes.Const(targets[0], Constant.V_BOOL(true)), attributes(expr)); block.add(Codes.Goto(exitLabel)); block.add(Codes.Label(falseLabel)); - block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(expr)); + block.add(Codes.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); block.add(Codes.Label(exitLabel)); break; default: @@ -1910,7 +1911,7 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b internalFailure("unexpected unary operator encountered", context, expr); return -1; } - return target; + return targets[0]; } private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Block block, CodeForest forest, @@ -1923,10 +1924,11 @@ private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Blo private int generate(Expr.Dereference expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - int operand = generate(expr.src, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(Codes.Dereference(expr.srcType.raw(), target, operand), attributes(expr)); - return target; + int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.DEREFERENCE), + attributes(expr)); + return targets[0]; } private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Block block, CodeForest forest, @@ -1966,16 +1968,17 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo return target; } else { - - int leftOperand = generate(v.lhs, environment, block, forest, context); - int rightOperand = generate(v.rhs, environment, block, forest, context); Type result = v.result().raw(); - int target = environment.allocate(result); + int[] targets = new int[] { environment.allocate(result) }; + int[] operands = { + generate(v.lhs, environment, block, forest, context), + generate(v.rhs, environment, block, forest, context) + }; - block.add(Codes.BinaryOperator(result, target, leftOperand, rightOperand, OP2BOP(v.op, v, context)), + block.add(Codes.Operator(result, targets, operands, OP2BOP(v.op, v, context)), attributes(v)); - return target; + return targets[0]; } } @@ -2056,28 +2059,28 @@ private int[] generate(List arguments, Environment environment, CodeForest // Helpers // ========================================================================= - private Codes.BinaryOperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context context) { + private Codes.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context context) { switch (bop) { case ADD: - return Codes.BinaryOperatorKind.ADD; + return Codes.OperatorKind.ADD; case SUB: - return Codes.BinaryOperatorKind.SUB; + return Codes.OperatorKind.SUB; case MUL: - return Codes.BinaryOperatorKind.MUL; + return Codes.OperatorKind.MUL; case DIV: - return Codes.BinaryOperatorKind.DIV; + return Codes.OperatorKind.DIV; case REM: - return Codes.BinaryOperatorKind.REM; + return Codes.OperatorKind.REM; case BITWISEAND: - return Codes.BinaryOperatorKind.BITWISEAND; + return Codes.OperatorKind.BITWISEAND; case BITWISEOR: - return Codes.BinaryOperatorKind.BITWISEOR; + return Codes.OperatorKind.BITWISEOR; case BITWISEXOR: - return Codes.BinaryOperatorKind.BITWISEXOR; + return Codes.OperatorKind.BITWISEXOR; case LEFTSHIFT: - return Codes.BinaryOperatorKind.LEFTSHIFT; + return Codes.OperatorKind.LEFTSHIFT; case RIGHTSHIFT: - return Codes.BinaryOperatorKind.RIGHTSHIFT; + return Codes.OperatorKind.RIGHTSHIFT; default: syntaxError(errorMessage(INVALID_BINARY_EXPRESSION), context, elem); } diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index 852b489428..c1ada38d13 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -53,10 +53,8 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { if (code instanceof Codes.LengthOf) { transformUnary(Expr.Unary.Op.LENGTHOF, (Codes.LengthOf) code, branch, forest); - } else if (code instanceof Codes.BinaryOperator) { - Codes.BinaryOperator bc = (Codes.BinaryOperator) code; - transformBinary(binaryOperatorMap[bc.kind.ordinal()], bc, - branch, forest); + } else if (code instanceof Codes.Operator) { + transform((Codes.Operator) code, forest, branch); } else if (code instanceof Codes.ArrayGenerator) { transform((Codes.ArrayGenerator) code, forest, branch); } else if (code instanceof Codes.NewArray) { @@ -75,8 +73,6 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { transform((Codes.IndirectInvoke) code, forest, branch); } else if (code instanceof Codes.Invoke) { transform((Codes.Invoke) code, forest, branch); - } else if (code instanceof Codes.Invert) { - transform((Codes.Invert) code, forest, branch); } else if (code instanceof Codes.Label) { // skip } else if (code instanceof Codes.IndexOf) { @@ -87,10 +83,6 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { transform((Codes.Assign) code, forest, branch); } else if (code instanceof Codes.Update) { transform((Codes.Update) code, forest, branch); - } else if (code instanceof Codes.UnaryOperator) { - transform((Codes.UnaryOperator) code, forest, branch); - } else if (code instanceof Codes.Dereference) { - transform((Codes.Dereference) code, forest, branch); } else if (code instanceof Codes.Nop) { // skip } else if (code instanceof Codes.NewObject) { @@ -120,11 +112,15 @@ protected void transform(Codes.Assign code, CodeForest forest, /** * Maps binary bytecodes into expression opcodes. */ - private static Expr.Binary.Op[] binaryOperatorMap = { Expr.Binary.Op.ADD, + private static Expr.Binary.Op[] binaryOperatorMap = { + null, // neg + null, // invert + null, // deref + Expr.Binary.Op.ADD, Expr.Binary.Op.SUB, Expr.Binary.Op.MUL, Expr.Binary.Op.DIV, - Expr.Binary.Op.REM, + Expr.Binary.Op.REM, null, null, // bitwise or null, // bitwise xor @@ -133,6 +129,37 @@ protected void transform(Codes.Assign code, CodeForest forest, null // right shift }; + protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch) { + switch(code.kind) { + case NEG:{ + Codes.Operator bc = (Codes.Operator) code; + transformUnary(Expr.Unary.Op.NEG, bc, branch, forest); + break; + } + case INVERT: + case DEREFERENCE: { + branch.havoc(code.target(0)); + break; + } + case ADD: + case SUB: + case MUL: + case DIV: + case REM:{ + transformBinary(binaryOperatorMap[code.kind.ordinal()], code, branch, forest); + break; + } + case BITWISEAND: + case BITWISEOR: + case BITWISEXOR: + case LEFTSHIFT: + case RIGHTSHIFT: { + branch.havoc(code.target(0)); + break; + } + } + } + protected void transform(Codes.Convert code, CodeForest forest, VcBranch branch) { Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()); Expr result = branch.read(code.operand(0)); @@ -150,10 +177,6 @@ protected void transform(Codes.Debug code, CodeForest forest, // do nout } - protected void transform(Codes.Dereference code, CodeForest forest, VcBranch branch) { - branch.havoc(code.target(0)); - } - protected void transform(Codes.FieldLoad code, CodeForest forest, VcBranch branch) { ArrayList fields = new ArrayList(code.type(0).fields().keySet()); Collections.sort(fields); @@ -222,10 +245,6 @@ protected void transform(Codes.Invoke code, CodeForest forest, } } - protected void transform(Codes.Invert code, CodeForest forest, VcBranch branch) { - branch.havoc(code.target(0)); - } - protected void transform(Codes.IndexOf code, CodeForest forest, VcBranch branch) { Expr src = branch.read(code.operand(0)); Expr idx = branch.read(code.operand(1)); @@ -266,16 +285,6 @@ protected void transform(Codes.Nop code, CodeForest forest, // do nout } - protected void transform(Codes.UnaryOperator code, CodeForest forest, VcBranch branch) { - switch (code.kind) { - case NEG: - transformUnary(Expr.Unary.Op.NEG, code, branch, forest); - break; - default: - branch.havoc(code.target(0)); - } - } - protected void transform(Codes.Update code, CodeForest forest, VcBranch branch) { Expr result = branch.read(code.result()); Expr oldSource = branch.read(code.target(0)); diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java index ddb3217879..1af1a85903 100644 --- a/modules/wyil/src/wyil/builders/VcUtils.java +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -368,7 +368,7 @@ public Pair[] getPreconditions(Code code, VcBranch branch, switch (code.opcode()) { case Code.OPCODE_div: case Code.OPCODE_rem: - return divideByZeroCheck((Codes.BinaryOperator) code, branch); + return divideByZeroCheck((Codes.Operator) code, branch); case Code.OPCODE_indexof: return indexOutOfBoundsChecks((Codes.IndexOf) code, branch); case Code.OPCODE_arrygen: @@ -395,7 +395,7 @@ public Pair[] getPreconditions(Code code, VcBranch branch, * --- The branch the division is on. * @return */ - public Pair[] divideByZeroCheck(Codes.BinaryOperator binOp, VcBranch branch) { + public Pair[] divideByZeroCheck(Codes.Operator binOp, VcBranch branch) { Expr rhs = branch.read(binOp.operand(1)); Value zero; if (binOp.type(0) instanceof Type.Int) { diff --git a/modules/wyil/src/wyil/checks/ModuleCheck.java b/modules/wyil/src/wyil/checks/ModuleCheck.java index 973ef42bc7..e8ca8d0ca9 100755 --- a/modules/wyil/src/wyil/checks/ModuleCheck.java +++ b/modules/wyil/src/wyil/checks/ModuleCheck.java @@ -140,10 +140,12 @@ protected void checkFunctionPure(int blockID, CodeForest forest) { syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if(code instanceof Codes.NewObject) { syntaxError(errorMessage(ALLOCATION_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if(code instanceof Codes.Dereference){ - syntaxError(errorMessage(REFERENCE_ACCESS_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if(code instanceof Code.AbstractCompoundBytecode) { - Code.AbstractCompoundBytecode a = (Code.AbstractCompoundBytecode) code; + } else if (code instanceof Codes.Operator + && ((Codes.Operator) code).kind == Codes.OperatorKind.DEREFERENCE) { + syntaxError(errorMessage(REFERENCE_ACCESS_NOT_PERMITTED_IN_FUNCTION), filename, + e.attribute(SourceLocation.class)); + } else if (code instanceof Code.AbstractCompoundBytecode) { + Code.AbstractCompoundBytecode a = (Code.AbstractCompoundBytecode) code; checkFunctionPure(a.block(), forest); } } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index b205aa11dc..b267a0b8d9 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -935,12 +935,12 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob }; schemas[Code.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Dereference((Type.Reference) types[0], targets[0], operands[0]); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DEREFERENCE); } }; schemas[Code.OPCODE_invert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Invert(types[0], targets[0], operands[0]); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.INVERT); } }; schemas[Code.OPCODE_lengthof] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ @@ -950,12 +950,7 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob }; schemas[Code.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.UnaryOperator(types[0], targets[0], operands[0], Codes.UnaryOperatorKind.NEG); - } - }; - schemas[Code.OPCODE_not] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Not(targets[0], operands[0]); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.NEG); } }; schemas[Code.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ @@ -1013,52 +1008,52 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= schemas[Code.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.ADD); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ADD); } }; schemas[Code.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.SUB); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.SUB); } }; schemas[Code.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.MUL); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.MUL); } }; schemas[Code.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.DIV); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DIV); } }; schemas[Code.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.REM); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.REM); } }; schemas[Code.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.BITWISEOR); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEOR); } }; schemas[Code.OPCODE_bitwisexor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.BITWISEXOR); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEXOR); } }; schemas[Code.OPCODE_bitwiseand] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.BITWISEAND); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEAND); } }; schemas[Code.OPCODE_lshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.LEFTSHIFT); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.LEFTSHIFT); } }; schemas[Code.OPCODE_rshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.BinaryOperator(types[0], targets[0], operands[0], operands[1], Codes.BinaryOperatorKind.RIGHTSHIFT); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RIGHTSHIFT); } }; schemas[Code.OPCODE_indexof] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index 816c904802..050ce4c1fa 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -346,13 +346,12 @@ public boolean equals(Object o) { public static final int UNARY_ASSIGNABLE = UNARY_OPERATOR+5; public static final int OPCODE_assign = UNARY_ASSIGNABLE+0; - public static final int OPCODE_dereference = UNARY_ASSIGNABLE+1; - public static final int OPCODE_invert = UNARY_ASSIGNABLE+2; + public static final int OPCODE_lengthof = UNARY_ASSIGNABLE+3; public static final int OPCODE_move = UNARY_ASSIGNABLE+4; public static final int OPCODE_newobject = UNARY_ASSIGNABLE+5; - public static final int OPCODE_neg = UNARY_ASSIGNABLE+6; - public static final int OPCODE_not = UNARY_ASSIGNABLE+7; + + //public static final int OPCODE_not = UNARY_ASSIGNABLE+7; public static final int OPCODE_fieldload = UNARY_ASSIGNABLE+8; public static final int OPCODE_convert = UNARY_ASSIGNABLE+9; public static final int OPCODE_const = UNARY_ASSIGNABLE+10; @@ -374,23 +373,26 @@ public boolean equals(Object o) { // ========================================================================= public static final int BINARY_ASSIGNABLE = BINARY_OPERATOR+6; - public static final int OPCODE_add = BINARY_ASSIGNABLE+0; - public static final int OPCODE_sub = BINARY_ASSIGNABLE+1; - public static final int OPCODE_mul = BINARY_ASSIGNABLE+2; - public static final int OPCODE_div = BINARY_ASSIGNABLE+3; - public static final int OPCODE_rem = BINARY_ASSIGNABLE+4; - public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+5; - public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+6; - public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+7; - public static final int OPCODE_lshr = BINARY_ASSIGNABLE+8; - public static final int OPCODE_rshr = BINARY_ASSIGNABLE+9; - public static final int OPCODE_indexof = BINARY_ASSIGNABLE+10; - public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+11; + public static final int OPCODE_neg = BINARY_ASSIGNABLE+0; + public static final int OPCODE_invert = BINARY_ASSIGNABLE+1; + public static final int OPCODE_dereference = BINARY_ASSIGNABLE+2; + public static final int OPCODE_add = BINARY_ASSIGNABLE+3; + public static final int OPCODE_sub = BINARY_ASSIGNABLE+4; + public static final int OPCODE_mul = BINARY_ASSIGNABLE+5; + public static final int OPCODE_div = BINARY_ASSIGNABLE+6; + public static final int OPCODE_rem = BINARY_ASSIGNABLE+7; + public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+8; + public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+9; + public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+10; + public static final int OPCODE_lshr = BINARY_ASSIGNABLE+11; + public static final int OPCODE_rshr = BINARY_ASSIGNABLE+12; + public static final int OPCODE_indexof = BINARY_ASSIGNABLE+13; + public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+14; // ========================================================================= // Nary Assignables // ========================================================================= - public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+12; + public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+15; public static final int OPCODE_newarray = NARY_ASSIGNABLE+0; public static final int OPCODE_newrecord = NARY_ASSIGNABLE+1; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index 188de6df28..2bdd93ce7b 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -45,10 +45,8 @@ public static Assume Assume(int block) { return new Assume(block); } - public static BinaryOperator BinaryOperator(Type type, int target, int leftOperand, - int rightOperand, BinaryOperatorKind op) { - return new BinaryOperator(type, target, leftOperand, - rightOperand, op); + public static Operator Operator(Type type, int[] targets, int[] operands, OperatorKind op) { + return new Operator(type, targets, operands, op); } /** @@ -159,10 +157,6 @@ public static Lambda Lambda(Type.FunctionOrMethod fun, int target, return new Lambda(fun, target, operands, name); } - public static Not Not(int target, int operand) { - return new Not(target, operand); - } - public static LengthOf LengthOf(Type.EffectiveArray type, int target, int operand) { return new LengthOf(type, target, operand); @@ -286,10 +280,6 @@ public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, return new IndirectInvoke(fun, targets, operand, operands); } - public static Invert Invert(Type type, int target, int operand) { - return new Invert(type, target, operand); - } - public static Label Label(String label) { return new Label(label); } @@ -318,11 +308,6 @@ public static NewObject NewObject(Type.Reference type, int target, return new NewObject(type, target, operand); } - public static Dereference Dereference(Type.Reference type, int target, - int operand) { - return new Dereference(type, target, operand); - } - public static Quantify Quantify(int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, int block) { return new Quantify(startOperand, endOperand, indexOperand, modifiedOperands, block); @@ -341,11 +326,6 @@ public static Update Update(Type beforeType, int target, int[] operands, afterType, fields); } - public static UnaryOperator UnaryOperator(Type type, int target, int operand, - UnaryOperatorKind uop) { - return new UnaryOperator(type, target, operand, uop); - } - // =============================================================== // Bytecode Implementations // =============================================================== @@ -357,60 +337,77 @@ public static UnaryOperator UnaryOperator(Type type, int target, int operand, * @author David J. Pearce * */ - public enum BinaryOperatorKind { - ADD(0) { + public enum OperatorKind { + // Unary + NEG(0) { + public String toString() { + return "neg"; + } + }, + INVERT(1) { + public String toString() { + return "invert"; + } + }, + DEREFERENCE(2) { + public String toString() { + return "deref"; + } + }, + // Binary + ADD(3) { public String toString() { return "add"; } }, - SUB(1) { + SUB(4) { public String toString() { return "sub"; } }, - MUL(2) { + MUL(5) { public String toString() { return "mul"; } }, - DIV(3) { + DIV(6) { public String toString() { return "div"; } }, - REM(4) { + REM(7) { public String toString() { return "rem"; } }, - BITWISEOR(5) { + BITWISEOR(8) { public String toString() { return "or"; } }, - BITWISEXOR(6) { + BITWISEXOR(9) { public String toString() { return "xor"; } }, - BITWISEAND(7) { + BITWISEAND(10) { public String toString() { return "and"; } }, - LEFTSHIFT(8) { + LEFTSHIFT(11) { public String toString() { return "shl"; } }, - RIGHTSHIFT(9) { + RIGHTSHIFT(12) { public String toString() { return "shr"; } }; public int offset; - private BinaryOperatorKind(int offset) { + private OperatorKind(int offset) { this.offset = offset; } }; @@ -457,28 +454,27 @@ private BinaryOperatorKind(int offset) { * @author David J. Pearce * */ - public static final class BinaryOperator extends AbstractBytecode { - public final BinaryOperatorKind kind; + public static final class Operator extends AbstractBytecode { + public final OperatorKind kind; - private BinaryOperator(Type type, int target, int lhs, int rhs, - BinaryOperatorKind bop) { - super(type, target, lhs, rhs); + private Operator(Type type, int[] targets, int[] operands, + OperatorKind bop) { + super(new Type[]{ type }, targets, operands); if (bop == null) { throw new IllegalArgumentException( - "BinOp bop argument cannot be null"); + "Operator kind cannot be null"); } this.kind = bop; } @Override - public int opcode() { - return OPCODE_add + kind.offset; + public int opcode() { + return OPCODE_neg + kind.offset; } @Override public Code clone(int[] nTargets, int[] nOperands) { - return BinaryOperator(type(0), nTargets[0], nOperands[0], nOperands[1], - kind); + return Operator(type(0), nTargets, nOperands, kind); } public int hashCode() { @@ -486,16 +482,15 @@ public int hashCode() { } public boolean equals(Object o) { - if (o instanceof BinaryOperator) { - BinaryOperator bo = (BinaryOperator) o; + if (o instanceof Operator) { + Operator bo = (Operator) o; return kind.equals(bo.kind) && super.equals(bo); } return false; } public String toString() { - return kind + " %" + target(0) + " = %" + operand(0) + ", %" - + operand(1) + " : " + type(0); + return kind + " %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0); } } @@ -1339,62 +1334,6 @@ public Invariant clone() { } } - /** - * Read a boolean value from the operand register, inverts it and writes the - * result to the target register. For example, the following Whiley code: - * - *

-	 * function f(bool x) -> bool:
-	 *     return !x
-	 * 
- * - * can be translated into the following WyIL: - * - *
-	 * function f(bool x) -> bool:
-	 * body:
-	 *     not %0 = %0     : int
-	 *     return %0       : int
-	 * 
- * - * This simply reads the parameter x stored in register - * %0, inverts it and then returns the inverted value. - * - * @author David J. Pearce - * - */ - public static final class Not extends AbstractBytecode { - - private Not(int target, int operand) { - super(Type.T_BOOL, target, operand); - } - - public int opcode() { - return OPCODE_not; - } - - @Override - public Code clone(int[] nTargets, int[] nOperands) { - return Not(nTargets[0], nOperands[0]); - } - - public int hashCode() { - return super.hashCode(); - } - - public boolean equals(Object o) { - if (o instanceof Not) { - Not n = (Not) o; - return super.equals(n); - } - return false; - } - - public String toString() { - return "not %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } - /** * Corresponds to a function or method call whose parameters are read from * zero or more operand registers. If a return value is required, this is @@ -2482,59 +2421,6 @@ public Code clone(int[] nTargets, int[] nOperands) { } - /** - * Corresponds to a bitwise inversion operation, which reads a byte value - * from the operand register, inverts it and writes the result to the target - * resgister. For example, the following Whiley code: - * - *
-	 * function f(byte x) -> byte:
-	 *    return ~x
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(byte x) -> byte:
-	 * body:
-	 *     invert %0 = %0   : byte
-	 *     return %0        : byte
-	 * 
- * - * Here, the expression ~x generates an invert - * bytecode. - * - * @author David J. Pearce - * - */ - public static final class Invert extends AbstractBytecode { - - private Invert(Type type, int target, int operand) { - super(type, target, operand); - } - - @Override - public int opcode() { - return OPCODE_invert; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return Invert(type(0), nTargets[0], nOperands[0]); - } - - public boolean equals(Object o) { - if (o instanceof Invert) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "invert %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } - /** * Instantiate a new object from the value in a given operand register, and * write the result (a reference to that object) to a given target register. @@ -2591,118 +2477,6 @@ public String toString() { } } - /** - * Reads a reference value from the operand register, dereferences it (i.e. - * extracts the value it refers to) and writes this to the target register. - * - * @author David J. Pearce - * - */ - public static final class Dereference extends AbstractBytecode { - - private Dereference(Type.Reference type, int target, int operand) { - super(type, target, operand); - } - - @Override - public int opcode() { - return OPCODE_dereference; - } - - protected Code clone(int[] nTargets, int[] nOperands) { - return Dereference(type(0), nTargets[0], nOperands[0]); - } - - public boolean equals(Object o) { - if (o instanceof Dereference) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "deref %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } - - public enum UnaryOperatorKind { - NEG(0) { - public String toString() { - return "neg"; - } - }; - - public final int offset; - - private UnaryOperatorKind(int offset) { - this.offset = offset; - } - }; - - /** - * Read a number (int or real) from the operand register, perform a unary - * arithmetic operation on it (e.g. negation) and writes the result to the - * target register. For example, the following Whiley code: - * - *
-	 * function f(int x) -> int:
-	 *     return -x
-	 * 
- * - * can be translated into the following WyIL: - * - *
-	 * function f(int x) -> int:
-	 * body:
-	 *     neg %0 = %0     : int
-	 *     return %0       : int
-	 * 
- * - * This simply reads the parameter x stored in register - * %0, negates it and then returns the negated value. - * - * @author David J. Pearce - * - */ - public static final class UnaryOperator extends AbstractBytecode { - public final UnaryOperatorKind kind; - - private UnaryOperator(Type type, int target, int operand, UnaryOperatorKind uop) { - super(type, target, operand); - if (uop == null) { - throw new IllegalArgumentException( - "UnaryArithOp bop argument cannot be null"); - } - this.kind = uop; - } - - @Override - public int opcode() { - return OPCODE_neg + kind.offset; - } - - @Override - public Code clone(int[] nTargets, int[] nOperands) { - return UnaryOperator(type(0), nTargets[0], nOperands[0], kind); - } - - public int hashCode() { - return kind.hashCode() + super.hashCode(); - } - - public boolean equals(Object o) { - if (o instanceof UnaryOperator) { - UnaryOperator bo = (UnaryOperator) o; - return kind.equals(bo.kind) && super.equals(bo); - } - return false; - } - - public String toString() { - return kind + " %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } - // ============================================================= // Helpers // ============================================================= diff --git a/modules/wyil/src/wyil/util/Interpreter.java b/modules/wyil/src/wyil/util/Interpreter.java index ea85cc3cf4..d33ba50023 100644 --- a/modules/wyil/src/wyil/util/Interpreter.java +++ b/modules/wyil/src/wyil/util/Interpreter.java @@ -161,16 +161,14 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.AssertOrAssume) bytecode, frame, context); } else if (bytecode instanceof Codes.Assign) { return execute((Codes.Assign) bytecode, frame, context); - } else if (bytecode instanceof Codes.BinaryOperator) { - return execute((Codes.BinaryOperator) bytecode, frame, context); + } else if (bytecode instanceof Codes.Operator) { + return execute((Codes.Operator) bytecode, frame, context); } else if (bytecode instanceof Codes.Const) { return execute((Codes.Const) bytecode, frame, context); } else if (bytecode instanceof Codes.Convert) { return execute((Codes.Convert) bytecode, frame, context); } else if (bytecode instanceof Codes.Debug) { return execute((Codes.Debug) bytecode, frame, context); - } else if (bytecode instanceof Codes.Dereference) { - return execute((Codes.Dereference) bytecode, frame, context); } else if (bytecode instanceof Codes.Fail) { return execute((Codes.Fail) bytecode, frame, context); } else if (bytecode instanceof Codes.FieldLoad) { @@ -185,8 +183,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.IndexOf) bytecode, frame, context); } else if (bytecode instanceof Codes.IndirectInvoke) { return execute((Codes.IndirectInvoke) bytecode, frame, context); - } else if (bytecode instanceof Codes.Invert) { - return execute((Codes.Invert) bytecode, frame, context); } else if (bytecode instanceof Codes.Invoke) { return execute((Codes.Invoke) bytecode, frame, context); } else if (bytecode instanceof Codes.Label) { @@ -216,8 +212,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.Return) bytecode, frame, context); } else if (bytecode instanceof Codes.Switch) { return execute((Codes.Switch) bytecode, frame, context); - } else if (bytecode instanceof Codes.UnaryOperator) { - return execute((Codes.UnaryOperator) bytecode, frame, context); } else if (bytecode instanceof Codes.Update) { return execute((Codes.Update) bytecode, frame, context); } else { @@ -283,22 +277,39 @@ private Object execute(Codes.Assign bytecode, Constant[] frame, * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.BinaryOperator bytecode, Constant[] frame, + private Object execute(Codes.Operator bytecode, Constant[] frame, Context context) { - Constant op1 = frame[bytecode.operand(0)]; - Constant op2 = frame[bytecode.operand(1)]; - // Compute result Constant result; - if (op1 instanceof Constant.Integer) { - checkType(op2, context, Constant.Integer.class); - result = execute(bytecode.kind, (Constant.Integer) op1, - (Constant.Integer) op2, context); - } else if (op1 instanceof Constant.Byte) { - checkType(op2, context, Constant.Byte.class, Constant.Integer.class); - result = execute(bytecode.kind, (Constant.Byte) op1, op2, context); - } else { - return deadCode(context); // dead because of above checks + // + switch(bytecode.kind) { + case NEG: + case INVERT: + case DEREFERENCE: + result = executeUnary(bytecode.kind,frame[bytecode.operand(0)],context); + break; + case ADD: + case SUB: + case MUL: + case DIV: + case REM: { + Constant.Integer lhs = checkType(frame[bytecode.operand(0)], context, Constant.Integer.class); + Constant.Integer rhs = checkType(frame[bytecode.operand(1)], context, Constant.Integer.class); + result = execute(bytecode.kind,lhs,rhs,context); + break; + } + case BITWISEXOR: + case BITWISEOR: + case BITWISEAND: + case LEFTSHIFT: + case RIGHTSHIFT: { + Constant.Byte lhs = checkType(frame[bytecode.operand(0)], context, Constant.Byte.class); + Constant rhs = frame[bytecode.operand(1)]; + result = execute(bytecode.kind,lhs,rhs,context); + break; + } + default: + return deadCode(context); } // Write result to target @@ -307,6 +318,26 @@ private Object execute(Codes.BinaryOperator bytecode, Constant[] frame, return context.pc.next(); } + private Constant executeUnary(Codes.OperatorKind kind, + Constant operand, Context context) { + switch(kind) { + case NEG: { + Constant.Integer i = checkType(operand, context, Constant.Integer.class); + return i.negate(); + } + case INVERT: { + Constant.Byte b = checkType(operand, context, Constant.Byte.class); + return Constant.V_BYTE((byte) ~b.value); + } + case DEREFERENCE: { + checkType(operand, context, ConstantObject.class); + ConstantObject ref = (ConstantObject) operand; + return ref.read(); + } + } + return (Constant) deadCode(context); + } + /** * Execute an integer binary operator * @@ -320,7 +351,7 @@ private Object execute(Codes.BinaryOperator bytecode, Constant[] frame, * --- Context in which bytecodes are executed * @return */ - private Constant execute(Codes.BinaryOperatorKind kind, + private Constant execute(Codes.OperatorKind kind, Constant.Integer i1, Constant.Integer i2, Context context) { switch (kind) { case ADD: @@ -351,7 +382,7 @@ private Constant execute(Codes.BinaryOperatorKind kind, * --- Context in which bytecodes are executed * @return */ - private Constant execute(Codes.BinaryOperatorKind kind, Constant.Byte i1, + private Constant execute(Codes.OperatorKind kind, Constant.Byte i1, Constant i2, Context context) { int result; switch (kind) { @@ -557,15 +588,6 @@ private Object execute(Codes.Debug bytecode, Constant[] frame, return context.pc.next(); } - private Object execute(Codes.Dereference bytecode, Constant[] frame, - Context context) { - Constant operand = frame[bytecode.operand(0)]; - checkType(operand, context, ConstantObject.class); - ConstantObject ref = (ConstantObject) operand; - frame[bytecode.target(0)] = ref.read(); - return context.pc.next(); - } - /** * Execute a fail bytecode instruction at a given point in the function or * method body. This will generate a runtime fault. @@ -901,30 +923,6 @@ private Object execute(Codes.Invariant bytecode, Constant[] frame, return context.pc.next(); } - /** - * Execute an Invert bytecode instruction at a given point in the function - * or method body. This checks the operand is a byte value. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.Invert bytecode, Constant[] frame, - Context context) { - Constant operand = frame[bytecode.operand(0)]; - // Check that we have a byte operand - checkType(operand, context, Constant.Byte.class); - // Write back the inverted value - Constant.Byte b = (Constant.Byte) operand; - frame[bytecode.target(0)] = Constant.V_BYTE((byte) ~b.value); - // Done - return context.pc.next(); - } - /** * Execute an Invoke bytecode instruction at a given point in the function * or method body. This generates a recursive call to execute the given @@ -1164,27 +1162,6 @@ private Object execute(Codes.Switch bytecode, Constant[] frame, Context context) return context.getLabel(bytecode.defaultTarget); } - private Object execute(Codes.UnaryOperator bytecode, Constant[] frame, - Context context) { - Constant _operand = frame[bytecode.operand(0)]; - Constant result; - // - switch (bytecode.kind) { - case NEG: - checkType(_operand, context, Constant.Integer.class); - Constant.Integer operand = (Constant.Integer) _operand; - result = operand.negate(); - break; - default: - return deadCode(context); - } - // Assign result to target register - frame[bytecode.target(0)] = result; - // Fall through to next bytecode - return context.pc.next(); - - } - private Object execute(Codes.Update bytecode, Constant[] frame, Context context) { Constant rhs = frame[bytecode.result()]; @@ -1269,17 +1246,17 @@ private Constant update(Constant lhs, Iterator descriptor, * @param types * --- Types to be checked against */ - private void checkType(Constant operand, Context context, - Class... types) { + private T checkType(Constant operand, Context context, Class... types) { // Got through each type in turn checking for a match for (int i = 0; i != types.length; ++i) { if (types[i].isInstance(operand)) { // Matched! - return; + return (T) operand; } } // No match, therefore through an error error("invalid operand", context); + return null; } /** diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 6d3b192de6..d5bd2df285 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -581,8 +581,8 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f ArrayList bytecodes) { try { - if (code instanceof Codes.BinaryOperator) { - translate(pc, (Codes.BinaryOperator) code, freeSlot, forest, bytecodes); + if (code instanceof Codes.Operator) { + translate(pc, (Codes.Operator) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Convert) { translate(pc, (Codes.Convert) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Const) { @@ -609,8 +609,6 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.IndirectInvoke) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Invoke) { translate(pc, (Codes.Invoke) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Invert) { - translate(pc, (Codes.Invert) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Label) { translate(pc, (Codes.Label) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.ArrayGenerator) { @@ -633,10 +631,6 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.NewArray) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.NewRecord) { translate(pc, (Codes.NewRecord) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.UnaryOperator) { - translate(pc, (Codes.UnaryOperator) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Dereference) { - translate(pc, (Codes.Dereference) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Return) { translate(pc, (Codes.Return) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Nop) { @@ -1302,14 +1296,23 @@ private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.fieldType()))); } - private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); - JvmType.Function ftype = new JvmType.Function(type, type); + JvmType.Function ftype = null; // first, load operands switch (c.kind) { + case NEG: + case INVERT: + ftype = new JvmType.Function(type); + bytecodes.add(new Bytecode.Load(c.operand(0), type)); + break; + case DEREFERENCE: + ftype = new JvmType.Function(JAVA_LANG_OBJECT); + bytecodes.add(new Bytecode.Load(c.operand(0), type)); + break; case ADD: case SUB: case MUL: @@ -1318,11 +1321,13 @@ private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeS case BITWISEAND: case BITWISEOR: case BITWISEXOR: + ftype = new JvmType.Function(type, type); bytecodes.add(new Bytecode.Load(c.operand(0), type)); bytecodes.add(new Bytecode.Load(c.operand(1), type)); break; case LEFTSHIFT: case RIGHTSHIFT: + ftype = new JvmType.Function(type, WHILEYINT); bytecodes.add(new Bytecode.Load(c.operand(0), type)); bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); break; @@ -1330,6 +1335,18 @@ private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeS // second, apply operation switch (c.kind) { + case NEG: + bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "negate", ftype, Bytecode.InvokeMode.VIRTUAL)); + break; + case INVERT: + bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "compliment", ftype, Bytecode.InvokeMode.VIRTUAL)); + break; + case DEREFERENCE: + bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype, Bytecode.InvokeMode.VIRTUAL)); + // finally, we need to cast the object we got back appropriately. + Type.Reference pt = (Type.Reference) c.type(0); + addReadConversion(pt.element(), bytecodes); + break; case ADD: bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "add", ftype, Bytecode.InvokeMode.VIRTUAL)); @@ -1351,27 +1368,22 @@ private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeS "remainder", ftype, Bytecode.InvokeMode.VIRTUAL)); break; case BITWISEAND: - ftype = new JvmType.Function(type, type); bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "and", ftype, Bytecode.InvokeMode.VIRTUAL)); break; case BITWISEOR: - ftype = new JvmType.Function(type, type); bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "or", ftype, Bytecode.InvokeMode.VIRTUAL)); break; case BITWISEXOR: - ftype = new JvmType.Function(type, type); bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "xor", ftype, Bytecode.InvokeMode.VIRTUAL)); break; case LEFTSHIFT: - ftype = new JvmType.Function(type, WHILEYINT); bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "leftShift", ftype, Bytecode.InvokeMode.VIRTUAL)); break; case RIGHTSHIFT: - ftype = new JvmType.Function(type, WHILEYINT); bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "rightShift", ftype, Bytecode.InvokeMode.VIRTUAL)); break; @@ -1383,31 +1395,6 @@ private void translate(CodeForest.Index index, Codes.BinaryOperator c, int freeS bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeForest.Index index, Codes.Invert c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - JvmType type = convertUnderlyingType(c.type(0)); - bytecodes.add(new Bytecode.Load(c.operand(0), type)); - JvmType.Function ftype = new JvmType.Function(type); - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "compliment", ftype, Bytecode.InvokeMode.VIRTUAL)); - bytecodes.add(new Bytecode.Store(c.target(0), type)); - } - - private void translate(CodeForest.Index index, Codes.UnaryOperator c, int freeSlot, CodeForest forest, - ArrayList bytecodes) { - JvmType srcType = convertUnderlyingType(c.type(0)); - JvmType targetType = null; - String name = null; - switch (c.kind) { - case NEG: - targetType = srcType; - name = "negate"; - break; - } - JvmType.Function ftype = new JvmType.Function(targetType); - bytecodes.add(new Bytecode.Load(c.operand(0), srcType)); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) srcType, name, ftype, Bytecode.InvokeMode.VIRTUAL)); - bytecodes.add(new Bytecode.Store(c.target(0), targetType)); - } - private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c.type(0)); bytecodes.add(new Bytecode.New(WHILEYOBJECT)); @@ -1419,18 +1406,6 @@ private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeForest.Index index, Codes.Dereference c, int freeSlot, CodeForest forest, - ArrayList bytecodes) { - JvmType type = convertUnderlyingType(c.type(0)); - JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Load(c.operand(0), type)); - bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype, Bytecode.InvokeMode.VIRTUAL)); - // finally, we need to cast the object we got back appropriately. - Type.Reference pt = (Type.Reference) c.type(0); - addReadConversion(pt.element(), bytecodes); - bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.type(0).element()))); - } - protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(WHILEYARRAY)); bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); From 5a66fc6ea94c07df7c6a8465b3420579e28e1022 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 11:51:56 +1300 Subject: [PATCH 13/43] Removed LengthOf, IndexOf, ArrayGen bytecodes #502 These bytecodes have been folded into the Operator class. --- .../wyc/src/wyc/builder/CodeGenerator.java | 28 +-- .../src/wyil/builders/VcExprGenerator.java | 55 +++-- modules/wyil/src/wyil/builders/VcUtils.java | 8 +- modules/wyil/src/wyil/io/WyilFileReader.java | 10 +- modules/wyil/src/wyil/lang/Code.java | 29 ++- modules/wyil/src/wyil/lang/Codes.java | 226 ++---------------- modules/wyil/src/wyil/util/Interpreter.java | 125 +++------- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 65 +++-- 8 files changed, 156 insertions(+), 390 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index b4a679e6e6..7963eae883 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1916,10 +1916,10 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - int operand = generate(expr.src, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(Codes.LengthOf((Type.EffectiveArray) expr.srcType.raw(), target, operand), attributes(expr)); - return target; + int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.LENGTHOF), attributes(expr)); + return targets[0]; } private int generate(Expr.Dereference expr, Environment environment, CodeForest.Block block, CodeForest forest, @@ -1933,11 +1933,11 @@ private int generate(Expr.Dereference expr, Environment environment, CodeForest. private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - int srcOperand = generate(expr.src, environment, block, forest, context); - int idxOperand = generate(expr.index, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(Codes.IndexOf((Type.Array) expr.srcType.raw(), target, srcOperand, idxOperand), attributes(expr)); - return target; + int[] operands = { generate(expr.src, environment, block, forest, context), + generate(expr.index, environment, block, forest, context) }; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.INDEXOF), attributes(expr)); + return targets[0]; } private int generate(Expr.Cast expr, Environment environment, CodeForest.Block block, CodeForest forest, @@ -1992,11 +1992,11 @@ private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeFo private int generate(Expr.ArrayGenerator expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - int element = generate(expr.element, environment, block, forest, context); - int count = generate(expr.count, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(Codes.ArrayGenerator((Type.Array) expr.type.raw(), target, element, count), attributes(expr)); - return target; + int[] operands = new int[] { generate(expr.element, environment, block, forest, context), + generate(expr.count, environment, block, forest, context) }; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYGEN), attributes(expr)); + return targets[0]; } private int generate(Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest, diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index c1ada38d13..3b0cd38e04 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -50,13 +50,8 @@ public VcExprGenerator(String filename, Builder builder, VcUtils utils) { */ public void transform(Code code, CodeForest forest, VcBranch branch) { try { - if (code instanceof Codes.LengthOf) { - transformUnary(Expr.Unary.Op.LENGTHOF, (Codes.LengthOf) code, - branch, forest); - } else if (code instanceof Codes.Operator) { + if (code instanceof Codes.Operator) { transform((Codes.Operator) code, forest, branch); - } else if (code instanceof Codes.ArrayGenerator) { - transform((Codes.ArrayGenerator) code, forest, branch); } else if (code instanceof Codes.NewArray) { transformNary(Expr.Nary.Op.ARRAY, (Codes.NewArray) code, branch, forest); } else if (code instanceof Codes.NewRecord) { @@ -75,8 +70,6 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { transform((Codes.Invoke) code, forest, branch); } else if (code instanceof Codes.Label) { // skip - } else if (code instanceof Codes.IndexOf) { - transform((Codes.IndexOf) code, forest, branch); } else if (code instanceof Codes.Move) { transform((Codes.Move) code, forest, branch); } else if (code instanceof Codes.Assign) { @@ -109,6 +102,16 @@ protected void transform(Codes.Assign code, CodeForest forest, } } + /** + * Maps unary bytecodes into expression opcodes. + */ + private static Expr.Unary.Op[] unaryOperatorMap = { + Expr.Unary.Op.NEG, // neg + null, // invert + null, // deref + Expr.Unary.Op.LENGTHOF + }; + /** * Maps binary bytecodes into expression opcodes. */ @@ -116,6 +119,7 @@ protected void transform(Codes.Assign code, CodeForest forest, null, // neg null, // invert null, // deref + null, // lengthof Expr.Binary.Op.ADD, Expr.Binary.Op.SUB, Expr.Binary.Op.MUL, @@ -131,9 +135,10 @@ protected void transform(Codes.Assign code, CodeForest forest, protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch) { switch(code.kind) { - case NEG:{ + case NEG: + case LENGTHOF: { Codes.Operator bc = (Codes.Operator) code; - transformUnary(Expr.Unary.Op.NEG, bc, branch, forest); + transformUnary(unaryOperatorMap[code.kind.ordinal()], bc, branch, forest); break; } case INVERT: @@ -157,6 +162,16 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch branch.havoc(code.target(0)); break; } + case INDEXOF: { + Expr src = branch.read(code.operand(0)); + Expr idx = branch.read(code.operand(1)); + branch.write(code.target(0), + new Expr.IndexOf(src, idx, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + break; + } + case ARRAYGEN: + transformArrayGenerator(code,forest,branch); + break; } } @@ -245,25 +260,18 @@ protected void transform(Codes.Invoke code, CodeForest forest, } } - protected void transform(Codes.IndexOf code, CodeForest forest, VcBranch branch) { - Expr src = branch.read(code.operand(0)); - Expr idx = branch.read(code.operand(1)); - branch.write(code.target(0), - new Expr.IndexOf(src, idx, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); - } - - protected void transform(Codes.ArrayGenerator code, CodeForest forest, VcBranch branch) { + protected void transformArrayGenerator(Codes.Operator code, CodeForest forest, VcBranch branch) { + Type elementType = ((Type.Array) code.type(0)).element(); Collection wyilAttributes = forest.get(branch.pc()).attributes(); - Collection attributes = VcUtils.toWycsAttributes(wyilAttributes); + Collection attributes = VcUtils.toWycsAttributes(wyilAttributes); Expr element = branch.read(code.operand(0)); Expr count = branch.read(code.operand(1)); branch.havoc(code.target(0)); Expr arg = new Expr.Nary(Expr.Nary.Op.TUPLE, new Expr[] { branch.read(code.target(0)), element, count }, attributes); ArrayList generics = new ArrayList(); - generics.add(utils.convert(code.type(0).element(),wyilAttributes)); - Expr.Invoke macro = new Expr.Invoke("generate", Trie.fromString("wycs/core/Array"), - generics, arg); + generics.add(utils.convert(elementType, wyilAttributes)); + Expr.Invoke macro = new Expr.Invoke("generate", Trie.fromString("wycs/core/Array"), generics, arg); branch.assume(macro); } @@ -345,7 +353,8 @@ protected void updateHelper(Iterator iter, Expr oldSource, Expr newS protected void transformUnary(Expr.Unary.Op operator, Code.AbstractBytecode code, VcBranch branch, CodeForest forest) { Expr lhs = branch.read(code.operand(0)); - branch.write(code.target(0), new Expr.Unary(operator, lhs, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); + branch.write(code.target(0), + new Expr.Unary(operator, lhs, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); } /** diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java index 1af1a85903..a24c51bb3e 100644 --- a/modules/wyil/src/wyil/builders/VcUtils.java +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -370,9 +370,9 @@ public Pair[] getPreconditions(Code code, VcBranch branch, case Code.OPCODE_rem: return divideByZeroCheck((Codes.Operator) code, branch); case Code.OPCODE_indexof: - return indexOutOfBoundsChecks((Codes.IndexOf) code, branch); + return indexOutOfBoundsChecks((Codes.Operator) code, branch); case Code.OPCODE_arrygen: - return arrayGeneratorChecks((Codes.ArrayGenerator) code, branch); + return arrayGeneratorChecks((Codes.Operator) code, branch); case Code.OPCODE_update: return updateChecks((Codes.Update) code, branch); case Code.OPCODE_invoke: @@ -419,7 +419,7 @@ public Pair[] divideByZeroCheck(Codes.Operator binOp, VcBranch bra * --- The branch the bytecode is on. * @return */ - public Pair[] indexOutOfBoundsChecks(Codes.IndexOf code, VcBranch branch) { + public Pair[] indexOutOfBoundsChecks(Codes.Operator code, VcBranch branch) { if (code.type(0) instanceof Type.EffectiveArray) { Expr src = branch.read(code.operand(0)); Expr idx = branch.read(code.operand(1)); @@ -450,7 +450,7 @@ public Pair[] indexOutOfBoundsChecks(Codes.IndexOf code, VcBranch b * --- The branch the bytecode is on. * @return */ - public Pair[] arrayGeneratorChecks(Codes.ArrayGenerator code, VcBranch branch) { + public Pair[] arrayGeneratorChecks(Codes.Operator code, VcBranch branch) { Expr idx = branch.read(code.operand(1)); Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), idx.attributes()); diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index b267a0b8d9..ced0e7942c 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -943,9 +943,9 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.INVERT); } }; - schemas[Code.OPCODE_lengthof] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.LengthOf((Type.EffectiveArray) types[0], targets[0], operands[0]); + schemas[Code.OPCODE_lengthof] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { + public Code construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.LENGTHOF); } }; schemas[Code.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ @@ -1058,12 +1058,12 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob }; schemas[Code.OPCODE_indexof] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.IndexOf((Type.EffectiveArray)types[0],targets[0],operands[0],operands[1]); + return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.INDEXOF); } }; schemas[Code.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.ArrayGenerator((Type.Array) types[0], targets[0], operands[0], operands[1]); + return Codes.Operator(types[0], targets, operands,Codes.OperatorKind.ARRAYGEN); } }; diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index 050ce4c1fa..ce92eaa565 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -347,11 +347,9 @@ public boolean equals(Object o) { public static final int OPCODE_assign = UNARY_ASSIGNABLE+0; - public static final int OPCODE_lengthof = UNARY_ASSIGNABLE+3; public static final int OPCODE_move = UNARY_ASSIGNABLE+4; public static final int OPCODE_newobject = UNARY_ASSIGNABLE+5; - //public static final int OPCODE_not = UNARY_ASSIGNABLE+7; public static final int OPCODE_fieldload = UNARY_ASSIGNABLE+8; public static final int OPCODE_convert = UNARY_ASSIGNABLE+9; public static final int OPCODE_const = UNARY_ASSIGNABLE+10; @@ -376,23 +374,24 @@ public boolean equals(Object o) { public static final int OPCODE_neg = BINARY_ASSIGNABLE+0; public static final int OPCODE_invert = BINARY_ASSIGNABLE+1; public static final int OPCODE_dereference = BINARY_ASSIGNABLE+2; - public static final int OPCODE_add = BINARY_ASSIGNABLE+3; - public static final int OPCODE_sub = BINARY_ASSIGNABLE+4; - public static final int OPCODE_mul = BINARY_ASSIGNABLE+5; - public static final int OPCODE_div = BINARY_ASSIGNABLE+6; - public static final int OPCODE_rem = BINARY_ASSIGNABLE+7; - public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+8; - public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+9; - public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+10; - public static final int OPCODE_lshr = BINARY_ASSIGNABLE+11; - public static final int OPCODE_rshr = BINARY_ASSIGNABLE+12; - public static final int OPCODE_indexof = BINARY_ASSIGNABLE+13; - public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+14; + public static final int OPCODE_lengthof = BINARY_ASSIGNABLE+3; + public static final int OPCODE_add = BINARY_ASSIGNABLE+4; + public static final int OPCODE_sub = BINARY_ASSIGNABLE+5; + public static final int OPCODE_mul = BINARY_ASSIGNABLE+6; + public static final int OPCODE_div = BINARY_ASSIGNABLE+7; + public static final int OPCODE_rem = BINARY_ASSIGNABLE+8; + public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+9; + public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+10; + public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+11; + public static final int OPCODE_lshr = BINARY_ASSIGNABLE+12; + public static final int OPCODE_rshr = BINARY_ASSIGNABLE+13; + public static final int OPCODE_indexof = BINARY_ASSIGNABLE+14; + public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+15; // ========================================================================= // Nary Assignables // ========================================================================= - public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+15; + public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+16; public static final int OPCODE_newarray = NARY_ASSIGNABLE+0; public static final int OPCODE_newrecord = NARY_ASSIGNABLE+1; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index 2bdd93ce7b..c4579c7096 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -146,7 +146,6 @@ public static Invariant Invariant(int block) { return new Invariant(block); } - public static Lambda Lambda(Type.FunctionOrMethod fun, int target, Collection operands, NameID name) { return new Lambda(fun, target, CodeUtils.toIntArray(operands), name); @@ -157,40 +156,10 @@ public static Lambda Lambda(Type.FunctionOrMethod fun, int target, return new Lambda(fun, target, operands, name); } - public static LengthOf LengthOf(Type.EffectiveArray type, int target, - int operand) { - return new LengthOf(type, target, operand); - } - public static Move Move(Type type, int target, int operand) { return new Move(type, target, operand); } - - /** - * Construct a listgen bytecode which constructs a new list - * initialised to a given length, with each element containing a given item. - * - * @param type - * @return - */ - public static ArrayGenerator ArrayGenerator(Type.Array type, int target, - int element, int count) { - return new ArrayGenerator(type, target, element, count); - } - /** - * Construct a listload bytecode which reads a value from a - * given index in a given list. - * - * @param type - * --- list type. - * @return - */ - public static IndexOf IndexOf(Type.EffectiveArray type, int target, - int leftOperand, int rightOperand) { - return new IndexOf(type, target, leftOperand, rightOperand); - } - public static Loop Loop(int[] modifiedOperands, int block) { return new Loop(modifiedOperands,block); } @@ -354,56 +323,71 @@ public String toString() { return "deref"; } }, + LENGTHOF(3) { + public String toString() { + return "length"; + } + }, // Binary - ADD(3) { + ADD(4) { public String toString() { return "add"; } }, - SUB(4) { + SUB(5) { public String toString() { return "sub"; } }, - MUL(5) { + MUL(6) { public String toString() { return "mul"; } }, - DIV(6) { + DIV(7) { public String toString() { return "div"; } }, - REM(7) { + REM(8) { public String toString() { return "rem"; } }, - BITWISEOR(8) { + BITWISEOR(9) { public String toString() { return "or"; } }, - BITWISEXOR(9) { + BITWISEXOR(10) { public String toString() { return "xor"; } }, - BITWISEAND(10) { + BITWISEAND(11) { public String toString() { return "and"; } }, - LEFTSHIFT(11) { + LEFTSHIFT(12) { public String toString() { return "shl"; } }, - RIGHTSHIFT(12) { + RIGHTSHIFT(13) { public String toString() { return "shr"; } + }, + INDEXOF(14) { + public String toString() { + return "indexof"; + } + }, + ARRAYGEN(15) { + public String toString() { + return "arraygen"; + } }; public int offset; @@ -1498,166 +1482,7 @@ public void registers(Set register) { } } - /** - * Constructs a new array value from the values given by zero or more operand - * registers. The new list is then written into the target register. For - * example, the following Whiley code: - * - *
-	 * function f(int x, int y, int z) -> int[]:
-	 *     return [x,y,z]
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x, int y, int z) -> int[]:
-	 * body:
-	 *    assign %4 = %0             : int
-	 *    assign %5 = %1             : int
-	 *    assign %6 = %2             : int
-	 *    newlist %3 = (%4, %5, %6)  : int[]
-	 *    return %3                  : int[]
-	 * 
- * - * Writes the array value given by [x,y,z] into register - * %3 and returns it. - * - * @author David J. Pearce - * - */ - public static final class ArrayGenerator extends AbstractBytecode { - - private ArrayGenerator(Type.Array type, int target, int element, int count) { - super(type, target, element, count); - } - - public int opcode() { - return OPCODE_arrygen; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return ArrayGenerator(type(0), nTargets[0], nOperands[0],nOperands[1]); - } - - public boolean equals(Object o) { - if (o instanceof ArrayGenerator) { - return super.equals(operands()); - } - return false; - } - - public String toString() { - return "arraygen %" + target(0) + " = [" + operand(0) + "; " + operand(1) + "]" + " : " + type(0); - } - } - - /** - * Reads an (effective) collection (i.e. a set, list or map) from the - * operand register, and writes its length into the target register. For - * example, the following Whiley code: - * - *
-	 * function f(int[] ls) -> int:
-	 *     return |ls|
-	 * 
- * - * translates to the following WyIL code: - * - *
-	 * function f(int[] ls) -> int:
-	 * body:
-	 *     lengthof %0 = %0   : int[]
-	 *     return %0          : int
-	 * 
- * - * @author David J. Pearce - * - */ - public static final class LengthOf extends AbstractBytecode { - - private LengthOf(Type.EffectiveArray type, int target, int operand) { - super((Type) type, target, operand); - } - - public int opcode() { - return OPCODE_lengthof; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return LengthOf(type(0), nTargets[0], nOperands[0]); - } - - public boolean equals(Object o) { - if (o instanceof LengthOf) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "lengthof %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } - /** - * Reads an effective list or map from the source (left) operand register, - * and a key value from the key (right) operand register and returns the - * value associated with that key. If the key does not exist, then a fault - * is raised. For example, the following Whiley code: - * - *
-	 * function f({int=>string} map, int key) -> string:
-	 *     return map[key]
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f({int->string} map, int key) -> string:
-	 * body:
-	 *     assertky %1, %0 "invalid key"       : {int->string}
-	 *     indexof %2 = %0, %1                 : {int->string}
-	 *     return %2                          : string
-	 * 
- * - * Here, we see the assertky bytecode is used to first check - * that the given key exists in map, otherwise a fault is - * raised. - * - * @author David J. Pearce - * - */ - public static final class IndexOf extends AbstractBytecode { - - private IndexOf(Type.EffectiveArray type, int target, - int sourceOperand, int keyOperand) { - super((Type) type, target, sourceOperand, keyOperand); - } - - public int opcode() { - return OPCODE_indexof; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return IndexOf(type(0), nTargets[0], nOperands[0], nOperands[1]); - } - - public boolean equals(Object o) { - if (o instanceof IndexOf) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "indexof %" + target(0) + " = %" + operand(0) + ", %" + operand(1) + " : " + type(0); - } - } - /** * Moves the contents of a given operand register into a given target * register. This is similar to an assign bytecode, except that @@ -2162,7 +1987,6 @@ public String toString() { } } - /** * Constructs a new array value from the values given by zero or more operand * registers. The new list is then written into the target register. For diff --git a/modules/wyil/src/wyil/util/Interpreter.java b/modules/wyil/src/wyil/util/Interpreter.java index d33ba50023..59742b8364 100644 --- a/modules/wyil/src/wyil/util/Interpreter.java +++ b/modules/wyil/src/wyil/util/Interpreter.java @@ -179,8 +179,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.If) bytecode, frame, context); } else if (bytecode instanceof Codes.IfIs) { return execute((Codes.IfIs) bytecode, frame, context); - } else if (bytecode instanceof Codes.IndexOf) { - return execute((Codes.IndexOf) bytecode, frame, context); } else if (bytecode instanceof Codes.IndirectInvoke) { return execute((Codes.IndirectInvoke) bytecode, frame, context); } else if (bytecode instanceof Codes.Invoke) { @@ -190,10 +188,6 @@ private Object execute(Constant[] frame, Context context) { return context.pc.next(); } else if (bytecode instanceof Codes.Lambda) { return execute((Codes.Lambda) bytecode, frame, context); - } else if (bytecode instanceof Codes.LengthOf) { - return execute((Codes.LengthOf) bytecode, frame, context); - } else if (bytecode instanceof Codes.ArrayGenerator) { - return execute((Codes.ArrayGenerator) bytecode, frame, context); } else if (bytecode instanceof Codes.Quantify) { return execute((Codes.Quantify) bytecode, frame, context); } else if (bytecode instanceof Codes.Loop) { @@ -286,8 +280,9 @@ private Object execute(Codes.Operator bytecode, Constant[] frame, case NEG: case INVERT: case DEREFERENCE: + case LENGTHOF: result = executeUnary(bytecode.kind,frame[bytecode.operand(0)],context); - break; + break; case ADD: case SUB: case MUL: @@ -308,6 +303,29 @@ private Object execute(Codes.Operator bytecode, Constant[] frame, result = execute(bytecode.kind,lhs,rhs,context); break; } + case INDEXOF: { + Constant.Array src = checkType(frame[bytecode.operand(0)], context, Constant.Array.class); + Constant.Integer index = checkType(frame[bytecode.operand(1)], context, Constant.Integer.class); + int i = index.value.intValue(); + if (i < 0 || i >= src.values.size()) { + error("index-out-of-bounds", context); + } + // Ok, get the element at that index + result = src.values.get(index.value.intValue()); + break; + } + case ARRAYGEN: { + Constant element = frame[bytecode.operand(0)]; + Constant.Integer count = checkType(frame[bytecode.operand(1)], context, Constant.Integer.class); + // Check that we have a integer count + int n = count.value.intValue(); + ArrayList values = new ArrayList(); + for(int i=0;i!=n;++i) { + values.add(element); + } + result = Constant.V_ARRAY(values); + break; + } default: return deadCode(context); } @@ -334,6 +352,12 @@ private Constant executeUnary(Codes.OperatorKind kind, ConstantObject ref = (ConstantObject) operand; return ref.read(); } + case LENGTHOF: { + checkType(operand, context, Constant.Array.class); + Constant.Array list = (Constant.Array) operand; + BigInteger length = BigInteger.valueOf(list.values.size()); + return Constant.V_INTEGER(length); + } } return (Constant) deadCode(context); } @@ -834,40 +858,6 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { return false; // deadcode } - /** - * Execute an IndexOf bytecode instruction at a given point in the function - * or method body. This checks the first operand is a list value, and the - * second operand is an integer value. It also checks that the integer index - * is within bounds and, if not, raises a runtime fault. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.IndexOf bytecode, Constant[] frame, - Context context) { - Constant operand_0 = frame[bytecode.operand(0)]; - Constant operand_1 = frame[bytecode.operand(1)]; - // Check we have a list and an integer index - checkType(operand_0, context, Constant.Array.class); - checkType(operand_1, context, Constant.Integer.class); - // Yes, now check that this is in bounds - Constant.Array list = (Constant.Array) operand_0; - Constant.Integer index = (Constant.Integer) operand_1; - int i = index.value.intValue(); - if (i < 0 || i >= list.values.size()) { - error("index-out-of-bounds", context); - } - // Ok, get the element at that index - frame[bytecode.target(0)] = list.values.get(index.value.intValue()); - // Done - return context.pc.next(); - } - /** * Execute an IndirectInvoke bytecode instruction at a given point in the * function or method body. This first checks the operand is a function @@ -968,59 +958,6 @@ private Object execute(Codes.Lambda bytecode, Constant[] frame, return context.pc.next(); } - /** - * Execute a LengthOf bytecode instruction at a given point in the function - * or method body. This simply returns the length of the list at the given - * position. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.LengthOf bytecode, Constant[] frame, - Context context) { - Constant _source = frame[bytecode.operand(0)]; - checkType(_source, context, Constant.Array.class); - Constant.Array list = (Constant.Array) _source; - BigInteger length = BigInteger.valueOf(list.values.size()); - frame[bytecode.target(0)] = Constant.V_INTEGER(length); - return context.pc.next(); - } - - /** - * Execute the list generator bytecode instruction at a given point in the - * function or method body. This simply assigns the generated list to the - * target register. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.ArrayGenerator bytecode, Constant[] frame, - Context context) { - Constant element = frame[bytecode.operand(0)]; - Constant count = frame[bytecode.operand(1)]; - // Check that we have a integer count - checkType(count, context, Constant.Integer.class); - // Now, perform the append - Constant.Integer l2 = (Constant.Integer) count; - int n = l2.value.intValue(); - ArrayList values = new ArrayList(); - for(int i=0;i!=n;++i) { - values.add(element); - } - frame[bytecode.target(0)] = Constant.V_ARRAY(values); - return context.pc.next(); - } - private Object execute(Codes.Loop bytecode, Constant[] frame, Context context) { Object r; diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index d5bd2df285..c9c25601a6 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -611,14 +611,8 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.Invoke) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Label) { translate(pc, (Codes.Label) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.ArrayGenerator) { - translate(pc, (Codes.ArrayGenerator) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Lambda) { translate(pc, (Codes.Lambda) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.LengthOf) { - translate(pc, (Codes.LengthOf) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.IndexOf) { - translate(pc, (Codes.IndexOf) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Assign) { translate(pc, (Codes.Assign) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Loop) { @@ -1251,33 +1245,6 @@ private void translate(CodeForest.Index index, Codes.Move c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), jt)); } - private void translate(CodeForest.Index index, Codes.ArrayGenerator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - JvmType elementType = convertUnderlyingType(c.type(0).element()); - bytecodes.add(new Bytecode.Load(c.operand(0), elementType)); - addWriteConversion(c.type(0).element(), bytecodes); - bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); - JvmType.Function ftype = new JvmType.Function(WHILEYARRAY, JAVA_LANG_OBJECT, WHILEYINT); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "generate", ftype, Bytecode.InvokeMode.STATIC)); - bytecodes.add(new Bytecode.Store(c.target(0), WHILEYARRAY)); - } - - private void translate(CodeForest.Index index, Codes.LengthOf c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType((Type) c.type(0)))); - JvmType.Clazz ctype = JAVA_LANG_OBJECT; - JvmType.Function ftype = new JvmType.Function(WHILEYINT); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "length", ftype, Bytecode.InvokeMode.VIRTUAL)); - bytecodes.add(new Bytecode.Store(c.target(0), WHILEYINT)); - } - - private void translate(CodeForest.Index index, Codes.IndexOf c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); - bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); - JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "get", ftype, Bytecode.InvokeMode.STATIC)); - addReadConversion(c.type(0).element(), bytecodes); - bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.type(0).element()))); - } - private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); @@ -1306,13 +1273,14 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C switch (c.kind) { case NEG: case INVERT: + case LENGTHOF: ftype = new JvmType.Function(type); bytecodes.add(new Bytecode.Load(c.operand(0), type)); break; case DEREFERENCE: ftype = new JvmType.Function(JAVA_LANG_OBJECT); bytecodes.add(new Bytecode.Load(c.operand(0), type)); - break; + break; case ADD: case SUB: case MUL: @@ -1331,6 +1299,19 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C bytecodes.add(new Bytecode.Load(c.operand(0), type)); bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); break; + case INDEXOF: + ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); + bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); + bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); + break; + case ARRAYGEN: { + Type elementType = ((Type.Array) c.type(0)).element(); + ftype = new JvmType.Function(WHILEYARRAY, JAVA_LANG_OBJECT, WHILEYINT); + bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(elementType))); + addWriteConversion(elementType, bytecodes); + bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); + break; + } } // second, apply operation @@ -1347,6 +1328,10 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C Type.Reference pt = (Type.Reference) c.type(0); addReadConversion(pt.element(), bytecodes); break; + case LENGTHOF: + ftype = new JvmType.Function(WHILEYINT); + bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "length", ftype, Bytecode.InvokeMode.VIRTUAL)); + break; case ADD: bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "add", ftype, Bytecode.InvokeMode.VIRTUAL)); @@ -1387,6 +1372,18 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "rightShift", ftype, Bytecode.InvokeMode.VIRTUAL)); break; + case INDEXOF: { + Type.EffectiveArray arrType = (Type.EffectiveArray) c.type(0); + bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "get", ftype, Bytecode.InvokeMode.STATIC)); + addReadConversion(arrType.element(), bytecodes); + type = convertUnderlyingType(arrType.element()); + break; + } + case ARRAYGEN: { + bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "generate", ftype, Bytecode.InvokeMode.STATIC)); + break; + } + default: internalFailure("unknown binary expression encountered", filename, forest.get(index).attribute(SourceLocation.class)); From 605cc6a7b0e9cf73de43a4db5f8016efba0d117f Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 11:56:48 +1300 Subject: [PATCH 14/43] Remove move bytecode #502 This bytecode was not currently used, so has been removed. However, I will expect it to make a come back later on. --- .../src/wyil/builders/VcExprGenerator.java | 6 -- modules/wyil/src/wyil/io/WyilFileReader.java | 6 +- modules/wyil/src/wyil/lang/Code.java | 2 +- modules/wyil/src/wyil/lang/Codes.java | 64 ------------------- modules/wyil/src/wyil/util/Interpreter.java | 25 -------- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 9 --- 6 files changed, 2 insertions(+), 110 deletions(-) diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index 3b0cd38e04..bf1423eaaf 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -70,8 +70,6 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { transform((Codes.Invoke) code, forest, branch); } else if (code instanceof Codes.Label) { // skip - } else if (code instanceof Codes.Move) { - transform((Codes.Move) code, forest, branch); } else if (code instanceof Codes.Assign) { transform((Codes.Assign) code, forest, branch); } else if (code instanceof Codes.Update) { @@ -280,10 +278,6 @@ protected void transform(Codes.Lambda code, CodeForest forest, VcBranch branch) branch.havoc(code.target(0)); } - protected void transform(Codes.Move code, VcBranch branch) { - branch.write(code.target(0), branch.read(code.operand(0))); - } - protected void transform(Codes.NewObject code, CodeForest forest, VcBranch branch) { branch.havoc(code.target(0)); } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index ced0e7942c..d3d9815eef 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -923,11 +923,7 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob return Codes.Assign(types[0], targets[0], operands[0]); } }; - schemas[Code.OPCODE_move] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Move(types[0], targets[0], operands[0]); - } - }; + schemas[Code.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.NewObject((Type.Reference) types[0], targets[0], operands[0]); diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index ce92eaa565..0120f41fac 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -347,7 +347,7 @@ public boolean equals(Object o) { public static final int OPCODE_assign = UNARY_ASSIGNABLE+0; - public static final int OPCODE_move = UNARY_ASSIGNABLE+4; + //public static final int OPCODE_move = UNARY_ASSIGNABLE+4; public static final int OPCODE_newobject = UNARY_ASSIGNABLE+5; public static final int OPCODE_fieldload = UNARY_ASSIGNABLE+8; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index c4579c7096..6ba6258ab9 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -156,10 +156,6 @@ public static Lambda Lambda(Type.FunctionOrMethod fun, int target, return new Lambda(fun, target, operands, name); } - public static Move Move(Type type, int target, int operand) { - return new Move(type, target, operand); - } - public static Loop Loop(int[] modifiedOperands, int block) { return new Loop(modifiedOperands,block); } @@ -1482,66 +1478,6 @@ public void registers(Set register) { } } - - /** - * Moves the contents of a given operand register into a given target - * register. This is similar to an assign bytecode, except that - * the register's contents are voided afterwards. This guarantees - * that the register is no longer live, which is useful for determining the - * live ranges of registers in a function or method. For example, the - * following Whiley code: - * - *
-	 * function f(int x, int y) -> int:
-	 *     x = x + 1
-	 *     return x
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x, int y) -> int:
-	 * body:
-	 *     ifge %0, %1 goto blklab0  : int
-	 *     move %0 = %1              : int
-	 * .blklab0
-	 *     return %0                 : int
-	 * 
- * - * Here we see that when x < y the value of y - * (held in register %1) is moved into variable - * x (held in register %0). This is safe because - * register %1 is no longer live at that point. - * - * @author David J. Pearce - * - */ - public static final class Move extends AbstractBytecode { - - private Move(Type type, int target, int operand) { - super(type, target, operand); - } - - public int opcode() { - return OPCODE_move; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return Move(type(0), nTargets[0], nOperands[0]); - } - - public boolean equals(Object o) { - if (o instanceof Move) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "move %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } /** * Represents a block of code which loops continuously until e.g. a diff --git a/modules/wyil/src/wyil/util/Interpreter.java b/modules/wyil/src/wyil/util/Interpreter.java index 59742b8364..6ae18e9839 100644 --- a/modules/wyil/src/wyil/util/Interpreter.java +++ b/modules/wyil/src/wyil/util/Interpreter.java @@ -192,8 +192,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.Quantify) bytecode, frame, context); } else if (bytecode instanceof Codes.Loop) { return execute((Codes.Loop) bytecode, frame, context); - } else if (bytecode instanceof Codes.Move) { - return execute((Codes.Move) bytecode, frame, context); } else if (bytecode instanceof Codes.NewArray) { return execute((Codes.NewArray) bytecode, frame, context); } else if (bytecode instanceof Codes.NewObject) { @@ -972,29 +970,6 @@ private Object execute(Codes.Loop bytecode, Constant[] frame, return r; } - /** - * Execute a move bytecode instruction at a given point in the function or - * method body. This moves the operand value into the target register, and - * voids the operand. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.Move bytecode, Constant[] frame, - Context context) { - int[] operands = bytecode.operands(); - for(int i=0;i!=operands.length;++i) { - frame[bytecode.target(i)] = frame[bytecode.operand(i)]; - frame[bytecode.operand(i)] = null; - } - return context.pc.next(); - } - /** * Execute a Record bytecode instruction at a given point in the function or * method body. This constructs a new list. diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index c9c25601a6..f82340194a 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -617,8 +617,6 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.Assign) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Loop) { translate(pc, (Codes.Loop) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Move) { - translate(pc, (Codes.Move) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Update) { translate(pc, (Codes.Update) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.NewArray) { @@ -1238,13 +1236,6 @@ private void translate(CodeForest.Index index, Codes.Assign c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), jt)); } - private void translate(CodeForest.Index index, Codes.Move c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { - JvmType jt = convertUnderlyingType(c.type(0)); - bytecodes.add(new Bytecode.Load(c.operand(0), jt)); - bytecodes.add(new Bytecode.Store(c.target(0), jt)); - } - private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); From 9b1e4c4258fe4cc046d504ab4d53ea5928897662 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 13:05:45 +1300 Subject: [PATCH 15/43] Refactored Interpreter #502 Have pulled out the "operators" from the interpreter and split them into an array of InternalFunction instances. The idea behind this is to reduce the amount of code in the Interpreter class. --- .../wyc/src/wyc/builder/CodeGenerator.java | 11 +- modules/wyc/src/wyc/testing/TestUtils.java | 2 +- modules/wyil/src/wyil/Main.java | 2 +- .../src/wyil/builders/VcExprGenerator.java | 9 +- modules/wyil/src/wyil/io/WyilFileReader.java | 13 +- modules/wyil/src/wyil/lang/Code.java | 4 +- modules/wyil/src/wyil/lang/Codes.java | 87 +--- .../util/{ => interpreter}/Interpreter.java | 400 +++++------------- .../util/interpreter/StandardFunctions.java | 210 +++++++++ modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 20 +- 10 files changed, 353 insertions(+), 405 deletions(-) rename modules/wyil/src/wyil/util/{ => interpreter}/Interpreter.java (78%) create mode 100644 modules/wyil/src/wyil/util/interpreter/StandardFunctions.java diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 7963eae883..553fe44abc 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1918,7 +1918,7 @@ private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Blo Context context) { int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.LENGTHOF), attributes(expr)); + block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.ARRAYLENGTH), attributes(expr)); return targets[0]; } @@ -1985,9 +1985,10 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { int[] operands = generate(expr.arguments, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(Codes.NewArray((Type.Array) expr.type.raw(), target, operands), attributes(expr)); - return target; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYCONSTRUCTOR), + attributes(expr)); + return targets[0]; } private int generate(Expr.ArrayGenerator expr, Environment environment, CodeForest.Block block, CodeForest forest, @@ -1995,7 +1996,7 @@ private int generate(Expr.ArrayGenerator expr, Environment environment, CodeFore int[] operands = new int[] { generate(expr.element, environment, block, forest, context), generate(expr.count, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYGEN), attributes(expr)); + block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYGENERATOR), attributes(expr)); return targets[0]; } diff --git a/modules/wyc/src/wyc/testing/TestUtils.java b/modules/wyc/src/wyc/testing/TestUtils.java index 9d7e0f699f..75f8b79f75 100644 --- a/modules/wyc/src/wyc/testing/TestUtils.java +++ b/modules/wyc/src/wyc/testing/TestUtils.java @@ -23,7 +23,7 @@ import wyil.io.WyilFileReader; import wyil.lang.Type; import wyil.lang.WyilFile; -import wyil.util.Interpreter; +import wyil.util.interpreter.Interpreter; /** * Provides some simple helper functions used by all test harnesses. diff --git a/modules/wyil/src/wyil/Main.java b/modules/wyil/src/wyil/Main.java index c7c8eac3e1..7267717b52 100644 --- a/modules/wyil/src/wyil/Main.java +++ b/modules/wyil/src/wyil/Main.java @@ -18,7 +18,7 @@ import wyil.lang.Constant; import wyil.lang.Type; import wyil.lang.WyilFile; -import wyil.util.Interpreter; +import wyil.util.interpreter.Interpreter; public class Main { diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index bf1423eaaf..c4f8baeae6 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -52,8 +52,6 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { try { if (code instanceof Codes.Operator) { transform((Codes.Operator) code, forest, branch); - } else if (code instanceof Codes.NewArray) { - transformNary(Expr.Nary.Op.ARRAY, (Codes.NewArray) code, branch, forest); } else if (code instanceof Codes.NewRecord) { transformNary(Expr.Nary.Op.TUPLE, (Codes.NewRecord) code, branch, forest); } else if (code instanceof Codes.Convert) { @@ -134,7 +132,7 @@ protected void transform(Codes.Assign code, CodeForest forest, protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch) { switch(code.kind) { case NEG: - case LENGTHOF: { + case ARRAYLENGTH: { Codes.Operator bc = (Codes.Operator) code; transformUnary(unaryOperatorMap[code.kind.ordinal()], bc, branch, forest); break; @@ -167,9 +165,12 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch new Expr.IndexOf(src, idx, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); break; } - case ARRAYGEN: + case ARRAYGENERATOR: transformArrayGenerator(code,forest,branch); break; + case ARRAYCONSTRUCTOR: + transformNary(Expr.Nary.Op.ARRAY,code,branch,forest); + break; } } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index d3d9815eef..99a0324afd 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -941,7 +941,7 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob }; schemas[Code.OPCODE_lengthof] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { public Code construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.LENGTHOF); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYLENGTH); } }; schemas[Code.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ @@ -1059,18 +1059,19 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob }; schemas[Code.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands,Codes.OperatorKind.ARRAYGEN); + return Codes.Operator(types[0], targets, operands,Codes.OperatorKind.ARRAYGENERATOR); } }; - // ========================================================================= - // Nary Assignables - // ========================================================================= schemas[Code.OPCODE_newarray] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.NewArray((Type.Array) types[0], targets[0], operands); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYCONSTRUCTOR); } }; + + // ========================================================================= + // Nary Assignables + // ========================================================================= schemas[Code.OPCODE_newrecord] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.NewRecord((Type.Record) types[0], targets[0], operands); diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index 0120f41fac..ccbcea9eaa 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -387,13 +387,13 @@ public boolean equals(Object o) { public static final int OPCODE_rshr = BINARY_ASSIGNABLE+13; public static final int OPCODE_indexof = BINARY_ASSIGNABLE+14; public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+15; - + public static final int OPCODE_newarray = BINARY_ASSIGNABLE+16; + // ========================================================================= // Nary Assignables // ========================================================================= public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+16; - public static final int OPCODE_newarray = NARY_ASSIGNABLE+0; public static final int OPCODE_newrecord = NARY_ASSIGNABLE+1; public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; public static final int OPCODE_indirectinvoke = NARY_ASSIGNABLE+3; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index 6ba6258ab9..25303d5901 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -160,29 +160,6 @@ public static Loop Loop(int[] modifiedOperands, int block) { return new Loop(modifiedOperands,block); } - /** - * Construct a NewArray bytecode which constructs a new array and - * puts it on the stack. - * - * @param type - * @return - */ - public static NewArray NewArray(Type.Array type, int target, - Collection operands) { - return new NewArray(type, target, CodeUtils.toIntArray(operands)); - } - - /** - * Construct a NewArray bytecode which constructs a new array and - * puts it on the stack. - * - * @param type - * @return - */ - public static NewArray NewArray(Type.Array type, int target, int[] operands) { - return new NewArray(type, target, operands); - } - /** * Construct a newrecord bytecode which constructs a new record * and puts it on the stack. @@ -319,7 +296,7 @@ public String toString() { return "deref"; } }, - LENGTHOF(3) { + ARRAYLENGTH(3) { public String toString() { return "length"; } @@ -380,10 +357,15 @@ public String toString() { return "indexof"; } }, - ARRAYGEN(15) { + ARRAYGENERATOR(15) { public String toString() { return "arraygen"; } + }, + ARRAYCONSTRUCTOR(16) { + public String toString() { + return "array"; + } }; public int offset; @@ -1923,61 +1905,6 @@ public String toString() { } } - /** - * Constructs a new array value from the values given by zero or more operand - * registers. The new list is then written into the target register. For - * example, the following Whiley code: - * - *
-	 * function f(int x, int y, int z) -> int[]:
-	 *     return [x,y,z]
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x, int y, int z) -> int[]:
-	 * body:
-	 *    assign %4 = %0             : int
-	 *    assign %5 = %1             : int
-	 *    assign %6 = %2             : int
-	 *    newlist %3 = (%4, %5, %6)  : int[]
-	 *    return %3                  : int[]
-	 * 
- * - * Writes the array value given by [x,y,z] into register - * %3 and returns it. - * - * @author David J. Pearce - * - */ - public static final class NewArray extends AbstractBytecode { - - private NewArray(Type.Array type, int target, int[] operands) { - super(type, target, operands); - } - - public int opcode() { - return OPCODE_newarray; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return NewArray(type(0), nTargets[0], nOperands); - } - - public boolean equals(Object o) { - if (o instanceof NewArray) { - return super.equals(operands()); - } - return false; - } - - public String toString() { - return "newlist %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0); - } - } - /** * Represents a no-operation bytecode which, as the name suggests, does * nothing. diff --git a/modules/wyil/src/wyil/util/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java similarity index 78% rename from modules/wyil/src/wyil/util/Interpreter.java rename to modules/wyil/src/wyil/util/interpreter/Interpreter.java index 6ae18e9839..a5eac737c7 100644 --- a/modules/wyil/src/wyil/util/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -1,4 +1,4 @@ -package wyil.util; +package wyil.util.interpreter; import java.io.IOException; import java.io.PrintStream; @@ -12,6 +12,7 @@ import wycc.util.ResolveError; import wyfs.lang.Path; import wyil.lang.*; +import wyil.util.TypeExpander; /** *

@@ -39,6 +40,11 @@ public class Interpreter { * constraints. */ private final TypeExpander expander; + + /** + * Implementations for the internal operators + */ + private final InternalFunction[] operators; /** * The debug stream provides an I/O stream through which debug bytecodes can @@ -50,6 +56,7 @@ public Interpreter(Build.Project project, PrintStream debug) { this.project = project; this.debug = debug; this.expander = new TypeExpander(project); + this.operators = StandardFunctions.standardFunctions; } /** @@ -66,33 +73,26 @@ public Interpreter(Build.Project project, PrintStream debug) { * The supplied arguments * @return */ - public Constant[] execute(NameID nid, Type.FunctionOrMethod sig, - Constant... args) { + public Constant[] execute(NameID nid, Type.FunctionOrMethod sig, Constant... args) { // First, find the enclosing WyilFile try { - Path.Entry entry = project.get(nid.module(), - WyilFile.ContentType); + Path.Entry entry = project.get(nid.module(), WyilFile.ContentType); if (entry == null) { - throw new IllegalArgumentException("no WyIL file found: " - + nid.module()); + throw new IllegalArgumentException("no WyIL file found: " + nid.module()); } // Second, find the given function or method WyilFile wyilFile = entry.read(); - WyilFile.FunctionOrMethod fm = wyilFile.functionOrMethod( - nid.name(), sig); + WyilFile.FunctionOrMethod fm = wyilFile.functionOrMethod(nid.name(), sig); if (fm == null) { - throw new IllegalArgumentException( - "no function or method found: " + nid + ", " + sig); + throw new IllegalArgumentException("no function or method found: " + nid + ", " + sig); } else if (sig.params().size() != args.length) { - throw new IllegalArgumentException( - "incorrect number of arguments: " + nid + ", " + sig); + throw new IllegalArgumentException("incorrect number of arguments: " + nid + ", " + sig); } // Third, get and check the function or method body CodeForest code = fm.code(); if (fm.body() == null) { // FIXME: add support for native functions or methods - throw new IllegalArgumentException( - "no function or method body found: " + nid + ", " + sig); + throw new IllegalArgumentException("no function or method body found: " + nid + ", " + sig); } // Fourth, construct the stack frame for execution ArrayList sig_params = sig.params(); @@ -121,9 +121,9 @@ private Object executeAllWithin(Constant[] frame, Context context) { CodeForest forest = context.forest; CodeForest.Index pc = context.pc; int block = pc.block(); - CodeForest.Block codes = forest.get(pc.block()); - - while(pc.block() == block && pc.offset() < codes.size()) { + CodeForest.Block codes = forest.get(pc.block()); + + while (pc.block() == block && pc.offset() < codes.size()) { Object r = execute(frame, new Context(pc, context.forest)); // Now, see whether we are continuing or not if (r instanceof CodeForest.Index) { @@ -131,7 +131,7 @@ private Object executeAllWithin(Constant[] frame, Context context) { } else { return r; } - } + } if (pc.block() != block) { // non-local exit return pc; @@ -152,7 +152,7 @@ private Object executeAllWithin(Constant[] frame, Context context) { * --- Context in which bytecodes are executed * @return */ - private Object execute(Constant[] frame, Context context) { + private Object execute(Constant[] frame, Context context) { Code bytecode = context.forest.get(context.pc).code(); // FIXME: turn this into a switch statement? if (bytecode instanceof Codes.Invariant) { @@ -192,8 +192,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.Quantify) bytecode, frame, context); } else if (bytecode instanceof Codes.Loop) { return execute((Codes.Loop) bytecode, frame, context); - } else if (bytecode instanceof Codes.NewArray) { - return execute((Codes.NewArray) bytecode, frame, context); } else if (bytecode instanceof Codes.NewObject) { return execute((Codes.NewObject) bytecode, frame, context); } else if (bytecode instanceof Codes.NewRecord) { @@ -207,8 +205,7 @@ private Object execute(Constant[] frame, Context context) { } else if (bytecode instanceof Codes.Update) { return execute((Codes.Update) bytecode, frame, context); } else { - throw new IllegalArgumentException("Unknown bytecode encountered: " - + bytecode); + throw new IllegalArgumentException("Unknown bytecode encountered: " + bytecode); } } @@ -226,7 +223,7 @@ private Object execute(Constant[] frame, Context context) { private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, Context context) { // CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); - Object r = executeAllWithin(frame, new Context(pc,context.forest)); + Object r = executeAllWithin(frame, new Context(pc, context.forest)); // if (r == null) { // Body of assert fell through to next @@ -248,12 +245,11 @@ private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, Context * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Assign bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Assign bytecode, Constant[] frame, Context context) { int[] targets = bytecode.targets(); for (int i = 0; i != targets.length; ++i) { frame[bytecode.target(i)] = frame[bytecode.operand(i)]; - } + } return context.pc.next(); } @@ -269,172 +265,21 @@ private Object execute(Codes.Assign bytecode, Constant[] frame, * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Operator bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Operator bytecode, Constant[] frame, Context context) { + int[] operands = bytecode.operands(); + Constant[] values = new Constant[operands.length]; + // Read all operands + for(int i=0;i!=operands.length;++i) { + values[i] = frame[operands[i]]; + } // Compute result - Constant result; - // - switch(bytecode.kind) { - case NEG: - case INVERT: - case DEREFERENCE: - case LENGTHOF: - result = executeUnary(bytecode.kind,frame[bytecode.operand(0)],context); - break; - case ADD: - case SUB: - case MUL: - case DIV: - case REM: { - Constant.Integer lhs = checkType(frame[bytecode.operand(0)], context, Constant.Integer.class); - Constant.Integer rhs = checkType(frame[bytecode.operand(1)], context, Constant.Integer.class); - result = execute(bytecode.kind,lhs,rhs,context); - break; - } - case BITWISEXOR: - case BITWISEOR: - case BITWISEAND: - case LEFTSHIFT: - case RIGHTSHIFT: { - Constant.Byte lhs = checkType(frame[bytecode.operand(0)], context, Constant.Byte.class); - Constant rhs = frame[bytecode.operand(1)]; - result = execute(bytecode.kind,lhs,rhs,context); - break; - } - case INDEXOF: { - Constant.Array src = checkType(frame[bytecode.operand(0)], context, Constant.Array.class); - Constant.Integer index = checkType(frame[bytecode.operand(1)], context, Constant.Integer.class); - int i = index.value.intValue(); - if (i < 0 || i >= src.values.size()) { - error("index-out-of-bounds", context); - } - // Ok, get the element at that index - result = src.values.get(index.value.intValue()); - break; - } - case ARRAYGEN: { - Constant element = frame[bytecode.operand(0)]; - Constant.Integer count = checkType(frame[bytecode.operand(1)], context, Constant.Integer.class); - // Check that we have a integer count - int n = count.value.intValue(); - ArrayList values = new ArrayList(); - for(int i=0;i!=n;++i) { - values.add(element); - } - result = Constant.V_ARRAY(values); - break; - } - default: - return deadCode(context); - } - + Constant result = operators[bytecode.opcode()].apply(values, context); // Write result to target frame[bytecode.target(0)] = result; - + // Continue on to next instruction return context.pc.next(); } - private Constant executeUnary(Codes.OperatorKind kind, - Constant operand, Context context) { - switch(kind) { - case NEG: { - Constant.Integer i = checkType(operand, context, Constant.Integer.class); - return i.negate(); - } - case INVERT: { - Constant.Byte b = checkType(operand, context, Constant.Byte.class); - return Constant.V_BYTE((byte) ~b.value); - } - case DEREFERENCE: { - checkType(operand, context, ConstantObject.class); - ConstantObject ref = (ConstantObject) operand; - return ref.read(); - } - case LENGTHOF: { - checkType(operand, context, Constant.Array.class); - Constant.Array list = (Constant.Array) operand; - BigInteger length = BigInteger.valueOf(list.values.size()); - return Constant.V_INTEGER(length); - } - } - return (Constant) deadCode(context); - } - - /** - * Execute an integer binary operator - * - * @param kind - * --- operator kind - * @param i1 - * --- left operand - * @param i2 - * --- right operand - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Constant execute(Codes.OperatorKind kind, - Constant.Integer i1, Constant.Integer i2, Context context) { - switch (kind) { - case ADD: - return i1.add(i2); - case SUB: - return i1.subtract(i2); - case MUL: - return i1.multiply(i2); - case DIV: - return i1.divide(i2); - case REM: - return i1.remainder(i2); - } - deadCode(context); - return null; - } - - /** - * Execute an bitwise binary operator - * - * @param kind - * --- operator kind - * @param i1 - * --- left operand - * @param i2 - * --- right operand - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Constant execute(Codes.OperatorKind kind, Constant.Byte i1, - Constant i2, Context context) { - int result; - switch (kind) { - case BITWISEAND: - checkType(i2, context, Constant.Byte.class); - result = i1.value & ((Constant.Byte) i2).value; - break; - case BITWISEOR: - checkType(i2, context, Constant.Byte.class); - result = i1.value | ((Constant.Byte) i2).value; - break; - case BITWISEXOR: - checkType(i2, context, Constant.Byte.class); - result = i1.value ^ ((Constant.Byte) i2).value; - break; - case LEFTSHIFT: - checkType(i2, context, Constant.Integer.class); - result = i1.value << ((Constant.Integer) i2).value.intValue(); - break; - case RIGHTSHIFT: - checkType(i2, context, Constant.Integer.class); - result = i1.value >> ((Constant.Integer) i2).value.intValue(); - break; - default: - deadCode(context); - return null; - } - return Constant.V_BYTE((byte) result); - } - /** * Execute a Const bytecode instruction at a given point in the function or * method body @@ -447,15 +292,13 @@ private Constant execute(Codes.OperatorKind kind, Constant.Byte i1, * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Const bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Const bytecode, Constant[] frame, Context context) { Constant c = cleanse(bytecode.constant, context); frame[bytecode.target()] = c; return context.pc.next(); } - private Object execute(Codes.Convert bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Convert bytecode, Constant[] frame, Context context) { try { Constant operand = frame[bytecode.operand(0)]; Type target = expander.getUnderlyingType(bytecode.result()); @@ -485,7 +328,8 @@ private Constant convert(Constant value, Type to, Context context) { // In this case, we don't need to do anything because the value is // already of the correct type. return value; - } if (to instanceof Type.Record) { + } + if (to instanceof Type.Record) { return convert(value, (Type.Record) to, context); } else if (to instanceof Type.Array) { return convert(value, (Type.Array) to, context); @@ -515,14 +359,12 @@ private Constant convert(Constant value, Type.Record to, Context context) { HashSet rv_fields = new HashSet(rv.values.keySet()); // Check fields in value are subset of those in target type if (!rv_fields.containsAll(to.keys())) { - error("cannot convert between records with differing fields", - context); + error("cannot convert between records with differing fields", context); return null; // deadcode } else { HashMap nValues = new HashMap(); for (String field : to.keys()) { - Constant nValue = convert(rv.values.get(field), - to.field(field), context); + Constant nValue = convert(rv.values.get(field), to.field(field), context); nValues.put(field, nValue); } return Constant.V_RECORD(nValues); @@ -548,7 +390,7 @@ private Constant convert(Constant value, Type.Array to, Context context) { } return Constant.V_ARRAY(values); } - + /** * Convert a value into a union type. In this case, we must find an * appropriate bound for the type in question. If no such type can be found, @@ -584,7 +426,7 @@ private Constant convert(Constant value, Type.Union to, Context context) { private Constant convert(Constant value, Type.FunctionOrMethod to, Context context) { return value; } - + /** * Execute a Debug bytecode instruction at a given point in the function or * method body. This will write the provided string out to the debug stream. @@ -597,8 +439,7 @@ private Constant convert(Constant value, Type.FunctionOrMethod to, Context conte * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Debug bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Debug bytecode, Constant[] frame, Context context) { // Constant.Array list = (Constant.Array) frame[bytecode.operand(0)]; for (Constant item : list.values) { @@ -622,20 +463,17 @@ private Object execute(Codes.Debug bytecode, Constant[] frame, * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Fail bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Fail bytecode, Constant[] frame, Context context) { throw new Error("Runtime fault occurred"); } - private Object execute(Codes.FieldLoad bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.FieldLoad bytecode, Constant[] frame, Context context) { Constant.Record rec = (Constant.Record) frame[bytecode.operand(0)]; frame[bytecode.target(0)] = rec.values.get(bytecode.field); return context.pc.next(); } - private Object execute(Codes.Quantify bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Quantify bytecode, Constant[] frame, Context context) { Constant startOperand = frame[bytecode.startOperand()]; Constant endOperand = frame[bytecode.endOperand()]; checkType(startOperand, context, Constant.Integer.class); @@ -644,12 +482,12 @@ private Object execute(Codes.Quantify bytecode, Constant[] frame, Constant.Integer eo = (Constant.Integer) endOperand; int start = so.value.intValue(); int end = eo.value.intValue(); - for (int i = start; i < end; ++i) { + for (int i = start; i < end; ++i) { // Assign the index variable frame[bytecode.indexOperand()] = Constant.V_INTEGER(BigInteger.valueOf(i)); // Execute loop body for one iteration CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); - Object r = executeAllWithin(frame, new Context(pc,context.forest)); + Object r = executeAllWithin(frame, new Context(pc, context.forest)); // Now, check whether we fell through to the end or not. If not, // then we must have exited somehow so return to signal that. if (r != null) { @@ -659,9 +497,8 @@ private Object execute(Codes.Quantify bytecode, Constant[] frame, return context.pc.next(); } - - private Object execute(Codes.Goto bytecode, Constant[] frame, - Context context) { + + private Object execute(Codes.Goto bytecode, Constant[] frame, Context context) { return context.getLabel(bytecode.destination()); } @@ -704,18 +541,17 @@ private Object execute(Codes.If bytecode, Constant[] frame, Context context) { } private boolean elementOf(Constant lhs, Constant rhs, Context context) { - checkType(rhs, context,Constant.Array.class); + checkType(rhs, context, Constant.Array.class); Constant.Array list = (Constant.Array) rhs; return list.values.contains(lhs); } - private boolean lessThan(Constant lhs, Constant rhs, boolean isStrict, - Context context) { + private boolean lessThan(Constant lhs, Constant rhs, boolean isStrict, Context context) { checkType(lhs, context, Constant.Integer.class); checkType(rhs, context, Constant.Integer.class); Constant.Integer lhs_i = (Constant.Integer) lhs; Constant.Integer rhs_i = (Constant.Integer) rhs; - int result = lhs_i.compareTo(rhs_i); + int result = lhs_i.compareTo(rhs_i); // In the strict case, the lhs must be strictly below the rhs. In the // non-strict case, they can be equal. if (isStrict) { @@ -783,15 +619,13 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { Type.Record rt = (Type.Record) type; Constant.Record t = (Constant.Record) value; Set fields = t.values.keySet(); - if (!fields.containsAll(rt.keys()) - || (!rt.keys().containsAll(fields) && !rt.isOpen())) { + if (!fields.containsAll(rt.keys()) || (!rt.keys().containsAll(fields) && !rt.isOpen())) { // In this case, the set of fields does not match properly return false; } boolean r = true; for (String field : fields) { - r &= isMemberOfType(t.values.get(field), rt.field(field), - context); + r &= isMemberOfType(t.values.get(field), rt.field(field), context); } return r; } @@ -807,23 +641,21 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { } else if (type instanceof Type.Negation) { Type.Negation t = (Type.Negation) type; return !isMemberOfType(value, t.element(), context); - } else if(type instanceof Type.FunctionOrMethod) { - if(value instanceof Constant.Lambda) { + } else if (type instanceof Type.FunctionOrMethod) { + if (value instanceof Constant.Lambda) { Constant.Lambda l = (Constant.Lambda) value; - if(Type.isSubtype(type, l.type)) { + if (Type.isSubtype(type, l.type)) { return true; } - } + } return false; - }else if (type instanceof Type.Nominal) { + } else if (type instanceof Type.Nominal) { Type.Nominal nt = (Type.Nominal) type; NameID nid = nt.name(); try { - Path.Entry entry = project.get(nid.module(), - WyilFile.ContentType); + Path.Entry entry = project.get(nid.module(), WyilFile.ContentType); if (entry == null) { - throw new IllegalArgumentException("no WyIL file found: " - + nid.module()); + throw new IllegalArgumentException("no WyIL file found: " + nid.module()); } // Second, find the given function or method WyilFile wyilFile = entry.read(); @@ -851,7 +683,7 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { return false; } } - + deadCode(context); return false; // deadcode } @@ -871,8 +703,7 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, Context context) { Constant operand = frame[bytecode.operand(0)]; // Check that we have a function reference checkType(operand, context, Constant.Lambda.class); @@ -882,14 +713,14 @@ private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, Constant.Lambda func = (Constant.Lambda) operand; List func_arguments = func.arguments; int[] operands = bytecode.operands(); - Constant[] arguments = new Constant[func_arguments.size() + (operands.length-1)]; + Constant[] arguments = new Constant[func_arguments.size() + (operands.length - 1)]; { - int i=0; + int i = 0; for (int j = 1; j != operands.length; ++j) { - arguments[i++] = frame[operands[j]]; + arguments[i++] = frame[operands[j]]; } for (int j = 0; j != func_arguments.size(); ++j) { - arguments[i++] = func.arguments.get(j); + arguments[i++] = func.arguments.get(j); } } // Make the actual call @@ -897,16 +728,15 @@ private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, // Check whether a return value was expected or not int[] targets = bytecode.targets(); List returns = bytecode.type(0).returns(); - for(int i=0;i!=targets.length;++i) { + for (int i = 0; i != targets.length; ++i) { // Coerce the result (may not be actually necessary)) - frame[targets[i]] = convert(results[i],returns.get(i),context); + frame[targets[i]] = convert(results[i], returns.get(i), context); } // Done return context.pc.next(); } - private Object execute(Codes.Invariant bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Invariant bytecode, Constant[] frame, Context context) { // FIXME: currently implemented as a NOP because of #480 return context.pc.next(); } @@ -925,8 +755,7 @@ private Object execute(Codes.Invariant bytecode, Constant[] frame, * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Invoke bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Invoke bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] arguments = new Constant[operands.length]; for (int i = 0; i != arguments.length; ++i) { @@ -934,18 +763,17 @@ private Object execute(Codes.Invoke bytecode, Constant[] frame, } Constant[] results = execute(bytecode.name, bytecode.type(0), arguments); int[] targets = bytecode.targets(); - for(int i=0;i!=targets.length;++i) { + for (int i = 0; i != targets.length; ++i) { frame[targets[i]] = results[i]; - } + } return context.pc.next(); } - private Object execute(Codes.Lambda bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Lambda bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] arguments = new Constant[operands.length]; - + for (int i = 0; i != arguments.length; ++i) { int reg = operands[i]; arguments[i] = frame[reg]; @@ -956,13 +784,12 @@ private Object execute(Codes.Lambda bytecode, Constant[] frame, return context.pc.next(); } - private Object execute(Codes.Loop bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Loop bytecode, Constant[] frame, Context context) { Object r; do { // Keep executing the loop body until we exit it somehow. CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); - r = executeAllWithin(frame, new Context(pc,context.forest)); + r = executeAllWithin(frame, new Context(pc, context.forest)); } while (r == null); // If we get here, then we have exited the loop body without falling @@ -970,30 +797,7 @@ private Object execute(Codes.Loop bytecode, Constant[] frame, return r; } - /** - * Execute a Record bytecode instruction at a given point in the function or - * method body. This constructs a new list. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.NewArray bytecode, Constant[] frame, - Context context) { - ArrayList values = new ArrayList(); - for (int operand : bytecode.operands()) { - values.add((Constant) frame[operand]); - } - frame[bytecode.target(0)] = Constant.V_ARRAY(values); - return context.pc.next(); - } - - private Object execute(Codes.NewObject bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.NewObject bytecode, Constant[] frame, Context context) { Constant operand = frame[bytecode.operand(0)]; ConstantObject o = new ConstantObject(operand); frame[bytecode.target(0)] = o; @@ -1012,8 +816,7 @@ private Object execute(Codes.NewObject bytecode, Constant[] frame, * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.NewRecord bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.NewRecord bytecode, Constant[] frame, Context context) { HashMap values = new HashMap(); ArrayList fields = new ArrayList(bytecode.type(0).fields().keySet()); Collections.sort(fields); @@ -1056,7 +859,7 @@ private Object execute(Codes.Nop bytecode, Constant[] frame, Context context) { private Object execute(Codes.Return bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] returns = new Constant[operands.length]; - for(int i=0;i!=operands.length;++i) { + for (int i = 0; i != operands.length; ++i) { returns[i] = frame[operands[i]]; } return returns; @@ -1074,8 +877,7 @@ private Object execute(Codes.Switch bytecode, Constant[] frame, Context context) return context.getLabel(bytecode.defaultTarget); } - private Object execute(Codes.Update bytecode, Constant[] frame, - Context context) { + private Object execute(Codes.Update bytecode, Constant[] frame, Context context) { Constant rhs = frame[bytecode.result()]; Constant lhs = frame[bytecode.target(0)]; frame[bytecode.target(0)] = update(lhs, bytecode.iterator(), rhs, frame, context); @@ -1103,8 +905,8 @@ private Object execute(Codes.Update bytecode, Constant[] frame, * * @return The left-hand side updated with the new value assigned */ - private Constant update(Constant lhs, Iterator descriptor, - Constant rhs, Constant[] frame, Context context) { + private Constant update(Constant lhs, Iterator descriptor, Constant rhs, Constant[] frame, + Context context) { if (descriptor.hasNext()) { Codes.LVal lval = descriptor.next(); // Check what shape the left-hand side is @@ -1116,8 +918,7 @@ private Constant update(Constant lhs, Iterator descriptor, checkType(lhs, context, Constant.Array.class); Constant.Array list = (Constant.Array) lhs; int index = ((Constant.Integer) operand).value.intValue(); - ArrayList values = new ArrayList( - list.values); + ArrayList values = new ArrayList(list.values); rhs = update(values.get(index), descriptor, rhs, frame, context); values.set(index, rhs); return Constant.V_ARRAY(values); @@ -1126,10 +927,8 @@ private Constant update(Constant lhs, Iterator descriptor, Codes.RecordLVal lv = (Codes.RecordLVal) lval; checkType(lhs, context, Constant.Record.class); Constant.Record record = (Constant.Record) lhs; - HashMap values = new HashMap( - record.values); - rhs = update(values.get(lv.field), descriptor, rhs, frame, - context); + HashMap values = new HashMap(record.values); + rhs = update(values.get(lv.field), descriptor, rhs, frame, context); values.put(lv.field, rhs); return Constant.V_RECORD(values); } else { @@ -1158,7 +957,7 @@ private Constant update(Constant lhs, Iterator descriptor, * @param types * --- Types to be checked against */ - private T checkType(Constant operand, Context context, Class... types) { + public static T checkType(Constant operand, Context context, Class... types) { // Got through each type in turn checking for a match for (int i = 0; i != types.length; ++i) { if (types[i].isInstance(operand)) { @@ -1181,11 +980,8 @@ private T checkType(Constant operand, Context context, Clas private Constant cleanse(Constant constant, Context context) { // See #494 for more on why this method exists, and whether or not we // can get rid of it. - if (constant instanceof Constant.Null - || constant instanceof Constant.Byte - || constant instanceof Constant.Bool - || constant instanceof Constant.Integer - || constant instanceof Constant.Lambda + if (constant instanceof Constant.Null || constant instanceof Constant.Byte || constant instanceof Constant.Bool + || constant instanceof Constant.Integer || constant instanceof Constant.Lambda || constant instanceof Constant.Type) { return constant; } else if (constant instanceof Constant.Array) { @@ -1219,7 +1015,7 @@ private Constant cleanse(Constant constant, Context context) { * @param context * --- Context in which bytecodes are executed */ - private Object error(String msg, Context context) { + public static Object error(String msg, Context context) { // FIXME: do more here throw new RuntimeException(msg); } @@ -1268,7 +1064,7 @@ public CodeForest.Index getLabel(String label) { * @author David J. Pearce * */ - private class ConstantObject extends Constant { + public static class ConstantObject extends Constant { private Constant value; public ConstantObject(Constant value) { @@ -1295,8 +1091,7 @@ public int hashCode() { public int compareTo(Constant o) { // This method cannot be implmened because it does not make sense to // compare a reference with another reference. - throw new UnsupportedOperationException( - "ConstantObject.compare() cannot be implemented"); + throw new UnsupportedOperationException("ConstantObject.compare() cannot be implemented"); } @Override @@ -1305,4 +1100,15 @@ public wyil.lang.Type type() { } } + + /** + * An internal function is simply a named internal function. This reads a + * bunch of operands and returns a set of results. + * + * @author David J. Pearce + * + */ + public static interface InternalFunction { + public Constant apply(Constant[] operands, Context context); + } } diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java new file mode 100644 index 0000000000..3a73a85770 --- /dev/null +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -0,0 +1,210 @@ +package wyil.util.interpreter; + +import wyil.lang.Code; +import wyil.lang.Constant; +import wyil.util.interpreter.Interpreter.ConstantObject; +import wyil.util.interpreter.Interpreter.Context; +import wyil.util.interpreter.Interpreter.InternalFunction; +import static wyil.util.interpreter.Interpreter.*; + +import java.math.BigInteger; +import java.util.ArrayList; + +public class StandardFunctions { + /** + * The standard functions for use with the interpreter. + */ + public static final InternalFunction[] standardFunctions = new InternalFunction[255]; + + static { + standardFunctions[Code.OPCODE_neg] = new Negate(); + standardFunctions[Code.OPCODE_invert] = new Invert(); + standardFunctions[Code.OPCODE_dereference] = new Dereference(); + standardFunctions[Code.OPCODE_lengthof] = new ArrayLength(); + standardFunctions[Code.OPCODE_add ] = new Add(); + standardFunctions[Code.OPCODE_sub ] = new Subtract(); + standardFunctions[Code.OPCODE_mul ] = new Multiply(); + standardFunctions[Code.OPCODE_div ] = new Divide(); + standardFunctions[Code.OPCODE_rem ] = new Remainder(); + standardFunctions[Code.OPCODE_bitwiseor] = new BitwiseOr(); + standardFunctions[Code.OPCODE_bitwisexor] = new BitwiseXor(); + standardFunctions[Code.OPCODE_bitwiseand] = new BitwiseAnd(); + standardFunctions[Code.OPCODE_lshr] = new LeftShift(); + standardFunctions[Code.OPCODE_rshr] = new RightShift(); + standardFunctions[Code.OPCODE_indexof] = new ArrayIndex(); + standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); + standardFunctions[Code.OPCODE_newarray] = new ArrayConstructor(); + }; + + private static final class Invert implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte b = checkType(operands[0], context, Constant.Byte.class); + return Constant.V_BYTE((byte) ~b.value); + } + } + + private static final class Dereference implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + ConstantObject ref = checkType(operands[0], context, ConstantObject.class); + return ref.read(); + } + + } + + // ==================================================================================== + // Arithmetic + // ==================================================================================== + + private static final class Negate implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Integer i = checkType(operands[0], context, Constant.Integer.class); + return i.negate(); + } + } + + private static final class Add implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + return lhs.add(rhs); + } + + } + private static final class Subtract implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + return lhs.subtract(rhs); + } + + } + private static final class Multiply implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + return lhs.multiply(rhs); + } + } + private static final class Divide implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + return lhs.divide(rhs); + } + + } + private static final class Remainder implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + return lhs.remainder(rhs); + } + } + + // ==================================================================================== + // Bytes + // ==================================================================================== + + private static final class BitwiseOr implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); + Constant.Byte rhs = checkType(operands[1], context, Constant.Byte.class); + int result = lhs.value | rhs.value; + return Constant.V_BYTE((byte) result); + } + } + private static final class BitwiseXor implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); + Constant.Byte rhs = checkType(operands[1], context, Constant.Byte.class); + int result = lhs.value ^ rhs.value; + return Constant.V_BYTE((byte) result); + } + } + private static final class BitwiseAnd implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); + Constant.Byte rhs = checkType(operands[1], context, Constant.Byte.class); + int result = lhs.value & rhs.value; + return Constant.V_BYTE((byte) result); + } + } + private static final class LeftShift implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + int result = lhs.value << rhs.value.intValue(); + return Constant.V_BYTE((byte) result); + } + } + private static final class RightShift implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); + Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); + int result = lhs.value >> rhs.value.intValue(); + return Constant.V_BYTE((byte) result); + } + } + + // ==================================================================================== + // Arrays + // ==================================================================================== + private static final class ArrayLength implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Array array = checkType(operands[0], context, Constant.Array.class); + BigInteger length = BigInteger.valueOf(array.values.size()); + return Constant.V_INTEGER(length); + } + } + private static final class ArrayIndex implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Array src = checkType(operands[0], context, Constant.Array.class); + Constant.Integer index = checkType(operands[1], context, Constant.Integer.class); + int i = index.value.intValue(); + if (i < 0 || i >= src.values.size()) { + error("index-out-of-bounds", context); + } + // Ok, get the element at that index + return src.values.get(index.value.intValue()); + } + } + private static final class ArrayGenerator implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant element = operands[0]; + Constant.Integer count = checkType(operands[1], context, Constant.Integer.class); + // Check that we have a integer count + int n = count.value.intValue(); + ArrayList values = new ArrayList(); + for (int i = 0; i != n; ++i) { + values.add(element); + } + return Constant.V_ARRAY(values); + } + } + private static final class ArrayConstructor implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + ArrayList values = new ArrayList(); + for (Constant operand : operands) { + values.add(operand); + } + return Constant.V_ARRAY(values); + } + } +} diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index f82340194a..15db5bd581 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -619,8 +619,6 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.Loop) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Update) { translate(pc, (Codes.Update) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.NewArray) { - translate(pc, (Codes.NewArray) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.NewRecord) { translate(pc, (Codes.NewRecord) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Return) { @@ -1264,7 +1262,7 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C switch (c.kind) { case NEG: case INVERT: - case LENGTHOF: + case ARRAYLENGTH: ftype = new JvmType.Function(type); bytecodes.add(new Bytecode.Load(c.operand(0), type)); break; @@ -1295,7 +1293,7 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); break; - case ARRAYGEN: { + case ARRAYGENERATOR: { Type elementType = ((Type.Array) c.type(0)).element(); ftype = new JvmType.Function(WHILEYARRAY, JAVA_LANG_OBJECT, WHILEYINT); bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(elementType))); @@ -1303,6 +1301,9 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); break; } + case ARRAYCONSTRUCTOR: + translateArrayConstructor(index,c,freeSlot,forest,bytecodes); + return; } // second, apply operation @@ -1319,7 +1320,7 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C Type.Reference pt = (Type.Reference) c.type(0); addReadConversion(pt.element(), bytecodes); break; - case LENGTHOF: + case ARRAYLENGTH: ftype = new JvmType.Function(WHILEYINT); bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "length", ftype, Bytecode.InvokeMode.VIRTUAL)); break; @@ -1370,7 +1371,7 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C type = convertUnderlyingType(arrType.element()); break; } - case ARRAYGEN: { + case ARRAYGENERATOR: { bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "generate", ftype, Bytecode.InvokeMode.STATIC)); break; } @@ -1394,7 +1395,8 @@ private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), type)); } - protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + protected void translateArrayConstructor(CodeForest.Index index, Codes.Operator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + Type.Array arrType = (Type.Array) c.type(0); bytecodes.add(new Bytecode.New(WHILEYARRAY)); bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); bytecodes.add(new Bytecode.LoadConst(c.operands().length)); @@ -1403,8 +1405,8 @@ protected void translate(CodeForest.Index index, Codes.NewArray c, int freeSlot, ftype = new JvmType.Function(WHILEYARRAY, WHILEYARRAY, JAVA_LANG_OBJECT); for (int i = 0; i != c.operands().length; ++i) { - bytecodes.add(new Bytecode.Load(c.operands()[i], convertUnderlyingType(c.type(0).element()))); - addWriteConversion(c.type(0).element(), bytecodes); + bytecodes.add(new Bytecode.Load(c.operands()[i], convertUnderlyingType(arrType.element()))); + addWriteConversion(arrType.element(), bytecodes); bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "internal_add", ftype, Bytecode.InvokeMode.STATIC)); } From 96f3959d1ec924c8061e326a09faf418f9cac17e Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 15:57:16 +1300 Subject: [PATCH 16/43] Refactor Jvm backend #502 This refactors the JVM code generator to split out the code responsible for simple bytecodes. This exploits the uniformity that is beginning to be seen in the WyIL bytecode format. --- .../util/interpreter/StandardFunctions.java | 23 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 250 ++++++----------- .../src/wyjc/util/BytecodeTranslators.java | 260 ++++++++++++++++++ 3 files changed, 352 insertions(+), 181 deletions(-) create mode 100644 modules/wyjc/src/wyjc/util/BytecodeTranslators.java diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index 3a73a85770..e20f35216b 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -18,7 +18,7 @@ public class StandardFunctions { static { standardFunctions[Code.OPCODE_neg] = new Negate(); - standardFunctions[Code.OPCODE_invert] = new Invert(); + standardFunctions[Code.OPCODE_invert] = new BitwiseInvert(); standardFunctions[Code.OPCODE_dereference] = new Dereference(); standardFunctions[Code.OPCODE_lengthof] = new ArrayLength(); standardFunctions[Code.OPCODE_add ] = new Add(); @@ -36,14 +36,10 @@ public class StandardFunctions { standardFunctions[Code.OPCODE_newarray] = new ArrayConstructor(); }; - private static final class Invert implements InternalFunction { - @Override - public Constant apply(Constant[] operands, Context context) { - Constant.Byte b = checkType(operands[0], context, Constant.Byte.class); - return Constant.V_BYTE((byte) ~b.value); - } - } - + // ==================================================================================== + // References + // ==================================================================================== + private static final class Dereference implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { @@ -113,6 +109,15 @@ public Constant apply(Constant[] operands, Context context) { // Bytes // ==================================================================================== + + private static final class BitwiseInvert implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Byte b = checkType(operands[0], context, Constant.Byte.class); + return Constant.V_BYTE((byte) ~b.value); + } + } + private static final class BitwiseOr implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 15db5bd581..2a0323d623 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -44,6 +44,8 @@ import wyil.lang.Constant; import wyil.util.TypeExpander; import static wyil.util.ErrorMessages.internalFailure; + +import wyjc.util.BytecodeTranslators; import wyjc.util.WyjcBuildTask; import jasm.attributes.Code.Handler; import jasm.attributes.LineNumberTable; @@ -89,31 +91,37 @@ public class Wyil2JavaBuilder implements Builder { /** * Filename of module being translated */ - protected String filename; + private String filename; /** * Type of enclosing class being generated */ - protected JvmType.Clazz owner; + private JvmType.Clazz owner; + /** + * The set of generators for individual WyIL bytecodes + */ + private BytecodeTranslator[] generators; + /** * Map of Constant values to their pool index */ - protected HashMap constants; + private HashMap constants; /** * List of temporary classes created to implement lambda expressions */ - protected ArrayList lambdas; + private ArrayList lambdas; /** * List of line number entries for current function / method being compiled. */ - protected ArrayList lineNumbers; + private ArrayList lineNumbers; public Wyil2JavaBuilder(Build.Project project) { this.project = project; this.expander = new TypeExpander(project); + this.generators = BytecodeTranslators.standardFunctions; } public void setLogger(Logger logger) { @@ -1254,134 +1262,8 @@ private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - - JvmType type = convertUnderlyingType(c.type(0)); - JvmType.Function ftype = null; - - // first, load operands - switch (c.kind) { - case NEG: - case INVERT: - case ARRAYLENGTH: - ftype = new JvmType.Function(type); - bytecodes.add(new Bytecode.Load(c.operand(0), type)); - break; - case DEREFERENCE: - ftype = new JvmType.Function(JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Load(c.operand(0), type)); - break; - case ADD: - case SUB: - case MUL: - case DIV: - case REM: - case BITWISEAND: - case BITWISEOR: - case BITWISEXOR: - ftype = new JvmType.Function(type, type); - bytecodes.add(new Bytecode.Load(c.operand(0), type)); - bytecodes.add(new Bytecode.Load(c.operand(1), type)); - break; - case LEFTSHIFT: - case RIGHTSHIFT: - ftype = new JvmType.Function(type, WHILEYINT); - bytecodes.add(new Bytecode.Load(c.operand(0), type)); - bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); - break; - case INDEXOF: - ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); - bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); - bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); - break; - case ARRAYGENERATOR: { - Type elementType = ((Type.Array) c.type(0)).element(); - ftype = new JvmType.Function(WHILEYARRAY, JAVA_LANG_OBJECT, WHILEYINT); - bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(elementType))); - addWriteConversion(elementType, bytecodes); - bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT)); - break; - } - case ARRAYCONSTRUCTOR: - translateArrayConstructor(index,c,freeSlot,forest,bytecodes); - return; - } - - // second, apply operation - switch (c.kind) { - case NEG: - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "negate", ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case INVERT: - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "compliment", ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case DEREFERENCE: - bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype, Bytecode.InvokeMode.VIRTUAL)); - // finally, we need to cast the object we got back appropriately. - Type.Reference pt = (Type.Reference) c.type(0); - addReadConversion(pt.element(), bytecodes); - break; - case ARRAYLENGTH: - ftype = new JvmType.Function(WHILEYINT); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "length", ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case ADD: - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "add", - ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case SUB: - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "subtract", - ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case MUL: - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "multiply", - ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case DIV: - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "divide", - ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case REM: - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "remainder", ftype, Bytecode.InvokeMode.VIRTUAL)); - break; - case BITWISEAND: - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "and", ftype, - Bytecode.InvokeMode.VIRTUAL)); - break; - case BITWISEOR: - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "or", ftype, - Bytecode.InvokeMode.VIRTUAL)); - break; - case BITWISEXOR: - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "xor", ftype, - Bytecode.InvokeMode.VIRTUAL)); - break; - case LEFTSHIFT: - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "leftShift", ftype, - Bytecode.InvokeMode.VIRTUAL)); - break; - case RIGHTSHIFT: - bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "rightShift", ftype, - Bytecode.InvokeMode.VIRTUAL)); - break; - case INDEXOF: { - Type.EffectiveArray arrType = (Type.EffectiveArray) c.type(0); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "get", ftype, Bytecode.InvokeMode.STATIC)); - addReadConversion(arrType.element(), bytecodes); - type = convertUnderlyingType(arrType.element()); - break; - } - case ARRAYGENERATOR: { - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "generate", ftype, Bytecode.InvokeMode.STATIC)); - break; - } - - default: - internalFailure("unknown binary expression encountered", filename, - forest.get(index).attribute(SourceLocation.class)); - } - - bytecodes.add(new Bytecode.Store(c.target(0), type)); + Context context = new Context(forest, index, freeSlot, bytecodes); + generators[c.opcode()].translate(c, context); } private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, CodeForest forest, ArrayList bytecodes) { @@ -1395,24 +1277,6 @@ private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), type)); } - protected void translateArrayConstructor(CodeForest.Index index, Codes.Operator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - Type.Array arrType = (Type.Array) c.type(0); - bytecodes.add(new Bytecode.New(WHILEYARRAY)); - bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); - bytecodes.add(new Bytecode.LoadConst(c.operands().length)); - JvmType.Function ftype = new JvmType.Function(T_VOID, T_INT); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "", ftype, Bytecode.InvokeMode.SPECIAL)); - - ftype = new JvmType.Function(WHILEYARRAY, WHILEYARRAY, JAVA_LANG_OBJECT); - for (int i = 0; i != c.operands().length; ++i) { - bytecodes.add(new Bytecode.Load(c.operands()[i], convertUnderlyingType(arrType.element()))); - addWriteConversion(arrType.element(), bytecodes); - bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "internal_add", ftype, Bytecode.InvokeMode.STATIC)); - } - - bytecodes.add(new Bytecode.Store(c.target(0), WHILEYARRAY)); - } - private void translate(CodeForest.Index index, Codes.NewRecord code, int freeSlot, CodeForest forest, ArrayList bytecodes) { construct(WHILEYRECORD, freeSlot, bytecodes); @@ -2190,26 +2054,26 @@ private void construct(JvmType.Clazz owner, int freeSlot, Bytecode.InvokeMode.SPECIAL)); } - private final static Type WHILEY_SYSTEM_T = Type.Nominal(new NameID(Trie + public final static Type WHILEY_SYSTEM_T = Type.Nominal(new NameID(Trie .fromString("whiley/lang/System"), "Console")); - private final static JvmType.Clazz WHILEYUTIL = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYUTIL = new JvmType.Clazz( "wyjc.runtime", "Util"); - private final static JvmType.Clazz WHILEYARRAY = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYARRAY = new JvmType.Clazz( "wyjc.runtime", "WyArray"); - private final static JvmType.Clazz WHILEYTYPE = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYTYPE = new JvmType.Clazz( "wyjc.runtime", "WyType"); - private final static JvmType.Clazz WHILEYRECORD = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYRECORD = new JvmType.Clazz( "wyjc.runtime", "WyRecord"); - private final static JvmType.Clazz WHILEYOBJECT = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYOBJECT = new JvmType.Clazz( "wyjc.runtime", "WyObject"); - private final static JvmType.Clazz WHILEYBOOL = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYBOOL = new JvmType.Clazz( "wyjc.runtime", "WyBool"); - private final static JvmType.Clazz WHILEYBYTE = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYBYTE = new JvmType.Clazz( "wyjc.runtime", "WyByte"); - private final static JvmType.Clazz WHILEYINT = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYINT = new JvmType.Clazz( "java.math", "BigInteger"); - private final static JvmType.Clazz WHILEYLAMBDA = new JvmType.Clazz( + public final static JvmType.Clazz WHILEYLAMBDA = new JvmType.Clazz( "wyjc.runtime", "WyLambda"); private static final JvmType.Clazz JAVA_LANG_CHARACTER = new JvmType.Clazz( @@ -2440,16 +2304,58 @@ public UnresolvedHandler(String start, String end, String target, } } - /* - * public static void testMangle1(Type.Fun ft) throws IOException { - * IdentifierOutputStream jout = new IdentifierOutputStream(); - * BinaryOutputStream binout = new BinaryOutputStream(jout); - * Types.BinaryWriter tm = new Types.BinaryWriter(binout); - * Type.build(tm,ft); binout.close(); System.out.println("MANGLED: " + ft + - * " => " + jout.toString()); Type.Fun type = (Type.Fun) new - * Types.BinaryReader( new BinaryInputStream(new IdentifierInputStream( - * jout.toString()))).read(); System.out.println("UNMANGLED TO: " + type); - * if(!type.equals(ft)) { throw new - * RuntimeException("INVALID TYPE RECONSTRUCTED"); } } + public final class Context { + /** + * The code forest in which we are currently operating + */ + private final CodeForest forest; + + /** + * The index of the bytecode being translated + */ + private final CodeForest.Index pc; + + /** + * The list of bytecodes that have been generated so far + */ + private final ArrayList bytecodes; + + /** + * The next available free register slot + */ + private final int freeSlot; + + public Context(CodeForest forest, CodeForest.Index pc, int freeSlot, ArrayList bytecodes) { + this.forest = forest; + this.bytecodes = bytecodes; + this.pc = pc; + this.freeSlot = freeSlot; + } + + public void add(Bytecode bytecode) { + bytecodes.add(bytecode); + } + + public void addReadConversion(Type type) { + Wyil2JavaBuilder.this.addReadConversion(type,bytecodes); + } + + public void addWriteConversion(Type type) { + Wyil2JavaBuilder.this.addWriteConversion(type,bytecodes); + } + + public JvmType toJvmType(Type type) { + return convertUnderlyingType(type); + } + } + + /** + * Provides a simple interface for translating individual bytecodes. + * + * @author David J. Pearce + * */ + public interface BytecodeTranslator { + void translate(Codes.Operator bytecode, Context context); + } } diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java new file mode 100644 index 0000000000..35330bcf11 --- /dev/null +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -0,0 +1,260 @@ +package wyjc.util; + +import static jasm.lang.JvmTypes.*; +import static wyjc.Wyil2JavaBuilder.*; +import jasm.lang.Bytecode; +import jasm.lang.JvmType; +import wyil.lang.Code; +import wyil.lang.Codes; +import wyil.lang.Type; +import wyjc.Wyil2JavaBuilder.BytecodeTranslator; +import wyjc.Wyil2JavaBuilder.Context; + +/** + * Provides implementations for the standard internal functions + * + * @author David J. Pearce + * + */ +public class BytecodeTranslators { + /** + * The standard functions for use with the interpreter. + */ + public static final BytecodeTranslator[] standardFunctions = new BytecodeTranslator[255]; + + static { + standardFunctions[Code.OPCODE_neg] = new Negate(); + standardFunctions[Code.OPCODE_invert] = new Invert(); + standardFunctions[Code.OPCODE_dereference] = new Dereference(); + standardFunctions[Code.OPCODE_lengthof] = new ArrayLength(); + standardFunctions[Code.OPCODE_add ] = new Add(); + standardFunctions[Code.OPCODE_sub ] = new Subtract(); + standardFunctions[Code.OPCODE_mul ] = new Multiply(); + standardFunctions[Code.OPCODE_div ] = new Divide(); + standardFunctions[Code.OPCODE_rem ] = new Remainder(); + standardFunctions[Code.OPCODE_bitwiseor] = new BitwiseOr(); + standardFunctions[Code.OPCODE_bitwisexor] = new BitwiseXor(); + standardFunctions[Code.OPCODE_bitwiseand] = new BitwiseAnd(); + standardFunctions[Code.OPCODE_lshr] = new LeftShift(); + standardFunctions[Code.OPCODE_rshr] = new RightShift(); + standardFunctions[Code.OPCODE_indexof] = new ArrayIndex(); + standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); + standardFunctions[Code.OPCODE_newarray] = new ArrayConstructor(); + }; + + // ==================================================================================== + // References + // ==================================================================================== + + private static final class Dereference implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype, Bytecode.InvokeMode.VIRTUAL)); + // finally, we need to cast the object we got back appropriately. + Type.Reference pt = (Type.Reference) bytecode.type(0); + context.addReadConversion(pt.element()); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + // ==================================================================================== + // Arithmetic + // ==================================================================================== + + private static final class Negate implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Invoke(type, "negate", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class Add implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke((JvmType.Clazz) type, "add", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class Subtract implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke((JvmType.Clazz) type, "subtract", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class Multiply implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke((JvmType.Clazz) type, "multiply", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class Divide implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke((JvmType.Clazz) type, "divide", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class Remainder implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke((JvmType.Clazz) type, "remainder", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + + // ==================================================================================== + // Bytes + // ==================================================================================== + private static final class Invert implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Invoke(WHILEYBYTE, "compliment", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class BitwiseOr implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke(WHILEYBYTE, "or", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class BitwiseXor implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke(WHILEYBYTE, "xor", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class BitwiseAnd implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + context.add(new Bytecode.Invoke(WHILEYBYTE, "and", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class LeftShift implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, WHILEYINT); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), WHILEYINT)); + context.add(new Bytecode.Invoke(WHILEYBYTE, "leftShift", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class RightShift implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type, WHILEYINT); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), WHILEYINT)); + context.add(new Bytecode.Invoke(WHILEYBYTE, "rightShift", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + // ==================================================================================== + // Arrays + // ==================================================================================== + private static final class ArrayLength implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(WHILEYINT); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Invoke(WHILEYARRAY, "length", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class ArrayIndex implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + Type.EffectiveArray arrType = (Type.EffectiveArray) bytecode.type(0); + JvmType elementType = context.toJvmType(arrType.element()); + JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); + context.add(new Bytecode.Load(bytecode.operand(0), WHILEYARRAY)); + context.add(new Bytecode.Load(bytecode.operand(1), WHILEYINT)); + context.add(new Bytecode.Invoke(WHILEYARRAY, "get", ftype, Bytecode.InvokeMode.STATIC)); + context.addReadConversion(arrType.element()); + context.add(new Bytecode.Store(bytecode.target(0), elementType)); + } + } + private static final class ArrayGenerator implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + Type elementType = ((Type.Array) bytecode.type(0)).element(); + JvmType.Function ftype = new JvmType.Function(WHILEYARRAY, JAVA_LANG_OBJECT, WHILEYINT); + context.add(new Bytecode.Load(bytecode.operand(0), context.toJvmType(elementType))); + context.addWriteConversion(elementType); + context.add(new Bytecode.Load(bytecode.operand(1), WHILEYINT)); + context.add(new Bytecode.Invoke(WHILEYARRAY, "generate", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), WHILEYARRAY)); + } + } + private static final class ArrayConstructor implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + Type.Array arrType = (Type.Array) bytecode.type(0); + JvmType elementType = context.toJvmType(arrType.element()); + JvmType.Function initJvmType = new JvmType.Function(T_VOID, T_INT); + JvmType.Function ftype = new JvmType.Function(WHILEYARRAY, WHILEYARRAY, JAVA_LANG_OBJECT); + + context.add(new Bytecode.New(WHILEYARRAY)); + context.add(new Bytecode.Dup(WHILEYARRAY)); + context.add(new Bytecode.LoadConst(bytecode.operands().length)); + context.add(new Bytecode.Invoke(WHILEYARRAY, "", initJvmType, Bytecode.InvokeMode.SPECIAL)); + + for (int i = 0; i != bytecode.operands().length; ++i) { + context.add(new Bytecode.Load(bytecode.operands()[i], elementType)); + context.addWriteConversion(arrType.element()); + context.add(new Bytecode.Invoke(WHILEYARRAY, "internal_add", ftype, Bytecode.InvokeMode.STATIC)); + } + + context.add(new Bytecode.Store(bytecode.target(0), WHILEYARRAY)); + } + } +} From c502e4322575910cc635bd91df35c9a346d6b944 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 16:40:42 +1300 Subject: [PATCH 17/43] Remove Codes.NewRecord #502 This remoces the bytecode Codes.NewRecord and turns it into an "Operator". This means it can be manipulated in a more uniform fashion. --- .../wyc/src/wyc/builder/CodeGenerator.java | 11 +-- .../src/wyil/builders/VcExprGenerator.java | 9 ++- modules/wyil/src/wyil/builders/VcUtils.java | 2 +- modules/wyil/src/wyil/io/WyilFileReader.java | 16 ++-- modules/wyil/src/wyil/lang/Code.java | 14 ++-- modules/wyil/src/wyil/lang/Codes.java | 81 ++----------------- .../wyil/util/interpreter/Interpreter.java | 30 +------ .../util/interpreter/StandardFunctions.java | 31 ++++++- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 35 ++------ .../src/wyjc/util/BytecodeTranslators.java | 40 ++++++++- 10 files changed, 107 insertions(+), 162 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 553fe44abc..431ce2d7c2 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1893,7 +1893,7 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b attributes(expr)); break; case INVERT: - block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.INVERT), + block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.BITWISEINVERT), attributes(expr)); break; case NOT: @@ -1936,7 +1936,7 @@ private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Bloc int[] operands = { generate(expr.src, environment, block, forest, context), generate(expr.index, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.INDEXOF), attributes(expr)); + block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.ARRAYINDEX), attributes(expr)); return targets[0]; } @@ -2024,9 +2024,10 @@ private int generate(Expr.Record expr, Environment environment, CodeForest.Block Expr arg = expr.fields.get(key); operands[i] = generate(arg, environment, block, forest, context); } - int target = environment.allocate(expr.result().raw()); - block.add(Codes.NewRecord((Type.Record) expr.result().raw(), target, operands), attributes(expr)); - return target; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.RECORDCONSTRUCTOR), + attributes(expr)); + return targets[0]; } private int generate(Expr.FieldAccess expr, Environment environment, CodeForest.Block block, CodeForest forest, diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index c4f8baeae6..6a0b2aee3e 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -52,8 +52,6 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { try { if (code instanceof Codes.Operator) { transform((Codes.Operator) code, forest, branch); - } else if (code instanceof Codes.NewRecord) { - transformNary(Expr.Nary.Op.TUPLE, (Codes.NewRecord) code, branch, forest); } else if (code instanceof Codes.Convert) { transform((Codes.Convert) code, forest, branch); } else if (code instanceof Codes.Const) { @@ -137,7 +135,7 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch transformUnary(unaryOperatorMap[code.kind.ordinal()], bc, branch, forest); break; } - case INVERT: + case BITWISEINVERT: case DEREFERENCE: { branch.havoc(code.target(0)); break; @@ -158,7 +156,7 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch branch.havoc(code.target(0)); break; } - case INDEXOF: { + case ARRAYINDEX: { Expr src = branch.read(code.operand(0)); Expr idx = branch.read(code.operand(1)); branch.write(code.target(0), @@ -171,6 +169,9 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch case ARRAYCONSTRUCTOR: transformNary(Expr.Nary.Op.ARRAY,code,branch,forest); break; + case RECORDCONSTRUCTOR: + transformNary(Expr.Nary.Op.TUPLE,code,branch,forest); + break; } } diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java index a24c51bb3e..74a6ee9585 100644 --- a/modules/wyil/src/wyil/builders/VcUtils.java +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -369,7 +369,7 @@ public Pair[] getPreconditions(Code code, VcBranch branch, case Code.OPCODE_div: case Code.OPCODE_rem: return divideByZeroCheck((Codes.Operator) code, branch); - case Code.OPCODE_indexof: + case Code.OPCODE_arrayindex: return indexOutOfBoundsChecks((Codes.Operator) code, branch); case Code.OPCODE_arrygen: return arrayGeneratorChecks((Codes.Operator) code, branch); diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 99a0324afd..292a21eae1 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -934,12 +934,12 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DEREFERENCE); } }; - schemas[Code.OPCODE_invert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + schemas[Code.OPCODE_arrayinvert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.INVERT); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEINVERT); } }; - schemas[Code.OPCODE_lengthof] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { + schemas[Code.OPCODE_arraylength] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { public Code construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYLENGTH); } @@ -1052,9 +1052,9 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RIGHTSHIFT); } }; - schemas[Code.OPCODE_indexof] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Code.OPCODE_arrayindex] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.INDEXOF); + return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.ARRAYINDEX); } }; schemas[Code.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ @@ -1063,7 +1063,7 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob } }; - schemas[Code.OPCODE_newarray] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + schemas[Code.OPCODE_array] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYCONSTRUCTOR); } @@ -1072,9 +1072,9 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Nary Assignables // ========================================================================= - schemas[Code.OPCODE_newrecord] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + schemas[Code.OPCODE_record] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.NewRecord((Type.Record) types[0], targets[0], operands); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RECORDCONSTRUCTOR); } }; schemas[Code.OPCODE_invoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index ccbcea9eaa..beb6e9e1fc 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -372,9 +372,9 @@ public boolean equals(Object o) { public static final int BINARY_ASSIGNABLE = BINARY_OPERATOR+6; public static final int OPCODE_neg = BINARY_ASSIGNABLE+0; - public static final int OPCODE_invert = BINARY_ASSIGNABLE+1; + public static final int OPCODE_arrayinvert = BINARY_ASSIGNABLE+1; public static final int OPCODE_dereference = BINARY_ASSIGNABLE+2; - public static final int OPCODE_lengthof = BINARY_ASSIGNABLE+3; + public static final int OPCODE_arraylength = BINARY_ASSIGNABLE+3; public static final int OPCODE_add = BINARY_ASSIGNABLE+4; public static final int OPCODE_sub = BINARY_ASSIGNABLE+5; public static final int OPCODE_mul = BINARY_ASSIGNABLE+6; @@ -385,16 +385,16 @@ public boolean equals(Object o) { public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+11; public static final int OPCODE_lshr = BINARY_ASSIGNABLE+12; public static final int OPCODE_rshr = BINARY_ASSIGNABLE+13; - public static final int OPCODE_indexof = BINARY_ASSIGNABLE+14; + public static final int OPCODE_arrayindex = BINARY_ASSIGNABLE+14; public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+15; - public static final int OPCODE_newarray = BINARY_ASSIGNABLE+16; + public static final int OPCODE_array = BINARY_ASSIGNABLE+16; + public static final int OPCODE_record = BINARY_ASSIGNABLE+17; // ========================================================================= // Nary Assignables // ========================================================================= - public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+16; - - public static final int OPCODE_newrecord = NARY_ASSIGNABLE+1; + public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+18; + public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; public static final int OPCODE_indirectinvoke = NARY_ASSIGNABLE+3; public static final int OPCODE_lambda = NARY_ASSIGNABLE+4; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index 25303d5901..d23ed68981 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -160,23 +160,6 @@ public static Loop Loop(int[] modifiedOperands, int block) { return new Loop(modifiedOperands,block); } - /** - * Construct a newrecord bytecode which constructs a new record - * and puts it on the stack. - * - * @param type - * @return - */ - public static NewRecord NewRecord(Type.Record type, int target, - Collection operands) { - return new NewRecord(type, target, CodeUtils.toIntArray(operands)); - } - - public static NewRecord NewRecord(Type.Record type, int target, - int[] operands) { - return new NewRecord(type, target, operands); - } - /** * Construct a return bytecode which does return a value and, hence, its * type automatically defaults to void. @@ -286,7 +269,7 @@ public String toString() { return "neg"; } }, - INVERT(1) { + BITWISEINVERT(1) { public String toString() { return "invert"; } @@ -352,7 +335,7 @@ public String toString() { return "shr"; } }, - INDEXOF(14) { + ARRAYINDEX(14) { public String toString() { return "indexof"; } @@ -366,6 +349,11 @@ public String toString() { public String toString() { return "array"; } + }, + RECORDCONSTRUCTOR(17) { + public String toString() { + return "record"; + } }; public int offset; @@ -1850,61 +1838,6 @@ public String toString() { } } - /** - * Constructs a new record value from the values of zero or more operand - * register, each of which is associated with a field name. The new record - * value is then written into the target register. For example, the - * following Whiley code: - * - *

-	 * type Point is {real x, real y}
-	 *
-	 * function f(real x, real y) -> Point:
-	 *     return {x: x, y: x}
-	 * 
- * - * can be translated into the following WyIL: - * - *
-	 * function f(real x, real y) -> Point:
-	 * body:
-	 *     assign %3 = %0         : real
-	 *     assign %4 = %0         : real
-	 *     newrecord %2 (%3, %4)  : {real x,real y}
-	 *     return %2              : {real x,real y}
-	 * 
- * - * @author David J. Pearce - * - */ - public static final class NewRecord extends - AbstractBytecode { - - private NewRecord(Type.Record type, int target, int[] operands) { - super(type, target, operands); - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return NewRecord(type(0), nTargets[0], nOperands); - } - - public int opcode() { - return OPCODE_newrecord; - } - - public boolean equals(Object o) { - if (o instanceof NewRecord) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "newrecord %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0); - } - } - /** * Represents a no-operation bytecode which, as the name suggests, does * nothing. diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index a5eac737c7..bde76840a4 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -194,8 +194,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.Loop) bytecode, frame, context); } else if (bytecode instanceof Codes.NewObject) { return execute((Codes.NewObject) bytecode, frame, context); - } else if (bytecode instanceof Codes.NewRecord) { - return execute((Codes.NewRecord) bytecode, frame, context); } else if (bytecode instanceof Codes.Nop) { return execute((Codes.Nop) bytecode, frame, context); } else if (bytecode instanceof Codes.Return) { @@ -804,30 +802,6 @@ private Object execute(Codes.NewObject bytecode, Constant[] frame, Context conte return context.pc.next(); } - /** - * Execute a Record bytecode instruction at a given point in the function or - * method body. This constructs a new record. - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.NewRecord bytecode, Constant[] frame, Context context) { - HashMap values = new HashMap(); - ArrayList fields = new ArrayList(bytecode.type(0).fields().keySet()); - Collections.sort(fields); - int[] operands = bytecode.operands(); - for (int i = 0; i != operands.length; ++i) { - values.put(fields.get(i), (Constant) frame[operands[i]]); - } - frame[bytecode.target(0)] = Constant.V_RECORD(values); - return context.pc.next(); - } - /** * Execute a Nop bytecode instruction at a given point in the function or * method body. This basically doesn't do anything! @@ -1056,6 +1030,10 @@ public CodeForest.Index getLabel(String label) { } return labels.get(label); } + + public Code getBytecode() { + return forest.get(pc).first(); + } } /** diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index e20f35216b..7808cb99f5 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -1,7 +1,9 @@ package wyil.util.interpreter; import wyil.lang.Code; +import wyil.lang.Codes; import wyil.lang.Constant; +import wyil.lang.Type; import wyil.util.interpreter.Interpreter.ConstantObject; import wyil.util.interpreter.Interpreter.Context; import wyil.util.interpreter.Interpreter.InternalFunction; @@ -9,6 +11,8 @@ import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; public class StandardFunctions { /** @@ -18,9 +22,9 @@ public class StandardFunctions { static { standardFunctions[Code.OPCODE_neg] = new Negate(); - standardFunctions[Code.OPCODE_invert] = new BitwiseInvert(); + standardFunctions[Code.OPCODE_arrayinvert] = new BitwiseInvert(); standardFunctions[Code.OPCODE_dereference] = new Dereference(); - standardFunctions[Code.OPCODE_lengthof] = new ArrayLength(); + standardFunctions[Code.OPCODE_arraylength] = new ArrayLength(); standardFunctions[Code.OPCODE_add ] = new Add(); standardFunctions[Code.OPCODE_sub ] = new Subtract(); standardFunctions[Code.OPCODE_mul ] = new Multiply(); @@ -31,9 +35,10 @@ public class StandardFunctions { standardFunctions[Code.OPCODE_bitwiseand] = new BitwiseAnd(); standardFunctions[Code.OPCODE_lshr] = new LeftShift(); standardFunctions[Code.OPCODE_rshr] = new RightShift(); - standardFunctions[Code.OPCODE_indexof] = new ArrayIndex(); + standardFunctions[Code.OPCODE_arrayindex] = new ArrayIndex(); standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); - standardFunctions[Code.OPCODE_newarray] = new ArrayConstructor(); + standardFunctions[Code.OPCODE_array] = new ArrayConstructor(); + standardFunctions[Code.OPCODE_record] = new RecordConstructor(); }; // ==================================================================================== @@ -212,4 +217,22 @@ public Constant apply(Constant[] operands, Context context) { return Constant.V_ARRAY(values); } } + + // ==================================================================================== + // Records + // ==================================================================================== + private static final class RecordConstructor implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Codes.Operator bytecode = (Codes.Operator) context.getBytecode(); + Type.EffectiveRecord rType = (Type.EffectiveRecord) bytecode.type(0); + HashMap values = new HashMap(); + ArrayList fields = new ArrayList(rType.fields().keySet()); + Collections.sort(fields); + for (int i = 0; i != operands.length; ++i) { + values.put(fields.get(i), operands[i]); + } + return Constant.V_RECORD(values); + } + } } diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 2a0323d623..708db0d07d 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -627,8 +627,6 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.Loop) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Update) { translate(pc, (Codes.Update) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.NewRecord) { - translate(pc, (Codes.NewRecord) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Return) { translate(pc, (Codes.Return) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Nop) { @@ -1277,29 +1275,6 @@ private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), type)); } - private void translate(CodeForest.Index index, Codes.NewRecord code, int freeSlot, CodeForest forest, - ArrayList bytecodes) { - construct(WHILEYRECORD, freeSlot, bytecodes); - JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); - - HashMap fields = code.type(0).fields(); - ArrayList keys = new ArrayList(fields.keySet()); - Collections.sort(keys); - for (int i = 0; i != code.operands().length; i++) { - int register = code.operands()[i]; - String key = keys.get(i); - Type fieldType = fields.get(key); - bytecodes.add(new Bytecode.Dup(WHILEYRECORD)); - bytecodes.add(new Bytecode.LoadConst(key)); - bytecodes.add(new Bytecode.Load(register, convertUnderlyingType(fieldType))); - addWriteConversion(fieldType, bytecodes); - bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "put", ftype, Bytecode.InvokeMode.VIRTUAL)); - bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT)); - } - - bytecodes.add(new Bytecode.Store(code.target(0), WHILEYRECORD)); - } - private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, CodeForest forest, ArrayList bytecodes) { @@ -2044,14 +2019,12 @@ private void addCheckCast(JvmType type, ArrayList bytecodes) { * @param bytecodes * @param params */ - private void construct(JvmType.Clazz owner, int freeSlot, - ArrayList bytecodes) { + private void construct(JvmType.Clazz owner, int freeSlot, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(owner)); bytecodes.add(new Bytecode.Dup(owner)); ArrayList paramTypes = new ArrayList(); JvmType.Function ftype = new JvmType.Function(T_VOID, paramTypes); - bytecodes.add(new Bytecode.Invoke(owner, "", ftype, - Bytecode.InvokeMode.SPECIAL)); + bytecodes.add(new Bytecode.Invoke(owner, "", ftype, Bytecode.InvokeMode.SPECIAL)); } public final static Type WHILEY_SYSTEM_T = Type.Nominal(new NameID(Trie @@ -2344,6 +2317,10 @@ public void addWriteConversion(Type type) { Wyil2JavaBuilder.this.addWriteConversion(type,bytecodes); } + public void construct(JvmType.Clazz type) { + Wyil2JavaBuilder.this.construct(type,freeSlot,bytecodes); + } + public JvmType toJvmType(Type type) { return convertUnderlyingType(type); } diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java index 35330bcf11..f85a416cd7 100644 --- a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -2,6 +2,11 @@ import static jasm.lang.JvmTypes.*; import static wyjc.Wyil2JavaBuilder.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + import jasm.lang.Bytecode; import jasm.lang.JvmType; import wyil.lang.Code; @@ -24,9 +29,9 @@ public class BytecodeTranslators { static { standardFunctions[Code.OPCODE_neg] = new Negate(); - standardFunctions[Code.OPCODE_invert] = new Invert(); + standardFunctions[Code.OPCODE_arrayinvert] = new Invert(); standardFunctions[Code.OPCODE_dereference] = new Dereference(); - standardFunctions[Code.OPCODE_lengthof] = new ArrayLength(); + standardFunctions[Code.OPCODE_arraylength] = new ArrayLength(); standardFunctions[Code.OPCODE_add ] = new Add(); standardFunctions[Code.OPCODE_sub ] = new Subtract(); standardFunctions[Code.OPCODE_mul ] = new Multiply(); @@ -37,9 +42,10 @@ public class BytecodeTranslators { standardFunctions[Code.OPCODE_bitwiseand] = new BitwiseAnd(); standardFunctions[Code.OPCODE_lshr] = new LeftShift(); standardFunctions[Code.OPCODE_rshr] = new RightShift(); - standardFunctions[Code.OPCODE_indexof] = new ArrayIndex(); + standardFunctions[Code.OPCODE_arrayindex] = new ArrayIndex(); standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); - standardFunctions[Code.OPCODE_newarray] = new ArrayConstructor(); + standardFunctions[Code.OPCODE_array] = new ArrayConstructor(); + standardFunctions[Code.OPCODE_record] = new RecordConstructor(); }; // ==================================================================================== @@ -257,4 +263,30 @@ public void translate(Codes.Operator bytecode, Context context) { context.add(new Bytecode.Store(bytecode.target(0), WHILEYARRAY)); } } + + private static final class RecordConstructor implements BytecodeTranslator { + @Override + public void translate(Codes.Operator code, Context context) { + Type.EffectiveRecord recType = (Type.EffectiveRecord) code.type(0); + JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); + + context.construct(WHILEYRECORD); + + ArrayList keys = new ArrayList(recType.fields().keySet()); + Collections.sort(keys); + for (int i = 0; i != code.operands().length; i++) { + int register = code.operands()[i]; + String key = keys.get(i); + Type fieldType = recType.field(key); + context.add(new Bytecode.Dup(WHILEYRECORD)); + context.add(new Bytecode.LoadConst(key)); + context.add(new Bytecode.Load(register, context.toJvmType(fieldType))); + context.addWriteConversion(fieldType); + context.add(new Bytecode.Invoke(WHILEYRECORD, "put", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Pop(JAVA_LANG_OBJECT)); + } + + context.add(new Bytecode.Store(code.target(0), WHILEYRECORD)); + } + } } From ea8cbd18a50d17bebce2d5bd17168cb7bd5bc52a Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 17:13:01 +1300 Subject: [PATCH 18/43] Remove Codes.Nop, Assign and NewObject #502 These bytecodes have been merged into Codes.Operator. --- .../wyc/src/wyc/builder/CodeGenerator.java | 24 ++- .../src/wyil/builders/VcExprGenerator.java | 32 +-- modules/wyil/src/wyil/checks/ModuleCheck.java | 5 +- modules/wyil/src/wyil/io/WyilFileReader.java | 14 +- modules/wyil/src/wyil/lang/Code.java | 13 +- modules/wyil/src/wyil/lang/Codes.java | 183 +----------------- .../wyil/util/interpreter/Interpreter.java | 49 ----- .../util/interpreter/StandardFunctions.java | 20 ++ modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 26 +-- .../src/wyjc/util/BytecodeTranslators.java | 36 ++++ 10 files changed, 101 insertions(+), 301 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 431ce2d7c2..080ebc67ee 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -447,11 +447,12 @@ private void generate(Stmt stmt, Environment environment, CodeForest.Block block private void generate(VariableDeclaration s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { // First, we allocate this variable to a given slot in the environment. - int root = environment.get(s.parameter.name); + int[] targets = { environment.get(s.parameter.name) }; // Second, translate initialiser expression if it exists. if (s.expr != null) { - int operand = generate(s.expr, environment, block, forest, context); - block.add(Codes.Assign(s.expr.result().raw(), root, operand), attributes(s)); + int[] operands = { generate(s.expr, environment, block, forest, context) }; + block.add(Codes.Operator(s.expr.result().raw(), targets, operands, Codes.OperatorKind.ASSIGN), + attributes(s)); } } @@ -542,8 +543,9 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme // This is the easiest case. Having translated the right-hand side // expression, we now assign it directly to the register allocated // for variable on the left-hand side. - int target = environment.get(v.var); - block.add(Codes.Assign(type, target, operand), attributes(lval)); + int[] targets = new int[] { environment.get(v.var) }; + int[] operands = new int[] { operand }; + block.add(Codes.Operator(type, targets, operands, Codes.OperatorKind.ASSIGN), attributes(lval)); } else if (lval instanceof Expr.IndexOf || lval instanceof Expr.FieldAccess || lval instanceof Expr.Dereference) { // This is the more complicated case, since the left-hand side @@ -750,7 +752,9 @@ private void generate(Stmt.Return s, Environment environment, CodeForest.Block b */ private void generate(Stmt.Skip s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - block.add(Codes.Nop, attributes(s)); + // TODO: should actually generate a NOP bytecode. This is an assignment + // from zero operands to zero targets. At the moment, I cannot encode + // this however because it will fail in the interpreter. } /** @@ -2041,10 +2045,10 @@ private int generate(Expr.FieldAccess expr, Environment environment, CodeForest. private int generate(Expr.New expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) throws ResolveError { - int operand = generate(expr.expr, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(Codes.NewObject(expr.type.raw(), target, operand)); - return target; + int[] operands = new int[] { generate(expr.expr, environment, block, forest, context) }; + int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.NEW)); + return targets[0]; } private int[] generate(List arguments, Environment environment, CodeForest.Block block, CodeForest forest, diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index 6a0b2aee3e..e6f186e83a 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -66,14 +66,8 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { transform((Codes.Invoke) code, forest, branch); } else if (code instanceof Codes.Label) { // skip - } else if (code instanceof Codes.Assign) { - transform((Codes.Assign) code, forest, branch); } else if (code instanceof Codes.Update) { transform((Codes.Update) code, forest, branch); - } else if (code instanceof Codes.Nop) { - // skip - } else if (code instanceof Codes.NewObject) { - transform((Codes.NewObject) code, forest, branch); } else if (code instanceof Codes.Lambda) { transform((Codes.Lambda) code, forest, branch); } else { @@ -88,14 +82,7 @@ public void transform(Code code, CodeForest forest, VcBranch branch) { internalFailure(e.getMessage(), filename, e, forest.get(branch.pc()).attributes()); } } - - protected void transform(Codes.Assign code, CodeForest forest, - VcBranch branch) { - for (int i = 0; i != code.operands().length; ++i) { - branch.write(code.target(i), branch.read(code.operand(i))); - } - } - + /** * Maps unary bytecodes into expression opcodes. */ @@ -129,6 +116,11 @@ protected void transform(Codes.Assign code, CodeForest forest, protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch) { switch(code.kind) { + case ASSIGN: + for (int i = 0; i != code.operands().length; ++i) { + branch.write(code.target(i), branch.read(code.operand(i))); + } + break; case NEG: case ARRAYLENGTH: { Codes.Operator bc = (Codes.Operator) code; @@ -172,6 +164,9 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch case RECORDCONSTRUCTOR: transformNary(Expr.Nary.Op.TUPLE,code,branch,forest); break; + case NEW: + branch.havoc(code.target(0)); + break; } } @@ -280,15 +275,6 @@ protected void transform(Codes.Lambda code, CodeForest forest, VcBranch branch) branch.havoc(code.target(0)); } - protected void transform(Codes.NewObject code, CodeForest forest, VcBranch branch) { - branch.havoc(code.target(0)); - } - - protected void transform(Codes.Nop code, CodeForest forest, - VcBranch branch) { - // do nout - } - protected void transform(Codes.Update code, CodeForest forest, VcBranch branch) { Expr result = branch.read(code.result()); Expr oldSource = branch.read(code.target(0)); diff --git a/modules/wyil/src/wyil/checks/ModuleCheck.java b/modules/wyil/src/wyil/checks/ModuleCheck.java index e8ca8d0ca9..092e273cda 100755 --- a/modules/wyil/src/wyil/checks/ModuleCheck.java +++ b/modules/wyil/src/wyil/checks/ModuleCheck.java @@ -138,10 +138,9 @@ protected void checkFunctionPure(int blockID, CodeForest forest) { syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if (code instanceof Codes.IndirectInvoke && ((Codes.IndirectInvoke)code).type(0) instanceof Type.Method) { syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if(code instanceof Codes.NewObject) { + } else if (code.opcode() == Code.OPCODE_newobject) { syntaxError(errorMessage(ALLOCATION_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if (code instanceof Codes.Operator - && ((Codes.Operator) code).kind == Codes.OperatorKind.DEREFERENCE) { + } else if (code.opcode() == Code.OPCODE_dereference) { syntaxError(errorMessage(REFERENCE_ACCESS_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if (code instanceof Code.AbstractCompoundBytecode) { diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 292a21eae1..a3eab49b3b 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -857,12 +857,7 @@ private static Codes.Label findLabel(int target, HashMap l private static final Schema[] schemas = new Schema[255]; static { - // - schemas[Code.OPCODE_nop] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO) { - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Nop; - } - }; + // schemas[Code.OPCODE_goto] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.TARGET){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Goto((String) extras[0]); @@ -920,13 +915,12 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= schemas[Code.OPCODE_assign] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Assign(types[0], targets[0], operands[0]); + return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ASSIGN); } - }; - + }; schemas[Code.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.NewObject((Type.Reference) types[0], targets[0], operands[0]); + return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.NEW); } }; schemas[Code.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Code.java index beb6e9e1fc..0b833a903a 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Code.java @@ -323,7 +323,7 @@ public boolean equals(Object o) { // ========================================================================= // Empty Bytecodes // ========================================================================= - public static final int OPCODE_nop = 0; + public static final int OPCODE_goto = 1; public static final int OPCODE_fail = 2; public static final int OPCODE_assert = 4; @@ -344,12 +344,7 @@ public boolean equals(Object o) { // Unary Assignables // ========================================================================= public static final int UNARY_ASSIGNABLE = UNARY_OPERATOR+5; - - public static final int OPCODE_assign = UNARY_ASSIGNABLE+0; - - //public static final int OPCODE_move = UNARY_ASSIGNABLE+4; - public static final int OPCODE_newobject = UNARY_ASSIGNABLE+5; - + public static final int OPCODE_fieldload = UNARY_ASSIGNABLE+8; public static final int OPCODE_convert = UNARY_ASSIGNABLE+9; public static final int OPCODE_const = UNARY_ASSIGNABLE+10; @@ -389,11 +384,13 @@ public boolean equals(Object o) { public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+15; public static final int OPCODE_array = BINARY_ASSIGNABLE+16; public static final int OPCODE_record = BINARY_ASSIGNABLE+17; + public static final int OPCODE_newobject = BINARY_ASSIGNABLE+18; + public static final int OPCODE_assign = BINARY_ASSIGNABLE+19; // ========================================================================= // Nary Assignables // ========================================================================= - public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+18; + public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+20; public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; public static final int OPCODE_indirectinvoke = NARY_ASSIGNABLE+3; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index d23ed68981..d96299fc49 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -63,20 +63,6 @@ public static Const Const(int target, Constant constant) { return new Const(target, constant); } - /** - * Construct a copy bytecode which copies the value from a - * given operand register into a given target register. - * - * @param type - * --- record type. - * @param reg - * --- reg to load. - * @return - */ - public static Assign Assign(Type type, int target, int operand) { - return new Assign(type, target, operand); - } - public static Convert Convert(Type from, int target, int operand, Type to) { return new Convert(from, target, operand, to); } @@ -209,8 +195,6 @@ public static Label Label(String label) { return new Label(label); } - public static final Nop Nop = new Nop(); - /** * Construct a switch bytecode which pops a value off the * stack, and switches to a given label based on it. @@ -227,11 +211,6 @@ public static Switch Switch(Type type, int operand, String defaultLabel, Collection> cases) { return new Switch(type, operand, defaultLabel, cases); } - - public static NewObject NewObject(Type.Reference type, int target, - int operand) { - return new NewObject(type, target, operand); - } public static Quantify Quantify(int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, int block) { @@ -354,6 +333,16 @@ public String toString() { public String toString() { return "record"; } + }, + NEW(18) { + public String toString() { + return "new"; + } + }, + ASSIGN(19) { + public String toString() { + return "assign"; + } }; public int offset; @@ -586,69 +575,6 @@ protected Code clone(int[] nTargets, int[] nOperands) { } } - /** - * Copy the contents from a given operand register into a given target - * register. For example, the following Whiley code: - * - *
-	 * function f(int x) -> int:
-	 *     x = x + 1
-	 *     return x
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x) -> int:
-	 * body:
-	 *     assign %1 = %0      : int
-	 *     const %2 = 1        : int
-	 *     add %0 = %1, %2     : int
-	 *     return %0           : int
-	 * 
- * - * Here we see that an initial assignment is made from register - * %0 to register %1. In fact, this assignment is - * unecessary but is useful to illustrate the assign bytecode. - * - *

- * NOTE: on many architectures this operation may not actually clone - * the data in question. Rather, it may copy the reference to the - * data and then increment its reference count. This is to ensure - * efficient treatment of large compound structures (e.g. lists, sets, maps - * and records). - *

- * - * @author David J. Pearce - * - */ - public static final class Assign extends AbstractBytecode { - - private Assign(Type type, int target, int operand) { - super(type, target, operand); - } - - public int opcode() { - return OPCODE_assign; - } - - @Override - public Code clone(int[] nTargets, int[] nOperands) { - return Assign(type(0), nTargets[0], nOperands[0]); - } - - public boolean equals(Object o) { - if (o instanceof Assign) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "assign %" + target(0) + " = %" + operand(0) + " " + " : " + type(0); - } - } - /** * Read a string from the operand register and prints it to the debug * console. For example, the following Whiley code: @@ -1838,39 +1764,6 @@ public String toString() { } } - /** - * Represents a no-operation bytecode which, as the name suggests, does - * nothing. - * - * @author David J. Pearce - * - */ - public static final class Nop extends AbstractBytecode { - private Nop() { - super(new Type[0],new int[0]); - } - - @Override - public int opcode() { - return OPCODE_nop; - } - - @Override - protected Code clone(int[] nTargets, int[] nOperands) { - return this; - } - - public boolean equals(Object o) { - return o instanceof Nop; - } - - public String toString() { - return "nop"; - } - - - } - /** * Returns from the enclosing function or method, possibly returning a * value. For example, the following Whiley code: @@ -2041,62 +1934,6 @@ public Code clone(int[] nTargets, int[] nOperands) { } - /** - * Instantiate a new object from the value in a given operand register, and - * write the result (a reference to that object) to a given target register. - * For example, the following Whiley code: - * - *
-	 * type PointObj as &{real x, real y}
-	 *
-	 * method f(real x, real y) -> PointObj:
-	 *     return new {x: x, y: y}
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * method f(int x, int y) -> &{real x,real y}:
-	 * body:
-	 *     newrecord %2 = (%0, %1)  : {real x,real y}
-	 *     newobject %2 = %2        : ref {real x,real y}
-	 *     return %2                : ref {real x,real y}
-	 * 
- * - * NOTE: objects are unlike other data types in WyIL, in that they - * represent mutable state allocated on a heap. Thus, changes to an object - * within a method are visible to those outside of the method. - * - * @author David J. Pearce - * - */ - public static final class NewObject extends AbstractBytecode { - - private NewObject(Type.Reference type, int target, int operand) { - super(type, target, operand); - } - - @Override - public int opcode() { - return OPCODE_newobject; - } - - protected Code clone(int[] nTargets, int[] nOperands) { - return NewObject(type(0), nTargets[0], nOperands[0]); - } - - public boolean equals(Object o) { - if (o instanceof NewObject) { - return super.equals(o); - } - return false; - } - - public String toString() { - return "newobject %" + target(0) + " = %" + operand(0) + " : " + type(0); - } - } - // ============================================================= // Helpers // ============================================================= diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index bde76840a4..e4e222d1ee 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -159,8 +159,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.Invariant) bytecode, frame, context); } else if (bytecode instanceof Codes.AssertOrAssume) { return execute((Codes.AssertOrAssume) bytecode, frame, context); - } else if (bytecode instanceof Codes.Assign) { - return execute((Codes.Assign) bytecode, frame, context); } else if (bytecode instanceof Codes.Operator) { return execute((Codes.Operator) bytecode, frame, context); } else if (bytecode instanceof Codes.Const) { @@ -192,10 +190,6 @@ private Object execute(Constant[] frame, Context context) { return execute((Codes.Quantify) bytecode, frame, context); } else if (bytecode instanceof Codes.Loop) { return execute((Codes.Loop) bytecode, frame, context); - } else if (bytecode instanceof Codes.NewObject) { - return execute((Codes.NewObject) bytecode, frame, context); - } else if (bytecode instanceof Codes.Nop) { - return execute((Codes.Nop) bytecode, frame, context); } else if (bytecode instanceof Codes.Return) { return execute((Codes.Return) bytecode, frame, context); } else if (bytecode instanceof Codes.Switch) { @@ -231,26 +225,6 @@ private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, Context } } - /** - * Execute an Assign bytecode instruction at a given point in the function - * or method body - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.Assign bytecode, Constant[] frame, Context context) { - int[] targets = bytecode.targets(); - for (int i = 0; i != targets.length; ++i) { - frame[bytecode.target(i)] = frame[bytecode.operand(i)]; - } - return context.pc.next(); - } - /** * Execute a binary operator instruction at a given point in the function or * method body. This will check operands match their expected types. @@ -795,29 +769,6 @@ private Object execute(Codes.Loop bytecode, Constant[] frame, Context context) { return r; } - private Object execute(Codes.NewObject bytecode, Constant[] frame, Context context) { - Constant operand = frame[bytecode.operand(0)]; - ConstantObject o = new ConstantObject(operand); - frame[bytecode.target(0)] = o; - return context.pc.next(); - } - - /** - * Execute a Nop bytecode instruction at a given point in the function or - * method body. This basically doesn't do anything! - * - * @param bytecode - * --- The bytecode to execute - * @param frame - * --- The current stack frame - * @param context - * --- Context in which bytecodes are executed - * @return - */ - private Object execute(Codes.Nop bytecode, Constant[] frame, Context context) { - return context.pc.next(); - } - /** * Execute a Return bytecode instruction at a given point in the function or * method body diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index 7808cb99f5..d3b84313e4 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -21,6 +21,7 @@ public class StandardFunctions { public static final InternalFunction[] standardFunctions = new InternalFunction[255]; static { + standardFunctions[Code.OPCODE_assign] = new Assign(); standardFunctions[Code.OPCODE_neg] = new Negate(); standardFunctions[Code.OPCODE_arrayinvert] = new BitwiseInvert(); standardFunctions[Code.OPCODE_dereference] = new Dereference(); @@ -39,8 +40,20 @@ public class StandardFunctions { standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); standardFunctions[Code.OPCODE_array] = new ArrayConstructor(); standardFunctions[Code.OPCODE_record] = new RecordConstructor(); + standardFunctions[Code.OPCODE_newobject] = new ObjectConstructor(); }; + // ==================================================================================== + // General + // ==================================================================================== + + private static final class Assign implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return operands[0]; + } + } + // ==================================================================================== // References // ==================================================================================== @@ -51,6 +64,13 @@ public Constant apply(Constant[] operands, Context context) { ConstantObject ref = checkType(operands[0], context, ConstantObject.class); return ref.read(); } + } + + private static final class ObjectConstructor implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return new ConstantObject(operands[0]); + } } diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 708db0d07d..30618951da 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -407,7 +407,7 @@ private void patchInvariantBlockHelper(String falseBranch, CodeForest.Block bloc if (c instanceof Codes.Return) { // first patch point - block.set(i, Codes.Nop); + block.set(i, Codes.Operator(Type.T_VOID, new int[0], new int[0], Codes.OperatorKind.ASSIGN)); } else if (c instanceof Codes.Fail) { // second patch point block.set(i, Codes.Goto(falseBranch)); @@ -621,20 +621,14 @@ private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest f translate(pc, (Codes.Label) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Lambda) { translate(pc, (Codes.Lambda) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Assign) { - translate(pc, (Codes.Assign) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Loop) { translate(pc, (Codes.Loop) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Update) { translate(pc, (Codes.Update) code, freeSlot, forest, bytecodes); } else if (code instanceof Codes.Return) { translate(pc, (Codes.Return) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Nop) { - // do nothing } else if (code instanceof Codes.Switch) { translate(pc, (Codes.Switch) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.NewObject) { - translate(pc, (Codes.NewObject) code, freeSlot, forest, bytecodes); } else { internalFailure("unknown wyil code encountered (" + code + ")", filename, @@ -1233,13 +1227,6 @@ private void translate(CodeForest.Index index, Codes.Debug c, int freeSlot, bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "print", ftype, Bytecode.InvokeMode.STATIC)); } - private void translate(CodeForest.Index index, Codes.Assign c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { - JvmType jt = convertUnderlyingType(c.type(0)); - bytecodes.add(new Bytecode.Load(c.operand(0), jt)); - bytecodes.add(new Bytecode.Store(c.target(0), jt)); - } - private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); @@ -1264,17 +1251,6 @@ private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, C generators[c.opcode()].translate(c, context); } - private void translate(CodeForest.Index index, Codes.NewObject c, int freeSlot, CodeForest forest, ArrayList bytecodes) { - JvmType type = convertUnderlyingType(c.type(0)); - bytecodes.add(new Bytecode.New(WHILEYOBJECT)); - bytecodes.add(new Bytecode.Dup(WHILEYOBJECT)); - bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0).element()))); - addWriteConversion(c.type(0).element(), bytecodes); - JvmType.Function ftype = new JvmType.Function(T_VOID, JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "", ftype, Bytecode.InvokeMode.SPECIAL)); - bytecodes.add(new Bytecode.Store(c.target(0), type)); - } - private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, CodeForest forest, ArrayList bytecodes) { diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java index f85a416cd7..e50ef8d3a2 100644 --- a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -28,6 +28,7 @@ public class BytecodeTranslators { public static final BytecodeTranslator[] standardFunctions = new BytecodeTranslator[255]; static { + standardFunctions[Code.OPCODE_assign] = new Assign(); standardFunctions[Code.OPCODE_neg] = new Negate(); standardFunctions[Code.OPCODE_arrayinvert] = new Invert(); standardFunctions[Code.OPCODE_dereference] = new Dereference(); @@ -46,8 +47,27 @@ public class BytecodeTranslators { standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); standardFunctions[Code.OPCODE_array] = new ArrayConstructor(); standardFunctions[Code.OPCODE_record] = new RecordConstructor(); + standardFunctions[Code.OPCODE_newobject] = new New(); }; + + // ==================================================================================== + // General + // ==================================================================================== + + private static final class Assign implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + JvmType jt = context.toJvmType(bytecode.type(0)); + int[] targets = bytecode.targets(); + int[] operands = bytecode.operands(); + for (int i = 0; i != operands.length; ++i) { + context.add(new Bytecode.Load(operands[i], jt)); + context.add(new Bytecode.Store(targets[i], jt)); + } + } + } + // ==================================================================================== // References // ==================================================================================== @@ -65,6 +85,22 @@ public void translate(Codes.Operator bytecode, Context context) { context.add(new Bytecode.Store(bytecode.target(0), type)); } } + + private static final class New implements BytecodeTranslator { + @Override + public void translate(Codes.Operator bytecode, Context context) { + Type.Reference refType = (Type.Reference) bytecode.type(0); + JvmType type = context.toJvmType(refType); + JvmType elementType = context.toJvmType(refType.element()); + JvmType.Function ftype = new JvmType.Function(T_VOID, JAVA_LANG_OBJECT); + context.add(new Bytecode.New(WHILEYOBJECT)); + context.add(new Bytecode.Dup(WHILEYOBJECT)); + context.add(new Bytecode.Load(bytecode.operand(0), elementType)); + context.addWriteConversion(refType.element()); + context.add(new Bytecode.Invoke(WHILEYOBJECT, "", ftype, Bytecode.InvokeMode.SPECIAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } // ==================================================================================== // Arithmetic // ==================================================================================== From 2a5110c550c509a42696d3e37854c210d3a30e96 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 19:15:17 +1300 Subject: [PATCH 19/43] Refactor wyil.lang.Code #502 Renamed Code to Bytecode, and Code.AbstractBranchingCode to Bytecode.Branching and Code.AbstractCompoundCode to Bytecode.Compound. This naming is better for sure, though there's more to go. --- .../src/wyil/builders/VcExprGenerator.java | 13 +- .../wyil/src/wyil/builders/VcGenerator.java | 4 +- modules/wyil/src/wyil/builders/VcUtils.java | 16 +- .../wyil/src/wyil/checks/CoercionCheck.java | 2 +- .../wyil/checks/DefiniteAssignmentCheck.java | 36 +-- modules/wyil/src/wyil/checks/ModuleCheck.java | 10 +- modules/wyil/src/wyil/io/WyilFilePrinter.java | 8 +- modules/wyil/src/wyil/io/WyilFileReader.java | 194 +++++++-------- modules/wyil/src/wyil/io/WyilFileWriter.java | 31 ++- .../wyil/lang/{Code.java => Bytecode.java} | 227 ++++++++---------- modules/wyil/src/wyil/lang/CodeForest.java | 20 +- modules/wyil/src/wyil/lang/CodeUtils.java | 2 +- modules/wyil/src/wyil/lang/Codes.java | 108 +++++---- .../src/wyil/transforms/LoopVariants.java | 16 +- .../wyil/util/dfa/BackwardFlowAnalysis.java | 4 +- .../wyil/util/dfa/ForwardFlowAnalysis.java | 4 +- .../wyil/util/interpreter/Interpreter.java | 4 +- .../util/interpreter/StandardFunctions.java | 42 ++-- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 5 +- .../src/wyjc/util/BytecodeTranslators.java | 42 ++-- 20 files changed, 386 insertions(+), 402 deletions(-) rename modules/wyil/src/wyil/lang/{Code.java => Bytecode.java} (76%) diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index e6f186e83a..caf1e47cf4 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -20,7 +20,7 @@ import wycs.syntax.SyntacticType; import wyfs.lang.Path; import wyfs.util.Trie; -import wyil.lang.Code; +import wyil.lang.Bytecode; import wyil.lang.CodeForest; import wyil.lang.Codes; import wyil.lang.Type; @@ -48,7 +48,7 @@ public VcExprGenerator(String filename, Builder builder, VcUtils utils) { * @param branch * The branch on entry to the bytecode. */ - public void transform(Code code, CodeForest forest, VcBranch branch) { + public void transform(Bytecode code, CodeForest forest, VcBranch branch) { try { if (code instanceof Codes.Operator) { transform((Codes.Operator) code, forest, branch); @@ -188,7 +188,8 @@ protected void transform(Codes.Debug code, CodeForest forest, } protected void transform(Codes.FieldLoad code, CodeForest forest, VcBranch branch) { - ArrayList fields = new ArrayList(code.type(0).fields().keySet()); + Type.EffectiveRecord er = (Type.EffectiveRecord) code.type(0); + ArrayList fields = new ArrayList(er.fields().keySet()); Collections.sort(fields); Expr src = branch.read(code.operand(0)); Expr index = new Expr.Constant(Value.Integer(BigInteger.valueOf(fields.indexOf(code.field)))); @@ -332,7 +333,7 @@ protected void updateHelper(Iterator iter, Expr oldSource, Expr newS * @param branch * --- The enclosing branch */ - protected void transformUnary(Expr.Unary.Op operator, Code.AbstractBytecode code, VcBranch branch, + protected void transformUnary(Expr.Unary.Op operator, Bytecode code, VcBranch branch, CodeForest forest) { Expr lhs = branch.read(code.operand(0)); branch.write(code.target(0), @@ -352,7 +353,7 @@ protected void transformUnary(Expr.Unary.Op operator, Code.AbstractBytecode code * @param branch * --- The enclosing branch */ - protected void transformBinary(Expr.Binary.Op operator, Code.AbstractBytecode code, VcBranch branch, + protected void transformBinary(Expr.Binary.Op operator, Bytecode code, VcBranch branch, CodeForest forest) { Expr lhs = branch.read(code.operand(0)); Expr rhs = branch.read(code.operand(1)); @@ -381,7 +382,7 @@ protected void transformBinary(Expr.Binary.Op operator, Code.AbstractBytecode co * @param branch * --- The enclosing branch */ - protected void transformNary(Expr.Nary.Op operator, Code.AbstractBytecode code, VcBranch branch, + protected void transformNary(Expr.Nary.Op operator, Bytecode code, VcBranch branch, CodeForest forest) { int[] code_operands = code.operands(); Expr[] vals = new Expr[code_operands.length]; diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java index 5435884773..bf40dc7d82 100644 --- a/modules/wyil/src/wyil/builders/VcGenerator.java +++ b/modules/wyil/src/wyil/builders/VcGenerator.java @@ -421,7 +421,7 @@ protected Pair> transform(int block, int offset, CodeFo } else { // Continue executing this branch as it is still within the // scope of this block. - Code code = forest.get(pc).code(); + Bytecode code = forest.get(pc).code(); // Now, dispatch statements. Control statements are treated // specially from unit statements. if (code instanceof Codes.AssertOrAssume) { @@ -444,7 +444,7 @@ protected Pair> transform(int block, int offset, CodeFo } else if (code instanceof Codes.If || code instanceof Codes.IfIs || code instanceof Codes.Switch - || code instanceof Code.AbstractCompoundBytecode) { + || code instanceof Bytecode.Compound) { List bs; if (code instanceof Codes.If) { bs = transform((Codes.If) code, branch, labels, forest); diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java index 74a6ee9585..d6a6c3b1cd 100644 --- a/modules/wyil/src/wyil/builders/VcUtils.java +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -26,7 +26,7 @@ import wycs.syntax.WyalFile.Function; import wyfs.lang.Path; import wyil.attributes.VariableDeclarations; -import wyil.lang.Code; +import wyil.lang.Bytecode; import wyil.lang.CodeForest; import wyil.lang.Codes; import wyil.lang.Constant; @@ -361,21 +361,21 @@ public boolean containsNominal(Type t, * @param branches * @param block */ - public Pair[] getPreconditions(Code code, VcBranch branch, + public Pair[] getPreconditions(Bytecode code, VcBranch branch, Type[] environment, CodeForest forest) { // try { switch (code.opcode()) { - case Code.OPCODE_div: - case Code.OPCODE_rem: + case Bytecode.OPCODE_div: + case Bytecode.OPCODE_rem: return divideByZeroCheck((Codes.Operator) code, branch); - case Code.OPCODE_arrayindex: + case Bytecode.OPCODE_arrayindex: return indexOutOfBoundsChecks((Codes.Operator) code, branch); - case Code.OPCODE_arrygen: + case Bytecode.OPCODE_arrygen: return arrayGeneratorChecks((Codes.Operator) code, branch); - case Code.OPCODE_update: + case Bytecode.OPCODE_update: return updateChecks((Codes.Update) code, branch); - case Code.OPCODE_invoke: + case Bytecode.OPCODE_invoke: return preconditionCheck((Codes.Invoke) code, branch, environment, forest); } return new Pair[0]; diff --git a/modules/wyil/src/wyil/checks/CoercionCheck.java b/modules/wyil/src/wyil/checks/CoercionCheck.java index cf659225b7..17ffe0d070 100755 --- a/modules/wyil/src/wyil/checks/CoercionCheck.java +++ b/modules/wyil/src/wyil/checks/CoercionCheck.java @@ -98,7 +98,7 @@ protected void check(int blockID, CodeForest forest, WyilFile.FunctionOrMethod m CodeForest.Block block = forest.get(blockID); for (int i = 0; i != block.size(); ++i) { CodeForest.Entry e = block.get(i); - Code code = e.code(); + Bytecode code = e.code(); if (code instanceof Codes.Convert) { Codes.Convert conv = (Codes.Convert) code; check(conv.type(0), conv.result(), new HashSet>(), e.attribute(SourceLocation.class)); diff --git a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java index 163fdc05a4..83fdd24727 100755 --- a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java +++ b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java @@ -102,7 +102,7 @@ public HashSet initialStore() { } @Override - public HashSet propagate(CodeForest.Index index, Code code, HashSet in) { + public HashSet propagate(CodeForest.Index index, Bytecode code, HashSet in) { checkUses(index, code, in); int[] defs = defs(code); @@ -185,33 +185,23 @@ protected HashSet join(HashSet s1, HashSet s2) { return r; } - public void checkUses(CodeForest.Index index, Code code, HashSet in) { - if (code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode a = (Code.AbstractBytecode) code; - for (int operand : a.operands()) { - if (!in.contains(operand)) { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, - forest.get(index).attribute(SourceLocation.class)); - } - } - if (code instanceof Codes.Update && !in.contains(a.target(0))) { - // In this case, we are assigning to an index or field. - // Therefore, the target register must already be defined. + public void checkUses(CodeForest.Index index, Bytecode code, HashSet in) { + + for (int operand : code.operands()) { + if (!in.contains(operand)) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, forest.get(index).attribute(SourceLocation.class)); } - return; - } else { - // includes abstract-assignables and branching bytecodes - return; + } + if (code instanceof Codes.Update && !in.contains(code.target(0))) { + // In this case, we are assigning to an index or field. + // Therefore, the target register must already be defined. + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, + forest.get(index).attribute(SourceLocation.class)); } } - public int[] defs(Code code) { - if (code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode aa = (Code.AbstractBytecode) code; - return aa.targets(); - } - return new int[0]; + public int[] defs(Bytecode code) { + return code.targets(); } } diff --git a/modules/wyil/src/wyil/checks/ModuleCheck.java b/modules/wyil/src/wyil/checks/ModuleCheck.java index 092e273cda..8960c2ccae 100755 --- a/modules/wyil/src/wyil/checks/ModuleCheck.java +++ b/modules/wyil/src/wyil/checks/ModuleCheck.java @@ -132,19 +132,19 @@ protected void checkFunctionPure(int blockID, CodeForest forest) { CodeForest.Block block = forest.get(blockID); for (int i = 0; i != block.size(); ++i) { CodeForest.Entry e = block.get(i); - Code code = e.first(); + Bytecode code = e.first(); if(code instanceof Codes.Invoke && ((Codes.Invoke)code).type(0) instanceof Type.Method) { // internal message send syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if (code instanceof Codes.IndirectInvoke && ((Codes.IndirectInvoke)code).type(0) instanceof Type.Method) { syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if (code.opcode() == Code.OPCODE_newobject) { + } else if (code.opcode() == Bytecode.OPCODE_newobject) { syntaxError(errorMessage(ALLOCATION_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if (code.opcode() == Code.OPCODE_dereference) { + } else if (code.opcode() == Bytecode.OPCODE_dereference) { syntaxError(errorMessage(REFERENCE_ACCESS_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if (code instanceof Code.AbstractCompoundBytecode) { - Code.AbstractCompoundBytecode a = (Code.AbstractCompoundBytecode) code; + } else if (code instanceof Bytecode.Compound) { + Bytecode.Compound a = (Bytecode.Compound) code; checkFunctionPure(a.block(), forest); } } diff --git a/modules/wyil/src/wyil/io/WyilFilePrinter.java b/modules/wyil/src/wyil/io/WyilFilePrinter.java index 38081ce9c8..eed5b834e6 100755 --- a/modules/wyil/src/wyil/io/WyilFilePrinter.java +++ b/modules/wyil/src/wyil/io/WyilFilePrinter.java @@ -189,7 +189,7 @@ private void writeParameters(List parameters, PrintWriter out) { private void write(int indent, int blockID, CodeForest forest, PrintWriter out) { CodeForest.Block block = forest.get(blockID); for(int i=0;i!=block.size();++i) { - Code code = block.get(i).code(); + Bytecode code = block.get(i).code(); if(code instanceof Codes.Label) { write(indent-1,code,forest,out); } else { @@ -198,7 +198,7 @@ private void write(int indent, int blockID, CodeForest forest, PrintWriter out) } } - private void write(int indent, Code c, CodeForest forest, PrintWriter out) { + private void write(int indent, Bytecode c, CodeForest forest, PrintWriter out) { String line = "null"; tabIndent(indent+1,out); @@ -223,8 +223,8 @@ private void write(int indent, Code c, CodeForest forest, PrintWriter out) { // } out.println(); - if(c instanceof Code.AbstractCompoundBytecode) { - Code.AbstractCompoundBytecode cc = (Code.AbstractCompoundBytecode) c; + if(c instanceof Bytecode.Compound) { + Bytecode.Compound cc = (Bytecode.Compound) c; write(indent+1,cc.block(),forest,out); } } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index a3eab49b3b..db9171d5ee 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -37,11 +37,11 @@ import wyfs.lang.Path; import wyfs.util.Trie; import wyil.lang.*; -import wyil.lang.Code.Schema; -import wyil.lang.Code.Extras; -import wyil.lang.Code.Operands; -import wyil.lang.Code.Targets; -import wyil.lang.Code.Types; +import wyil.lang.Bytecode.Schema; +import wyil.lang.Bytecode.Extras; +import wyil.lang.Bytecode.Operands; +import wyil.lang.Bytecode.Targets; +import wyil.lang.Bytecode.Types; /** * Read a binary WYIL file from a byte stream and convert into the corresponding @@ -724,16 +724,16 @@ public CodeForest.Block readCodeBlock(int offset, HashMap ArrayList bytecodes = new ArrayList(); for (int i = 0; i < nCodes; ++i) { - Code code = readBytecode(i + offset, labels); + Bytecode code = readBytecode(i + offset, labels); bytecodes.add(new CodeForest.Entry(code)); } // TODO: read any attributes given return new CodeForest.Block(bytecodes); } - private Code readBytecode(int offset, HashMap labels) throws IOException { + private Bytecode readBytecode(int offset, HashMap labels) throws IOException { int opcode = input.read_u8(); - Code.Schema schema = schemas[opcode]; + Bytecode.Schema schema = schemas[opcode]; // First, read and validate all targets, operands and types int nTargets = input.read_uv(); @@ -759,7 +759,7 @@ private Code readBytecode(int offset, HashMap labels) thro * @return * @throws IOException */ - private Object[] readExtras(Code.Schema schema, HashMap labels) + private Object[] readExtras(Bytecode.Schema schema, HashMap labels) throws IOException { Extras[] extras = schema.extras(); Object[] results = new Object[extras.length]; @@ -858,28 +858,28 @@ private static Codes.Label findLabel(int target, HashMap l static { // - schemas[Code.OPCODE_goto] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_goto] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Goto((String) extras[0]); } }; - schemas[Code.OPCODE_fail] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_fail] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Fail(); } }; - schemas[Code.OPCODE_assert] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_assert] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Assert((Integer) extras[0]); } }; - schemas[Code.OPCODE_assume] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_assume] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Assume((Integer) extras[0]); } }; - schemas[Code.OPCODE_invariant] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_invariant] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Invariant((Integer) extras[0]); } }; @@ -887,25 +887,25 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Unary Operators. // ========================================================================= - schemas[Code.OPCODE_debug] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_debug] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Debug(operands[0]); } }; - schemas[Code.OPCODE_return] = new Schema(Targets.ZERO, Operands.MANY, Types.MANY){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_return] = new Schema(Targets.ZERO, Operands.MANY, Types.MANY){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Return(types,operands); } }; - schemas[Code.OPCODE_switch] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE, Extras.TARGET, Extras.SWITCH_ARRAY){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_switch] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE, Extras.TARGET, Extras.SWITCH_ARRAY){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { String defaultTarget = (String) extras[0]; Pair[] cases = (Pair[]) extras[1]; return Codes.Switch(types[0], operands[0], defaultTarget, Arrays.asList(cases)); } }; - schemas[Code.OPCODE_ifis] = new Schema(Targets.ZERO, Operands.ONE, Types.TWO, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_ifis] = new Schema(Targets.ZERO, Operands.ONE, Types.TWO, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.IfIs(types[0], operands[0], types[1], (String)extras[0]); } }; @@ -913,48 +913,48 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Unary Assignables // ========================================================================= - schemas[Code.OPCODE_assign] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_assign] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ASSIGN); } }; - schemas[Code.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.NEW); } }; - schemas[Code.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DEREFERENCE); } }; - schemas[Code.OPCODE_arrayinvert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_arrayinvert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEINVERT); } }; - schemas[Code.OPCODE_arraylength] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { - public Code construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_arraylength] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { + public Bytecode construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYLENGTH); } }; - schemas[Code.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.NEG); } }; - schemas[Code.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); } }; - schemas[Code.OPCODE_convert] = new Schema(Targets.ONE, Operands.ONE, Types.TWO){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_convert] = new Schema(Targets.ONE, Operands.ONE, Types.TWO){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Convert(types[0], targets[0], operands[0], types[1]); } }; - schemas[Code.OPCODE_const] = new Schema(Targets.ONE, Operands.ZERO, Types.ZERO, Extras.CONSTANT){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_const] = new Schema(Targets.ONE, Operands.ZERO, Types.ZERO, Extras.CONSTANT){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Const(targets[0], (Constant) extras[0]); } }; @@ -962,33 +962,33 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Binary Operators // ========================================================================= - schemas[Code.OPCODE_ifeq] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_ifeq] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.EQ, (String) extras[0]); } }; - schemas[Code.OPCODE_ifne] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_ifne] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.NEQ, (String) extras[0]); } }; - schemas[Code.OPCODE_iflt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_iflt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.LT, (String) extras[0]); } }; - schemas[Code.OPCODE_ifle] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_ifle] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.LTEQ, (String) extras[0]); } }; - schemas[Code.OPCODE_ifgt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_ifgt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.GT, (String) extras[0]); } }; - schemas[Code.OPCODE_ifge] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_ifge] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.GTEQ, (String) extras[0]); } }; @@ -996,69 +996,69 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Binary Assignables // ========================================================================= - schemas[Code.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ADD); } }; - schemas[Code.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.SUB); } }; - schemas[Code.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.MUL); } }; - schemas[Code.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DIV); } }; - schemas[Code.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.REM); } }; - schemas[Code.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEOR); } }; - schemas[Code.OPCODE_bitwisexor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_bitwisexor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEXOR); } }; - schemas[Code.OPCODE_bitwiseand] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_bitwiseand] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEAND); } }; - schemas[Code.OPCODE_lshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_lshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.LEFTSHIFT); } }; - schemas[Code.OPCODE_rshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_rshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RIGHTSHIFT); } }; - schemas[Code.OPCODE_arrayindex] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_arrayindex] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.ARRAYINDEX); } }; - schemas[Code.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands,Codes.OperatorKind.ARRAYGENERATOR); } }; - schemas[Code.OPCODE_array] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_array] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYCONSTRUCTOR); } }; @@ -1066,40 +1066,40 @@ public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Ob // ========================================================================= // Nary Assignables // ========================================================================= - schemas[Code.OPCODE_record] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_record] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RECORDCONSTRUCTOR); } }; - schemas[Code.OPCODE_invoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_invoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Invoke((Type.FunctionOrMethod) types[0], targets, operands, (NameID) extras[0]); } }; - schemas[Code.OPCODE_indirectinvoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_indirectinvoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { int src = operands[0]; operands = Arrays.copyOfRange(operands,1,operands.length); return Codes.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, src, operands); } }; - schemas[Code.OPCODE_lambda] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_lambda] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, (NameID) extras[0]); } }; - schemas[Code.OPCODE_loop] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_loop] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Loop(targets, (Integer) extras[0]); } }; - schemas[Code.OPCODE_quantify] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_quantify] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return Codes.Quantify(operands[0],operands[1],operands[2], targets, (Integer) extras[0]); } }; - schemas[Code.OPCODE_update] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.STRING_ARRAY){ - public Code construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + schemas[Bytecode.OPCODE_update] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.STRING_ARRAY){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { ArrayList fields = new ArrayList(); String[] strings = (String[]) extras[0]; for(int i=0;i!=strings.length;++i) { fields.add(strings[i]); } diff --git a/modules/wyil/src/wyil/io/WyilFileWriter.java b/modules/wyil/src/wyil/io/WyilFileWriter.java index 3d87b889af..6e64390856 100644 --- a/modules/wyil/src/wyil/io/WyilFileWriter.java +++ b/modules/wyil/src/wyil/io/WyilFileWriter.java @@ -594,7 +594,7 @@ private HashMap buildLabelsMap(CodeForest forest) { for (int i = 0; i != forest.numBlocks(); ++i) { CodeForest.Block block = forest.get(i); for (int j = 0; j != block.size(); ++j) { - Code code = block.get(j).code(); + Bytecode code = block.get(j).code(); if (code instanceof Codes.Label) { Codes.Label l = (Codes.Label) code; labels.put(l.label, offset-1); @@ -678,12 +678,12 @@ private int writeCodeBlock(CodeForest.Block block, int offset, HashMap */ - private void writeBytecode(Code.AbstractBytecode code, int offset, HashMap labels, BinaryOutputStream output) + private void writeBytecode(Bytecode code, int offset, HashMap labels, BinaryOutputStream output) throws IOException { writeCommon(code, output); writeRest(code, offset, labels, output); @@ -745,7 +745,7 @@ private void writeBytecode(Code.AbstractBytecode code, int offset, HashMap labels, + private void writeRest(Bytecode code, int offset, HashMap labels, BinaryOutputStream output) throws IOException { // now deal with non-uniform instructions // First, deal with special cases - if(code instanceof Code.AbstractCompoundBytecode) { + if(code instanceof Bytecode.Compound) { // Assert / Assume / Loop / Quantify - Code.AbstractCompoundBytecode cb = (Code.AbstractCompoundBytecode) code; + Bytecode.Compound cb = (Bytecode.Compound) code; output.write_uv(cb.block()); - } else if(code instanceof Code.AbstractBranchingBytecode) { - Code.AbstractBranchingBytecode bb = (Code.AbstractBranchingBytecode) code; + } else if(code instanceof Bytecode.Branching) { + Bytecode.Branching bb = (Bytecode.Branching) code; int destination = labels.get(bb.destination()); output.write_uv(destination); } else if (code instanceof Codes.Const) { @@ -870,7 +870,7 @@ private int generateModifiers(Collection modifiers) { private int countLabels(CodeForest.Block block) { int nlabels = 0; for (int i = 0; i != block.size(); ++i) { - Code code = block.get(i).code(); + Bytecode code = block.get(i).code(); if (code instanceof Codes.Label) { nlabels++; } @@ -951,7 +951,7 @@ private void buildPools(CodeForest.Block block) { } } - private void buildPools(Code code) { + private void buildPools(Bytecode code) { // First, deal with special cases if (code instanceof Codes.Const) { @@ -982,11 +982,8 @@ private void buildPools(Code code) { } // Second, deal with standard cases - if (code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode a = (Code.AbstractBytecode) code; - for (Type type : a.types()) { - addTypeItem(type); - } + for (Type type : code.types()) { + addTypeItem(type); } } diff --git a/modules/wyil/src/wyil/lang/Code.java b/modules/wyil/src/wyil/lang/Bytecode.java similarity index 76% rename from modules/wyil/src/wyil/lang/Code.java rename to modules/wyil/src/wyil/lang/Bytecode.java index 0b833a903a..4c7404f23a 100755 --- a/modules/wyil/src/wyil/lang/Code.java +++ b/modules/wyil/src/wyil/lang/Bytecode.java @@ -107,11 +107,22 @@ * * @author David J. Pearce */ -public interface Code { - - // =============================================================== - // Abstract Methods - // =============================================================== +public abstract class Bytecode { + protected final Type[] types; + private final int[] targets; + protected final int[] operands; + + public Bytecode(Type type, int target, int... operands) { + this.types = new Type[]{type}; + this.targets = new int[] {target}; + this.operands = operands; + } + + public Bytecode(Type[] types, int[] targets, int... operands) { + this.types = types; + this.targets = targets; + this.operands = operands; + } /** * Determine which registers are used in this bytecode. This can be used, @@ -120,7 +131,15 @@ public interface Code { * * @param register */ - public void registers(java.util.Set register); + public void registers(java.util.Set registers) { + for (int i = 0; i != targets().length; ++i) { + registers.add(targets()[i]); + } + for (int i = 0; i != operands().length; ++i) { + registers.add(operands[i]); + } + } + /** * Remaps all registers according to a given binding. Registers not @@ -131,126 +150,84 @@ public interface Code { * --- map from (existing) registers to (new) registers. * @return */ - public Code remap(Map binding); + public Bytecode remap(Map binding) { + int[] nTargets = remapOperands(binding, targets()); + int[] nOperands = remapOperands(binding, operands()); + if (nTargets != targets() || nOperands != operands()) { + return clone(nTargets, nOperands); + } + return this; + } + + protected abstract Bytecode clone(int[] nTargets, int[] nOperands); + + @Override + public int hashCode() { + return Arrays.hashCode(types) + Arrays.hashCode(targets()) + Arrays.hashCode(operands()); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Bytecode) { + Bytecode bo = (Bytecode) o; + return Arrays.equals(targets(), bo.targets()) && Arrays.equals(operands(), bo.operands()) + && Arrays.equals(types, bo.types); + } + return false; + } + + public Type[] types() { + return types; + } + + public Type type(int i) { + return (Type) types[i]; + } /** - * Return the opcode value of this bytecode. + * Return a specific target register assigned by this bytecode. + * * @return */ - public abstract int opcode(); - - // ===============================================================C - // Abstract Bytecodes - // =============================================================== - + public int target(int i) { + return targets[i]; + } + /** - * Represents the set of all bytecodes. Each bytecode consists of zero or - * more types, zero or more targets and zero or more operands. Furthermore, - * bytecodes may contain additional data. + * Return the target registers assigned by this bytecode. * - * @author David J. Pearce + * @return + */ + public int[] targets() { + return targets; + } + + /** + * Return the operand registers assigned by this bytecode. * - * @param - * --- the type associated with this bytecode. + * @return */ - public static abstract class AbstractBytecode implements Code { - protected final Type[] types; - private final int[] targets; - protected final int[] operands; - - public AbstractBytecode(Type type, int target, int... operands) { - this.types = new Type[]{type}; - this.targets = new int[] {target}; - this.operands = operands; - } - - public AbstractBytecode(Type[] types, int[] targets, int... operands) { - this.types = types; - this.targets = targets; - this.operands = operands; - } - - @Override - public void registers(java.util.Set registers) { - for (int i = 0; i != targets().length; ++i) { - registers.add(targets()[i]); - } - for (int i = 0; i != operands().length; ++i) { - registers.add(operands[i]); - } - } - - @Override - public Code remap(Map binding) { - int[] nTargets = remapOperands(binding, targets()); - int[] nOperands = remapOperands(binding, operands()); - if (nTargets != targets() || nOperands != operands()) { - return clone(nTargets, nOperands); - } - return this; - } - - protected abstract Code clone(int[] nTargets, int[] nOperands); - - @Override - public int hashCode() { - return Arrays.hashCode(types) + Arrays.hashCode(targets()) + Arrays.hashCode(operands()); - } - - @Override - public boolean equals(Object o) { - if (o instanceof AbstractBytecode) { - AbstractBytecode bo = (AbstractBytecode) o; - return Arrays.equals(targets(), bo.targets()) && Arrays.equals(operands(), bo.operands()) - && Arrays.equals(types, bo.types); - } - return false; - } - - public Type[] types() { - return types; - } - - public T type(int i) { - return (T) types[i]; - } - - /** - * Return a specific target register assigned by this bytecode. - * - * @return - */ - public int target(int i) { - return targets[i]; - } - - /** - * Return the target registers assigned by this bytecode. - * - * @return - */ - public int[] targets() { - return targets; - } - - /** - * Return the operand registers assigned by this bytecode. - * - * @return - */ - public int[] operands() { - return operands; - } + public int[] operands() { + return operands; + } - /** - * Return the ith operand read by this bytecode. - * @param i - * @return - */ - public int operand(int i) { - return operands[i]; - } + /** + * Return the ith operand read by this bytecode. + * @param i + * @return + */ + public int operand(int i) { + return operands[i]; } + // =============================================================== + // Abstract Methods + // =============================================================== + + /** + * Return the opcode value of this bytecode. + * @return + */ + public abstract int opcode(); /** * A compound bytecode represents a bytecode that contains sequence of zero @@ -261,10 +238,10 @@ public int operand(int i) { * @author David J. Pearce * */ - public static abstract class AbstractCompoundBytecode extends AbstractBytecode { + public static abstract class Compound extends Bytecode { protected final int block; - public AbstractCompoundBytecode(int block, Type[] types, int[] targets, int... operands) { + public Compound(int block, Type[] types, int[] targets, int... operands) { super(types, targets, operands); this.block = block; } @@ -278,8 +255,8 @@ public int hashCode() { } public boolean equals(Object o) { - if(o instanceof AbstractCompoundBytecode) { - AbstractCompoundBytecode abc = (AbstractCompoundBytecode) o; + if(o instanceof Compound) { + Compound abc = (Compound) o; return block == abc.block && super.equals(o); } return false; @@ -295,10 +272,10 @@ public boolean equals(Object o) { * @author David J. Pearce * */ - public static abstract class AbstractBranchingBytecode extends AbstractBytecode { + public static abstract class Branching extends Bytecode { protected final String destination; - public AbstractBranchingBytecode(String destination, Type[] types, int[] targets, int... operands) { + public Branching(String destination, Type[] types, int[] targets, int... operands) { super(types, targets, operands); this.destination = destination; } @@ -312,8 +289,8 @@ public int hashCode() { } public boolean equals(Object o) { - if(o instanceof AbstractBranchingBytecode) { - AbstractBranchingBytecode abc = (AbstractBranchingBytecode) o; + if(o instanceof Branching) { + Branching abc = (Branching) o; return destination.equals(abc.destination) && super.equals(o); } return false; @@ -442,7 +419,7 @@ public Extras[] extras() { return extras; } - public abstract Code construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras); + public abstract Bytecode construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras); public String toString() { return "<" + targets.toString() + " targets, " + operands + " operands, " + types + " types, " + Arrays.toString(extras) + ">"; diff --git a/modules/wyil/src/wyil/lang/CodeForest.java b/modules/wyil/src/wyil/lang/CodeForest.java index 3d52f95259..e1e161d1c2 100644 --- a/modules/wyil/src/wyil/lang/CodeForest.java +++ b/modules/wyil/src/wyil/lang/CodeForest.java @@ -222,22 +222,22 @@ public Block() { public Block(Collection entries) { super(entries); } - public void add(Code code, Attribute...attributes) { + public void add(Bytecode code, Attribute...attributes) { super.add(new Entry(code,attributes)); } - public void add(Code code, List attributes) { + public void add(Bytecode code, List attributes) { super.add(new Entry(code,attributes)); } - public void add(int start, Code code, Attribute...attributes) { + public void add(int start, Bytecode code, Attribute...attributes) { super.add(start, new Entry(code,attributes)); } - public void add(int start, Code code, List attributes) { + public void add(int start, Bytecode code, List attributes) { super.add(start, new Entry(code,attributes)); } - public void set(int i, Code code, Attribute...attributes) { + public void set(int i, Bytecode code, Attribute...attributes) { super.set(i,new Entry(code,attributes)); } - public void set(int i, Code code, List attributes) { + public void set(int i, Bytecode code, List attributes) { super.set(i,new Entry(code,attributes)); } } @@ -249,14 +249,14 @@ public void set(int i, Code code, List attributes) { * @author David J. Pearce * */ - public static class Entry extends Pair> { - public Entry(Code code, List attributes) { + public static class Entry extends Pair> { + public Entry(Bytecode code, List attributes) { super(code,attributes); } - public Entry(Code code, Attribute... attributes) { + public Entry(Bytecode code, Attribute... attributes) { super(code,Arrays.asList(attributes)); } - public Code code() { + public Bytecode code() { return first(); } public T attribute(Class clazz) { diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java index 0a4ff4be19..718b738585 100644 --- a/modules/wyil/src/wyil/lang/CodeUtils.java +++ b/modules/wyil/src/wyil/lang/CodeUtils.java @@ -86,7 +86,7 @@ public static Map buildLabelMap(CodeForest forest) { for (int i = 0; i != forest.numBlocks(); ++i) { CodeForest.Block block = forest.get(i); for (int j = 0; j != block.size(); ++j) { - Code code = block.get(j).code(); + Bytecode code = block.get(j).code(); if (code instanceof Codes.Label) { // Found a label, so register it in the labels map Codes.Label label = (Codes.Label) code; diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java index d96299fc49..fb6ae00cde 100644 --- a/modules/wyil/src/wyil/lang/Codes.java +++ b/modules/wyil/src/wyil/lang/Codes.java @@ -11,8 +11,8 @@ import wycc.lang.NameID; import wycc.util.Pair; -import wyil.lang.Code.*; -import static wyil.lang.Code.*; +import wyil.lang.Bytecode.*; +import static wyil.lang.Bytecode.*; import static wyil.lang.CodeUtils.*; public abstract class Codes { @@ -393,7 +393,7 @@ private OperatorKind(int offset) { * @author David J. Pearce * */ - public static final class Operator extends AbstractBytecode { + public static final class Operator extends Bytecode { public final OperatorKind kind; private Operator(Type type, int[] targets, int[] operands, @@ -412,7 +412,7 @@ public int opcode() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return Operator(type(0), nTargets, nOperands, kind); } @@ -473,13 +473,13 @@ public String toString() { *

* */ - public static final class Convert extends AbstractBytecode { + public static final class Convert extends Bytecode { private Convert(Type from, int target, int operand, Type result) { super(new Type[]{from,result}, new int[]{target}, operand); } - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return Convert(type(0), nTargets[0], nOperands[0], type(1)); } @@ -536,7 +536,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class Const extends AbstractBytecode { + public static final class Const extends Bytecode { public final Constant constant; private Const(int target, Constant constant) { @@ -570,7 +570,7 @@ public String toString() { } @Override - protected Code clone(int[] nTargets, int[] nOperands) { + protected Bytecode clone(int[] nTargets, int[] nOperands) { return new Const(nTargets[0],constant); } } @@ -605,7 +605,7 @@ protected Code clone(int[] nTargets, int[] nOperands) { * @author David J. Pearce * */ - public static final class Debug extends AbstractBytecode { + public static final class Debug extends Bytecode { private Debug(int operand) { super(new Type[]{Type.Array(Type.T_INT,false)}, new int[0], operand); @@ -616,7 +616,7 @@ public int opcode() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return Debug(nOperands[0]); } @@ -636,7 +636,7 @@ public String toString() { * @author David J. Pearce * */ - public static abstract class AssertOrAssume extends AbstractCompoundBytecode { + public static abstract class AssertOrAssume extends Compound { private AssertOrAssume(int block) { super(block, new Type[0], new int[0],new int[0]); } @@ -666,7 +666,7 @@ public boolean equals(Object o) { } @Override - protected Code clone(int[] nTargets, int[] nOperands) { + protected Bytecode clone(int[] nTargets, int[] nOperands) { return this; } } @@ -696,7 +696,7 @@ public boolean equals(Object o) { } @Override - protected Code clone(int[] nTargets, int[] nOperands) { + protected Bytecode clone(int[] nTargets, int[] nOperands) { return this; } } @@ -709,7 +709,7 @@ protected Code clone(int[] nTargets, int[] nOperands) { * @author David J. Pearce * */ - public static final class Fail extends Code.AbstractBytecode { + public static final class Fail extends Bytecode { private Fail() { super(new Type[0],new int[0]); } @@ -720,7 +720,7 @@ public int opcode() { } @Override - protected Code clone(int[] nTargets, int[] nOperands) { + protected Bytecode clone(int[] nTargets, int[] nOperands) { return this; } @@ -757,7 +757,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class FieldLoad extends AbstractBytecode { + public static final class FieldLoad extends Bytecode{ public final String field; private FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { @@ -770,8 +770,8 @@ private FieldLoad(Type.EffectiveRecord type, int target, int operand, String fie } @Override - public Code clone(int[] nTargets, int[] nOperands) { - return FieldLoad(type(0), nTargets[0], nOperands[0], field); + public Bytecode clone(int[] nTargets, int[] nOperands) { + return FieldLoad((Type.EffectiveRecord) type(0), nTargets[0], nOperands[0], field); } public int opcode() { @@ -783,7 +783,8 @@ public int hashCode() { } public Type fieldType() { - return type(0).fields().get(field); + Type.EffectiveRecord er = (Type.EffectiveRecord) type(0); + return er.fields().get(field); } public boolean equals(Object o) { @@ -841,7 +842,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class Goto extends AbstractBranchingBytecode { + public static final class Goto extends Branching { private Goto(String target) { super(target,new Type[0],new int[0]); } @@ -864,7 +865,7 @@ public boolean equals(Object o) { } @Override - protected Code clone(int[] nTargets, int[] nOperands) { + protected Bytecode clone(int[] nTargets, int[] nOperands) { return this; } @@ -924,7 +925,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class If extends AbstractBranchingBytecode { + public static final class If extends Branching { public final Comparator op; private If(Type type, int leftOperand, int rightOperand, Comparator op, @@ -947,7 +948,7 @@ public int opcode() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return If(types[0], nOperands[0], nOperands[1], op, destination()); } @@ -1055,7 +1056,7 @@ private Comparator(int offset) { * @author David J. Pearce * */ - public static final class IfIs extends AbstractBranchingBytecode { + public static final class IfIs extends Branching { private IfIs(Type type, int leftOperand, Type rightOperand, String target) { super(target, new Type[] { type, rightOperand }, new int[0], leftOperand); } @@ -1078,7 +1079,7 @@ public IfIs relabel(Map labels) { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return IfIs(types[0], nOperands[0], types[1], destination()); } @@ -1106,7 +1107,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class IndirectInvoke extends AbstractBytecode { + public static final class IndirectInvoke extends Bytecode { /** * Construct an indirect invocation bytecode which assigns to an @@ -1155,9 +1156,14 @@ public int[] parameters() { public int opcode() { return OPCODE_indirectinvoke; } - + + @Override + public Type.FunctionOrMethod type(int i) { + return (Type.FunctionOrMethod) super.type(i); + } + @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return IndirectInvoke(type(0), nTargets, nOperands[0], Arrays.copyOfRange(nOperands, 1, nOperands.length)); } @@ -1250,7 +1256,7 @@ public Invariant clone() { * @author David J. Pearce * */ - public static final class Invoke extends AbstractBytecode { + public static final class Invoke extends Bytecode { public final NameID name; private Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, @@ -1268,7 +1274,12 @@ public int hashCode() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Type.FunctionOrMethod type(int i) { + return (Type.FunctionOrMethod) super.type(i); + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { return Invoke(type(0), nTargets, nOperands, name); } @@ -1286,7 +1297,7 @@ public String toString() { } } - public static final class Lambda extends AbstractBytecode { + public static final class Lambda extends Bytecode { public final NameID name; private Lambda(Type.FunctionOrMethod type, int target, int[] operands, @@ -1304,8 +1315,13 @@ public int hashCode() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { - return Lambda(type(0), nTargets[0], nOperands, name); + public Type.FunctionOrMethod type(int i) { + return (Type.FunctionOrMethod) super.type(i); + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return Lambda((Type.FunctionOrMethod) type(0), nTargets[0], nOperands, name); } public boolean equals(Object o) { @@ -1328,10 +1344,11 @@ public String toString() { * @author David J. Pearce * */ - public static class Label implements Code { + public static class Label extends Bytecode { public final String label; private Label(String label) { + super(new Type[0],new int[0],new int[0]); this.label = label; } @@ -1349,7 +1366,7 @@ public Label relabel(Map labels) { } @Override - public Code remap(Map binding) { + public Bytecode remap(Map binding) { return this; } @@ -1372,9 +1389,14 @@ public String toString() { public void registers(Set register) { // TODO Auto-generated method stub } + + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return new Label(label); + } } - /** * Represents a block of code which loops continuously until e.g. a * conditional branch is taken out of the block. For example: @@ -1416,7 +1438,7 @@ public void registers(Set register) { * @author David J. Pearce * */ - public static class Loop extends AbstractCompoundBytecode { + public static class Loop extends Compound { private Loop(int[] targets, int block, int... operands) { super(block, new Type[0], targets, operands); @@ -1614,7 +1636,7 @@ public void remove() { * @author David J. Pearce * */ - public static final class Update extends AbstractBytecode + public static final class Update extends Bytecode implements Iterable { public final ArrayList fields; @@ -1734,7 +1756,7 @@ public Type rhs() { } @Override - public final Code clone(int[] nTargets, int[] nOperands) { + public final Bytecode clone(int[] nTargets, int[] nOperands) { return new Update(types, nTargets, nOperands, fields); } @@ -1790,7 +1812,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class Return extends AbstractBytecode { + public static final class Return extends Bytecode { private Return(Type[] types, int... operands) { super(types, new int[0], operands); @@ -1802,7 +1824,7 @@ public int opcode() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return new Return(Arrays.copyOf(types, types.length), nOperands); } @@ -1868,7 +1890,7 @@ public String toString() { * @author David J. Pearce * */ - public static final class Switch extends AbstractBytecode { + public static final class Switch extends Bytecode { public final ArrayList> branches; public final String defaultTarget; @@ -1928,7 +1950,7 @@ public String toString() { } @Override - public Code clone(int[] nTargets, int[] nOperands) { + public Bytecode clone(int[] nTargets, int[] nOperands) { return new Switch(types[0], nOperands[0], defaultTarget, branches); } diff --git a/modules/wyil/src/wyil/transforms/LoopVariants.java b/modules/wyil/src/wyil/transforms/LoopVariants.java index 214ce6b45f..05ab4399a7 100644 --- a/modules/wyil/src/wyil/transforms/LoopVariants.java +++ b/modules/wyil/src/wyil/transforms/LoopVariants.java @@ -8,7 +8,7 @@ import wybs.lang.Builder; import wycc.lang.Transform; import wyil.lang.CodeForest; -import wyil.lang.Code; +import wyil.lang.Bytecode; import wyil.lang.Codes; import wyil.lang.Type; import wyil.lang.WyilFile; @@ -113,16 +113,12 @@ protected BitSet infer(int blockID, CodeForest forest) { int size = block.size(); for (int i = 0; i < size; ++i) { CodeForest.Entry e = block.get(i); - Code code = e.code(); - - if (code instanceof Code.AbstractBytecode) { - Code.AbstractBytecode aa = (Code.AbstractBytecode) code; - for (int target : aa.targets()) { - modified.set(target); - } + Bytecode code = e.code(); + for (int target : code.targets()) { + modified.set(target); } - if (code instanceof Code.AbstractCompoundBytecode) { - Code.AbstractCompoundBytecode body = (Code.AbstractCompoundBytecode) code; + if (code instanceof Bytecode.Compound) { + Bytecode.Compound body = (Bytecode.Compound) code; BitSet loopModified = infer(body.block(), forest); if (code instanceof Codes.Quantify) { // Unset the modified status of the index operand, it is diff --git a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java index c9b70fbd16..8322e5666f 100755 --- a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java +++ b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java @@ -120,7 +120,7 @@ protected T propagate(int blockID, T store, List> handlers) { CodeForest.Block block = forest.get(blockID); for (int i = block.size()-1; i >= 0; --i) { - Code code = block.get(i).code(); + Bytecode code = block.get(i).code(); // Construct the bytecode index CodeForest.Index id = new CodeForest.Index(blockID,i); @@ -272,7 +272,7 @@ protected abstract T propagate(CodeForest.Index index, Codes.Loop code, T store, * statement. * @return */ - protected abstract T propagate(CodeForest.Index index, Code code, T store); + protected abstract T propagate(CodeForest.Index index, Bytecode code, T store); /** * Propagate from an exception handler. diff --git a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java index 94212ef240..1ad033912c 100755 --- a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java +++ b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java @@ -119,7 +119,7 @@ protected T propagate(int blockID, T store) { CodeForest.Block block = forest.get(blockID); for (int i = 0; i < block.size(); ++i) { - Code code = block.get(i).code(); + Bytecode code = block.get(i).code(); // Construct the bytecode ID CodeForest.Index id = new CodeForest.Index(blockID,i); @@ -297,7 +297,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract T propagate(CodeForest.Index index, Code code, T store); + protected abstract T propagate(CodeForest.Index index, Bytecode code, T store); /** * Determine the initial store for the current method case. diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index e4e222d1ee..a661f3d47a 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -153,7 +153,7 @@ private Object executeAllWithin(Constant[] frame, Context context) { * @return */ private Object execute(Constant[] frame, Context context) { - Code bytecode = context.forest.get(context.pc).code(); + Bytecode bytecode = context.forest.get(context.pc).code(); // FIXME: turn this into a switch statement? if (bytecode instanceof Codes.Invariant) { return execute((Codes.Invariant) bytecode, frame, context); @@ -982,7 +982,7 @@ public CodeForest.Index getLabel(String label) { return labels.get(label); } - public Code getBytecode() { + public Bytecode getBytecode() { return forest.get(pc).first(); } } diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index d3b84313e4..f41ea7f21b 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -1,6 +1,6 @@ package wyil.util.interpreter; -import wyil.lang.Code; +import wyil.lang.Bytecode; import wyil.lang.Codes; import wyil.lang.Constant; import wyil.lang.Type; @@ -21,26 +21,26 @@ public class StandardFunctions { public static final InternalFunction[] standardFunctions = new InternalFunction[255]; static { - standardFunctions[Code.OPCODE_assign] = new Assign(); - standardFunctions[Code.OPCODE_neg] = new Negate(); - standardFunctions[Code.OPCODE_arrayinvert] = new BitwiseInvert(); - standardFunctions[Code.OPCODE_dereference] = new Dereference(); - standardFunctions[Code.OPCODE_arraylength] = new ArrayLength(); - standardFunctions[Code.OPCODE_add ] = new Add(); - standardFunctions[Code.OPCODE_sub ] = new Subtract(); - standardFunctions[Code.OPCODE_mul ] = new Multiply(); - standardFunctions[Code.OPCODE_div ] = new Divide(); - standardFunctions[Code.OPCODE_rem ] = new Remainder(); - standardFunctions[Code.OPCODE_bitwiseor] = new BitwiseOr(); - standardFunctions[Code.OPCODE_bitwisexor] = new BitwiseXor(); - standardFunctions[Code.OPCODE_bitwiseand] = new BitwiseAnd(); - standardFunctions[Code.OPCODE_lshr] = new LeftShift(); - standardFunctions[Code.OPCODE_rshr] = new RightShift(); - standardFunctions[Code.OPCODE_arrayindex] = new ArrayIndex(); - standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); - standardFunctions[Code.OPCODE_array] = new ArrayConstructor(); - standardFunctions[Code.OPCODE_record] = new RecordConstructor(); - standardFunctions[Code.OPCODE_newobject] = new ObjectConstructor(); + standardFunctions[Bytecode.OPCODE_assign] = new Assign(); + standardFunctions[Bytecode.OPCODE_neg] = new Negate(); + standardFunctions[Bytecode.OPCODE_arrayinvert] = new BitwiseInvert(); + standardFunctions[Bytecode.OPCODE_dereference] = new Dereference(); + standardFunctions[Bytecode.OPCODE_arraylength] = new ArrayLength(); + standardFunctions[Bytecode.OPCODE_add ] = new Add(); + standardFunctions[Bytecode.OPCODE_sub ] = new Subtract(); + standardFunctions[Bytecode.OPCODE_mul ] = new Multiply(); + standardFunctions[Bytecode.OPCODE_div ] = new Divide(); + standardFunctions[Bytecode.OPCODE_rem ] = new Remainder(); + standardFunctions[Bytecode.OPCODE_bitwiseor] = new BitwiseOr(); + standardFunctions[Bytecode.OPCODE_bitwisexor] = new BitwiseXor(); + standardFunctions[Bytecode.OPCODE_bitwiseand] = new BitwiseAnd(); + standardFunctions[Bytecode.OPCODE_lshr] = new LeftShift(); + standardFunctions[Bytecode.OPCODE_rshr] = new RightShift(); + standardFunctions[Bytecode.OPCODE_arrayindex] = new ArrayIndex(); + standardFunctions[Bytecode.OPCODE_arrygen] = new ArrayGenerator(); + standardFunctions[Bytecode.OPCODE_array] = new ArrayConstructor(); + standardFunctions[Bytecode.OPCODE_record] = new RecordConstructor(); + standardFunctions[Bytecode.OPCODE_newobject] = new ObjectConstructor(); }; // ==================================================================================== diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 30618951da..f0ec1ef57b 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -51,6 +51,7 @@ import jasm.attributes.LineNumberTable; import jasm.attributes.SourceFile; import jasm.lang.*; +import jasm.lang.Bytecode; import jasm.lang.Bytecode.Goto; import jasm.lang.Modifier; import jasm.lang.Bytecode.Load; @@ -403,7 +404,7 @@ private void patchInvariantBlockHelper(String falseBranch, CodeForest.Block bloc for (int i = 0; i != block.size(); ++i) { // This is still a valid index CodeForest.Entry e = block.get(i); - Code c = e.code(); + wyil.lang.Bytecode c = e.code(); if (c instanceof Codes.Return) { // first patch point @@ -585,7 +586,7 @@ private void translate(CodeForest.Index pc, int freeSlot, CodeForest forest, Arr * The list of bytecodes being accumulated * @return */ - private int translate(CodeForest.Index pc, Code code, int freeSlot, CodeForest forest, + private int translate(CodeForest.Index pc, wyil.lang.Bytecode code, int freeSlot, CodeForest forest, ArrayList bytecodes) { try { diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java index e50ef8d3a2..f0747c21ef 100644 --- a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -2,6 +2,7 @@ import static jasm.lang.JvmTypes.*; import static wyjc.Wyil2JavaBuilder.*; +import static wyil.lang.Bytecode.*; import java.util.ArrayList; import java.util.Collections; @@ -9,7 +10,6 @@ import jasm.lang.Bytecode; import jasm.lang.JvmType; -import wyil.lang.Code; import wyil.lang.Codes; import wyil.lang.Type; import wyjc.Wyil2JavaBuilder.BytecodeTranslator; @@ -28,26 +28,26 @@ public class BytecodeTranslators { public static final BytecodeTranslator[] standardFunctions = new BytecodeTranslator[255]; static { - standardFunctions[Code.OPCODE_assign] = new Assign(); - standardFunctions[Code.OPCODE_neg] = new Negate(); - standardFunctions[Code.OPCODE_arrayinvert] = new Invert(); - standardFunctions[Code.OPCODE_dereference] = new Dereference(); - standardFunctions[Code.OPCODE_arraylength] = new ArrayLength(); - standardFunctions[Code.OPCODE_add ] = new Add(); - standardFunctions[Code.OPCODE_sub ] = new Subtract(); - standardFunctions[Code.OPCODE_mul ] = new Multiply(); - standardFunctions[Code.OPCODE_div ] = new Divide(); - standardFunctions[Code.OPCODE_rem ] = new Remainder(); - standardFunctions[Code.OPCODE_bitwiseor] = new BitwiseOr(); - standardFunctions[Code.OPCODE_bitwisexor] = new BitwiseXor(); - standardFunctions[Code.OPCODE_bitwiseand] = new BitwiseAnd(); - standardFunctions[Code.OPCODE_lshr] = new LeftShift(); - standardFunctions[Code.OPCODE_rshr] = new RightShift(); - standardFunctions[Code.OPCODE_arrayindex] = new ArrayIndex(); - standardFunctions[Code.OPCODE_arrygen] = new ArrayGenerator(); - standardFunctions[Code.OPCODE_array] = new ArrayConstructor(); - standardFunctions[Code.OPCODE_record] = new RecordConstructor(); - standardFunctions[Code.OPCODE_newobject] = new New(); + standardFunctions[OPCODE_assign] = new Assign(); + standardFunctions[OPCODE_neg] = new Negate(); + standardFunctions[OPCODE_arrayinvert] = new Invert(); + standardFunctions[OPCODE_dereference] = new Dereference(); + standardFunctions[OPCODE_arraylength] = new ArrayLength(); + standardFunctions[OPCODE_add ] = new Add(); + standardFunctions[OPCODE_sub ] = new Subtract(); + standardFunctions[OPCODE_mul ] = new Multiply(); + standardFunctions[OPCODE_div ] = new Divide(); + standardFunctions[OPCODE_rem ] = new Remainder(); + standardFunctions[OPCODE_bitwiseor] = new BitwiseOr(); + standardFunctions[OPCODE_bitwisexor] = new BitwiseXor(); + standardFunctions[OPCODE_bitwiseand] = new BitwiseAnd(); + standardFunctions[OPCODE_lshr] = new LeftShift(); + standardFunctions[OPCODE_rshr] = new RightShift(); + standardFunctions[OPCODE_arrayindex] = new ArrayIndex(); + standardFunctions[OPCODE_arrygen] = new ArrayGenerator(); + standardFunctions[OPCODE_array] = new ArrayConstructor(); + standardFunctions[OPCODE_record] = new RecordConstructor(); + standardFunctions[OPCODE_newobject] = new New(); }; From 5eec3a9704f8bfd8c5e0f20fb4b716d3b4a8ec6a Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 29 Mar 2016 19:58:28 +1300 Subject: [PATCH 20/43] Refactor Expr.LengthOf into Expr.UnOp #502 This is something of an experiment. Basically, I'm wondering how far I can push the refactoring done so far. In particular, can we make the Abstract Syntax Tree more uniform in the same way that we have done for WyIL bytecodes? I'd guess the answer is yes, but it probably needs a fair bit of work. I have succeeding in doing one example only so far, and it wasn't easy. --- .../wyc/src/wyc/builder/CodeGenerator.java | 13 +- .../wyc/src/wyc/builder/FlowTypeChecker.java | 178 ++++++++---------- modules/wyc/src/wyc/io/WhileyFileParser.java | 12 +- modules/wyc/src/wyc/io/WhileyFilePrinter.java | 13 +- modules/wyc/src/wyc/lang/Expr.java | 32 +--- modules/wyc/src/wyc/lang/Exprs.java | 8 - 6 files changed, 102 insertions(+), 154 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 080ebc67ee..de315704d3 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1720,8 +1720,6 @@ public int generate(Expr expression, Environment environment, CodeForest.Block b return generate((Expr.ArrayGenerator) expression, environment, block, forest, context); } else if (expression instanceof Expr.BinOp) { return generate((Expr.BinOp) expression, environment, block, forest, context); - } else if (expression instanceof Expr.LengthOf) { - return generate((Expr.LengthOf) expression, environment, block, forest, context); } else if (expression instanceof Expr.Dereference) { return generate((Expr.Dereference) expression, environment, block, forest, context); } else if (expression instanceof Expr.Cast) { @@ -1910,6 +1908,9 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b block.add(Codes.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); block.add(Codes.Label(exitLabel)); break; + case ARRAYLENGTH: + block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYLENGTH), attributes(expr)); + break; default: // should be dead-code internalFailure("unexpected unary operator encountered", context, expr); @@ -1918,14 +1919,6 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b return targets[0]; } - private int generate(Expr.LengthOf expr, Environment environment, CodeForest.Block block, CodeForest forest, - Context context) { - int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.ARRAYLENGTH), attributes(expr)); - return targets[0]; - } - private int generate(Expr.Dereference expr, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; diff --git a/modules/wyc/src/wyc/builder/FlowTypeChecker.java b/modules/wyc/src/wyc/builder/FlowTypeChecker.java index 5ac3e0cdfe..e7d75bf0cf 100644 --- a/modules/wyc/src/wyc/builder/FlowTypeChecker.java +++ b/modules/wyc/src/wyc/builder/FlowTypeChecker.java @@ -142,7 +142,7 @@ public class FlowTypeChecker { private final WhileyBuilder builder; private final TypeExpander expander; private String filename; - private WhileyFile.FunctionOrMethod current; + //private WhileyFile.FunctionOrMethod current; /** * The constant cache contains a cache of expanded constant values. This is @@ -244,8 +244,6 @@ public void propagate(WhileyFile.Constant cd) throws IOException, ResolveError { * @throws IOException */ public void propagate(WhileyFile.FunctionOrMethod d) throws IOException { - this.current = d; // ugly - // Resolve the types of all parameters and construct an appropriate // environment for use in the flow-sensitive type propagation. Environment environment = addDeclaredParameters(d.parameters, new Environment(), d); @@ -266,7 +264,7 @@ public void propagate(WhileyFile.FunctionOrMethod d) throws IOException { // Finally, propagate type information throughout all statements in the // function / method body. - Environment last = propagate(d.statements, environment); + Environment last = propagate(d.statements, environment, d); // checkReturnValue(d, last); } @@ -282,7 +280,7 @@ public void propagate(WhileyFile.FunctionOrMethod d) throws IOException { */ private void checkReturnValue(WhileyFile.FunctionOrMethod d, Environment last) { if (!d.hasModifier(Modifier.NATIVE) && last != BOTTOM - && !current.resolvedType().returns().isEmpty()) { + && !d.resolvedType().returns().isEmpty()) { // In this case, code reaches the end of the function or method and, // furthermore, that this requires a return value. To get here means // that there was no explicit return statement given on at least one @@ -322,14 +320,14 @@ private void propagateConditions(List conditions, Environment environment, * this block * @return */ - private Environment propagate(ArrayList block, Environment environment) { + private Environment propagate(ArrayList block, Environment environment, Context context) { for (int i = 0; i != block.size(); ++i) { Stmt stmt = block.get(i); if (stmt instanceof Expr) { - block.set(i, (Stmt) propagate((Expr) stmt, environment, current)); + block.set(i, (Stmt) propagate((Expr) stmt, environment, context)); } else { - environment = propagate(stmt, environment); + environment = propagate(stmt, environment, context); } } @@ -350,36 +348,36 @@ private Environment propagate(ArrayList block, Environment environment) { * this block * @return */ - private Environment propagate(Stmt stmt, Environment environment) { + private Environment propagate(Stmt stmt, Environment environment, Context context) { if (environment == BOTTOM) { syntaxError(errorMessage(UNREACHABLE_CODE), filename, stmt); return null; // dead code } try { if (stmt instanceof Stmt.VariableDeclaration) { - return propagate((Stmt.VariableDeclaration) stmt, environment); + return propagate((Stmt.VariableDeclaration) stmt, environment, context); } else if (stmt instanceof Stmt.Assign) { - return propagate((Stmt.Assign) stmt, environment); + return propagate((Stmt.Assign) stmt, environment, context); } else if (stmt instanceof Stmt.Return) { - return propagate((Stmt.Return) stmt, environment); + return propagate((Stmt.Return) stmt, environment, context); } else if (stmt instanceof Stmt.IfElse) { - return propagate((Stmt.IfElse) stmt, environment); + return propagate((Stmt.IfElse) stmt, environment, context); } else if (stmt instanceof Stmt.While) { - return propagate((Stmt.While) stmt, environment); + return propagate((Stmt.While) stmt, environment, context); } else if (stmt instanceof Stmt.Switch) { - return propagate((Stmt.Switch) stmt, environment); + return propagate((Stmt.Switch) stmt, environment, context); } else if (stmt instanceof Stmt.DoWhile) { - return propagate((Stmt.DoWhile) stmt, environment); + return propagate((Stmt.DoWhile) stmt, environment, context); } else if (stmt instanceof Stmt.Break) { - return propagate((Stmt.Break) stmt, environment); + return propagate((Stmt.Break) stmt, environment, context); } else if (stmt instanceof Stmt.Continue) { - return propagate((Stmt.Continue) stmt, environment); + return propagate((Stmt.Continue) stmt, environment, context); } else if (stmt instanceof Stmt.Assert) { - return propagate((Stmt.Assert) stmt, environment); + return propagate((Stmt.Assert) stmt, environment, context); } else if (stmt instanceof Stmt.Assume) { - return propagate((Stmt.Assume) stmt, environment); + return propagate((Stmt.Assume) stmt, environment, context); } else if (stmt instanceof Stmt.Fail) { - return propagate((Stmt.Fail) stmt, environment); + return propagate((Stmt.Fail) stmt, environment, context); } else if (stmt instanceof Stmt.Debug) { return propagate((Stmt.Debug) stmt, environment); } else if (stmt instanceof Stmt.Skip) { @@ -410,8 +408,8 @@ private Environment propagate(Stmt stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Assert stmt, Environment environment) { - stmt.expr = propagate(stmt.expr, environment, current); + private Environment propagate(Stmt.Assert stmt, Environment environment, Context context) { + stmt.expr = propagate(stmt.expr, environment, context); checkIsSubtype(Type.T_BOOL, stmt.expr); return environment; } @@ -427,8 +425,8 @@ private Environment propagate(Stmt.Assert stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Assume stmt, Environment environment) { - stmt.expr = propagate(stmt.expr, environment, current); + private Environment propagate(Stmt.Assume stmt, Environment environment, Context context) { + stmt.expr = propagate(stmt.expr, environment, context); checkIsSubtype(Type.T_BOOL, stmt.expr); return environment; } @@ -444,7 +442,7 @@ private Environment propagate(Stmt.Assume stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Fail stmt, Environment environment) { + private Environment propagate(Stmt.Fail stmt, Environment environment, Context context) { return BOTTOM; } @@ -464,23 +462,23 @@ private Environment propagate(Stmt.Fail stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.VariableDeclaration stmt, Environment environment) + private Environment propagate(Stmt.VariableDeclaration stmt, Environment environment, Context context) throws IOException, ResolveError { // First, resolve declared type - stmt.type = resolveAsType(stmt.parameter.type, current); + stmt.type = resolveAsType(stmt.parameter.type, context); // Second, resolve type of initialiser. This must be performed before we // update the environment, since this expression is not allowed to refer // to the newly declared variable. if (stmt.expr != null) { - stmt.expr = propagate(stmt.expr, environment, current); + stmt.expr = propagate(stmt.expr, environment, context); checkIsSubtype(stmt.type, stmt.expr); } // Third, update environment accordingly. Observe that we can safely // assume any variable(s) are not already declared in the enclosing // scope because the parser checks this for us. - environment = addDeclaredParameter(stmt.parameter, environment, current); + environment = addDeclaredParameter(stmt.parameter, environment, context); // Fourth, set the current type of the assigned variable if an // initialiser is used. This is because the current type may differ @@ -503,10 +501,10 @@ private Environment propagate(Stmt.VariableDeclaration stmt, Environment environ * this block * @return */ - private Environment propagate(Stmt.Assign stmt, Environment environment) throws IOException, ResolveError { + private Environment propagate(Stmt.Assign stmt, Environment environment, Context context) throws IOException, ResolveError { // First, type check each lval that occurs on the left-hand side. for(int i=0;i!=stmt.lvals.size();++i) { - stmt.lvals.set(i, propagate(stmt.lvals.get(i), environment)); + stmt.lvals.set(i, propagate(stmt.lvals.get(i), environment, context)); } // Second, type check expressions on right-hand side, and calculate the // number of values produced by the right-hand side. This is challenging @@ -514,7 +512,7 @@ private Environment propagate(Stmt.Assign stmt, Environment environment) throws // than the number of values produced. This occurs when an invocation // occurs on the right-hand side has multiple return values. for (int i = 0; i != stmt.rvals.size(); ++i) { - stmt.rvals.set(i, propagate(stmt.rvals.get(i), environment, current)); + stmt.rvals.set(i, propagate(stmt.rvals.get(i), environment, context)); } List> valuesProduced = calculateTypesProduced(stmt.rvals); // Check the number of expected values matches the number of values @@ -578,7 +576,7 @@ private Expr.AssignedVariable inferAfterType(Expr.LVal lv, Nominal afterType) { * this block * @return */ - private Environment propagate(Stmt.Break stmt, Environment environment) { + private Environment propagate(Stmt.Break stmt, Environment environment, Context context) { // FIXME: need to propagate environment to the break destination return BOTTOM; } @@ -595,7 +593,7 @@ private Environment propagate(Stmt.Break stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Continue stmt, Environment environment) { + private Environment propagate(Stmt.Continue stmt, Environment environment, Context context) { // FIXME: need to propagate environment to the continue destination return BOTTOM; } @@ -611,8 +609,8 @@ private Environment propagate(Stmt.Continue stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Debug stmt, Environment environment) { - stmt.expr = propagate(stmt.expr, environment, current); + private Environment propagate(Stmt.Debug stmt, Environment environment, Context context) { + stmt.expr = propagate(stmt.expr, environment, context); checkIsSubtype(Type.Array(Type.T_INT, false), stmt.expr); return environment; } @@ -627,16 +625,16 @@ private Environment propagate(Stmt.Debug stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.DoWhile stmt, Environment environment) { + private Environment propagate(Stmt.DoWhile stmt, Environment environment, Context context) { // Iterate to a fixed point - environment = computeFixedPoint(environment, stmt.body, stmt.condition, true, stmt); + environment = computeFixedPoint(environment, stmt.body, stmt.condition, true, stmt, context); // Type invariants List stmt_invariants = stmt.invariants; for (int i = 0; i != stmt_invariants.size(); ++i) { Expr invariant = stmt_invariants.get(i); - invariant = propagate(invariant, environment, current); + invariant = propagate(invariant, environment, context); stmt_invariants.set(i, invariant); checkIsSubtype(Type.T_BOOL, invariant); } @@ -644,7 +642,7 @@ private Environment propagate(Stmt.DoWhile stmt, Environment environment) { // Type condition assuming its false to represent the terminated loop. // This is important if the condition contains a type test, as we'll // know that doesn't hold here. - Pair p = propagateCondition(stmt.condition, false, environment, current); + Pair p = propagateCondition(stmt.condition, false, environment, context); stmt.condition = p.first(); environment = p.second(); @@ -691,13 +689,13 @@ private Environment propagate(Stmt.DoWhile stmt, Environment environment) { * @return */ - private Environment propagate(Stmt.IfElse stmt, Environment environment) { + private Environment propagate(Stmt.IfElse stmt, Environment environment, Context context) { // First, check condition and apply variable retypings. Pair p1, p2; - p1 = propagateCondition(stmt.condition, true, environment.clone(), current); - p2 = propagateCondition(stmt.condition, false, environment.clone(), current); + p1 = propagateCondition(stmt.condition, true, environment.clone(), context); + p2 = propagateCondition(stmt.condition, false, environment.clone(), context); stmt.condition = p1.first(); Environment trueEnvironment = p1.second(); @@ -705,13 +703,13 @@ private Environment propagate(Stmt.IfElse stmt, Environment environment) { // Second, update environments for true and false branches if (stmt.trueBranch != null && stmt.falseBranch != null) { - trueEnvironment = propagate(stmt.trueBranch, trueEnvironment); - falseEnvironment = propagate(stmt.falseBranch, falseEnvironment); + trueEnvironment = propagate(stmt.trueBranch, trueEnvironment, context); + falseEnvironment = propagate(stmt.falseBranch, falseEnvironment, context); } else if (stmt.trueBranch != null) { - trueEnvironment = propagate(stmt.trueBranch, trueEnvironment); + trueEnvironment = propagate(stmt.trueBranch, trueEnvironment, context); } else if (stmt.falseBranch != null) { trueEnvironment = environment; - falseEnvironment = propagate(stmt.falseBranch, falseEnvironment); + falseEnvironment = propagate(stmt.falseBranch, falseEnvironment, context); } // Finally, join results back together @@ -732,13 +730,14 @@ private Environment propagate(Stmt.IfElse stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Return stmt, Environment environment) throws IOException { + private Environment propagate(Stmt.Return stmt, Environment environment, Context context) throws IOException { List stmt_returns = stmt.returns; for(int i=0;i!=stmt_returns.size();++i) { - stmt_returns.set(i, propagate(stmt_returns.get(i), environment, current)); + stmt_returns.set(i, propagate(stmt_returns.get(i), environment, context)); } List> stmt_types = calculateTypesProduced(stmt_returns); - List current_returns = current.resolvedType().returns(); + // FIXME: this is less than ideal + List current_returns = ((WhileyFile.FunctionOrMethod)context).resolvedType().returns(); if (stmt_types.size() < current_returns.size()) { // In this case, a return statement was provided with too few return @@ -827,9 +826,9 @@ private Environment propagate(Stmt.Skip stmt, Environment environment) { * this block * @return */ - private Environment propagate(Stmt.Switch stmt, Environment environment) throws IOException { + private Environment propagate(Stmt.Switch stmt, Environment environment, Context context) throws IOException { - stmt.expr = propagate(stmt.expr, environment, current); + stmt.expr = propagate(stmt.expr, environment, context); Environment finalEnv = null; boolean hasDefault = false; @@ -840,14 +839,14 @@ private Environment propagate(Stmt.Switch stmt, Environment environment) throws ArrayList values = new ArrayList(); for (Expr e : c.expr) { - values.add(resolveAsConstant(e, current).first()); + values.add(resolveAsConstant(e, context).first()); } c.constants = values; // second, propagate through the statements Environment localEnv = environment.clone(); - localEnv = propagate(c.stmts, localEnv); + localEnv = propagate(c.stmts, localEnv, context); if (finalEnv == null) { finalEnv = localEnv; @@ -884,16 +883,16 @@ private Environment propagate(Stmt.Switch stmt, Environment environment) throws * this block * @return */ - private Environment propagate(Stmt.While stmt, Environment environment) { + private Environment propagate(Stmt.While stmt, Environment environment, Context context) { // Determine typing at beginning of loop - environment = computeFixedPoint(environment, stmt.body, stmt.condition, false, stmt); + environment = computeFixedPoint(environment, stmt.body, stmt.condition, false, stmt, context); // Type loop invariant(s) List stmt_invariants = stmt.invariants; for (int i = 0; i != stmt_invariants.size(); ++i) { Expr invariant = stmt_invariants.get(i); - invariant = propagate(invariant, environment, current); + invariant = propagate(invariant, environment, context); stmt_invariants.set(i, invariant); checkIsSubtype(Type.T_BOOL, invariant); } @@ -901,7 +900,7 @@ private Environment propagate(Stmt.While stmt, Environment environment) { // Type condition assuming its false to represent the terminated loop. // This is important if the condition contains a type test, as we'll // know that doesn't hold here. - Pair p = propagateCondition(stmt.condition, false, environment, current); + Pair p = propagateCondition(stmt.condition, false, environment, context); stmt.condition = p.first(); environment = p.second(); @@ -912,7 +911,7 @@ private Environment propagate(Stmt.While stmt, Environment environment) { // LVals // ========================================================================= - private Expr.LVal propagate(Expr.LVal lval, Environment environment) { + private Expr.LVal propagate(Expr.LVal lval, Environment environment, Context context) { try { if (lval instanceof Expr.AbstractVariable) { Expr.AbstractVariable av = (Expr.AbstractVariable) lval; @@ -925,27 +924,23 @@ private Expr.LVal propagate(Expr.LVal lval, Environment environment) { return lv; } else if (lval instanceof Expr.Dereference) { Expr.Dereference pa = (Expr.Dereference) lval; - Expr.LVal src = propagate((Expr.LVal) pa.src, environment); + Expr.LVal src = propagate((Expr.LVal) pa.src, environment, context); pa.src = src; pa.srcType = expandAsReference(src.result()); return pa; } else if (lval instanceof Expr.IndexOf) { // this indicates either a list, string or dictionary update Expr.IndexOf ai = (Expr.IndexOf) lval; - Expr.LVal src = propagate((Expr.LVal) ai.src, environment); - Expr index = propagate(ai.index, environment, current); + Expr.LVal src = propagate((Expr.LVal) ai.src, environment, context); + Expr index = propagate(ai.index, environment, context); ai.src = src; ai.index = index; - Nominal.Array srcType = expandAsEffectiveList(src.result()); - if (srcType == null) { - syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), filename, lval); - } - ai.srcType = srcType; + ai.srcType = expandAsEffectiveArray(src,context); return ai; } else if (lval instanceof Expr.FieldAccess) { // this indicates a record update Expr.FieldAccess ad = (Expr.FieldAccess) lval; - Expr.LVal src = propagate((Expr.LVal) ad.src, environment); + Expr.LVal src = propagate((Expr.LVal) ad.src, environment, context); Expr.FieldAccess ra = new Expr.FieldAccess(src, ad.name, ad.attributes()); Nominal.Record srcType = expandAsEffectiveRecord(src.result()); if (srcType == null) { @@ -1383,8 +1378,6 @@ public Expr propagate(Expr expr, Environment environment, Context context) { return propagate((Expr.IndexOf) expr, environment, context); } else if (expr instanceof Expr.Lambda) { return propagate((Expr.Lambda) expr, environment, context); - } else if (expr instanceof Expr.LengthOf) { - return propagate((Expr.LengthOf) expr, environment, context); } else if (expr instanceof Expr.LocalVariable) { return propagate((Expr.LocalVariable) expr, environment, context); } else if (expr instanceof Expr.ArrayInitialiser) { @@ -1490,7 +1483,7 @@ private Expr propagate(Expr.BinOp expr, Environment environment, Context context return expr; } - private Expr propagate(Expr.UnOp expr, Environment environment, Context context) throws IOException { + private Expr propagate(Expr.UnOp expr, Environment environment, Context context) throws IOException, ResolveError { if (expr.op == Expr.UOp.NOT) { // hand off to special method for conditions @@ -1507,7 +1500,10 @@ private Expr propagate(Expr.UnOp expr, Environment environment, Context context) case INVERT: checkIsSubtype(Type.T_BYTE, src, context); break; - + case ARRAYLENGTH: { + expr.type = expandAsEffectiveArray(expr.mhs, context); + return expr; + } default: internalFailure("unknown operator: " + expr.op.getClass().getName(), context, expr); } @@ -1709,7 +1705,7 @@ private Expr propagate(Expr.IndexOf expr, Environment environment, Context conte throws IOException, ResolveError { expr.src = propagate(expr.src, environment, context); expr.index = propagate(expr.index, environment, context); - Nominal.Array srcType = expandAsEffectiveList(expr.src.result()); + Nominal.Array srcType = expandAsEffectiveArray(expr.src, context); if (srcType == null) { syntaxError(errorMessage(INVALID_ARRAY_EXPRESSION), context, expr.src); @@ -1722,22 +1718,6 @@ private Expr propagate(Expr.IndexOf expr, Environment environment, Context conte return expr; } - private Expr propagate(Expr.LengthOf expr, Environment environment, Context context) - throws IOException, ResolveError { - expr.src = propagate(expr.src, environment, context); - - Nominal.Array srcType = expandAsEffectiveList(expr.src.result()); - - if (srcType == null) { - syntaxError("found " + expr.src.result().nominal() + ", expected string, set, list or dictionary.", context, - expr.src); - } else { - expr.srcType = srcType; - } - - return expr; - } - private Expr propagate(Expr.LocalVariable expr, Environment environment, Context context) throws IOException { Nominal type = environment.getCurrentType(expr.var); expr.type = type; @@ -1921,7 +1901,7 @@ private List> calculateTypesProduced(List expressions) * @return */ private Environment computeFixedPoint(Environment environment, ArrayList body, Expr condition, - boolean doWhile, SyntacticElement element) { + boolean doWhile, SyntacticElement element, Context context) { // The count is used simply to guarantee termination. int count = 0; // The original environment is an exact copy of the initial environment. @@ -1945,14 +1925,14 @@ private Environment computeFixedPoint(Environment environment, ArrayList b // Second, propagate through condition (if applicable). This may // update the environment if one or more type tests are used. if (condition != null && !doWhile) { - tmp = propagateCondition(condition, true, old.clone(), current).second(); + tmp = propagateCondition(condition, true, old.clone(), context).second(); } else { tmp = old; doWhile = false; } // Merge updated environment with original environment to produce // potentially updated environment. - environment = original.merge(variables, propagate(body, tmp)); + environment = original.merge(variables, propagate(body, tmp, context)); old.free(); // hacky, but safe // Finally, check loop count to force termination if (count++ == 10) { @@ -3142,7 +3122,9 @@ private Constant.Array evaluate(Expr.ArrayGenerator bop, Constant element, Const // expandAsType // ========================================================================= - public Nominal.Array expandAsEffectiveList(Nominal lhs) throws IOException, ResolveError { + public Nominal.Array expandAsEffectiveArray(Expr src, Context context) + throws IOException, ResolveError { + Nominal lhs = src.result(); Type raw = lhs.raw(); if (raw instanceof Type.EffectiveArray) { Type nominal = expandOneLevel(lhs.nominal()); @@ -3150,8 +3132,9 @@ public Nominal.Array expandAsEffectiveList(Nominal lhs) throws IOException, Reso nominal = raw; // discard nominal information } return (Nominal.Array) Nominal.construct(nominal, raw); - } else { - return null; + } else { + syntaxError(errorMessage(INVALID_ARRAY_EXPRESSION), context, src); + return null; // dead code } } @@ -3170,8 +3153,7 @@ public Nominal.Record expandAsEffectiveRecord(Nominal lhs) throws IOException, R nominal = (Type) raw; // discard nominal information } return (Nominal.Record) Nominal.construct(nominal, raw); - } - { + } else { return null; } } diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java index 0bd3dbfe16..54df62a686 100644 --- a/modules/wyc/src/wyc/io/WhileyFileParser.java +++ b/modules/wyc/src/wyc/io/WhileyFileParser.java @@ -2877,7 +2877,7 @@ private Expr parseLengthOfExpression(WhileyFile wf, // only. However, the expression is guaranteed to be terminated by '|'. Expr e = parseShiftExpression(wf, environment, true); match(VerticalBar); - return new Expr.LengthOf(e, sourceAttr(start, index - 1)); + return new Expr.UnOp(Expr.UOp.ARRAYLENGTH, e, sourceAttr(start, index - 1)); } /** @@ -3438,9 +3438,13 @@ private boolean mustParseAsExpr(Expr e) { return false; } else if(e instanceof Expr.UnOp) { Expr.UnOp uop = (Expr.UnOp) e; - if(uop.op == Expr.UOp.NOT) { + switch(uop.op) { + case NOT: return mustParseAsExpr(uop.mhs); - } else { + case ARRAYLENGTH: + case INVERT: + return true; + default: return false; } } else if(e instanceof Expr.AbstractFunctionOrMethod) { @@ -3461,8 +3465,6 @@ private boolean mustParseAsExpr(Expr e) { return true; } else if(e instanceof Expr.Lambda) { return true; - } else if(e instanceof Expr.LengthOf) { - return true; } else if(e instanceof Expr.ArrayInitialiser) { return true; } else if(e instanceof Expr.New) { diff --git a/modules/wyc/src/wyc/io/WhileyFilePrinter.java b/modules/wyc/src/wyc/io/WhileyFilePrinter.java index f651ada6f4..c24e0fe2b1 100644 --- a/modules/wyc/src/wyc/io/WhileyFilePrinter.java +++ b/modules/wyc/src/wyc/io/WhileyFilePrinter.java @@ -339,8 +339,6 @@ public void print(Expr expression) { print ((Expr.ArrayInitialiser) expression); } else if (expression instanceof Expr.BinOp) { print ((Expr.BinOp) expression); - } else if (expression instanceof Expr.LengthOf) { - print ((Expr.LengthOf) expression); } else if (expression instanceof Expr.Dereference) { print ((Expr.Dereference) expression); } else if (expression instanceof Expr.Cast) { @@ -412,12 +410,6 @@ public void print(Expr.BinOp e) { printWithBrackets(e.rhs, Expr.BinOp.class, Expr.Cast.class); } - public void print(Expr.LengthOf e) { - out.print("|"); - print(e.src); - out.print("|"); - } - public void print(Expr.Dereference e) { out.print("*"); print(e.src); @@ -448,6 +440,11 @@ public void print(Expr.UnOp e) { case INVERT: out.print("~"); break; + case ARRAYLENGTH: + out.print("|"); + print(e.mhs); + out.print("|"); + return; } printWithBrackets(e.mhs,Expr.BinOp.class,Expr.Cast.class); } diff --git a/modules/wyc/src/wyc/lang/Expr.java b/modules/wyc/src/wyc/lang/Expr.java index 8209281bab..17c43059d6 100755 --- a/modules/wyc/src/wyc/lang/Expr.java +++ b/modules/wyc/src/wyc/lang/Expr.java @@ -351,7 +351,8 @@ public String toString() { public enum UOp { NOT, NEG, - INVERT + INVERT, + ARRAYLENGTH } public static class UnOp extends SyntacticElement.Impl implements Expr { @@ -366,7 +367,11 @@ public UnOp(UOp op, Expr mhs, Attribute... attributes) { } public Nominal result() { - return type; + if(op == UOp.ARRAYLENGTH) { + return Nominal.T_INT; + } else { + return type; + } } public String toString() { @@ -766,29 +771,6 @@ public Nominal.FunctionOrMethod type() { } } - public static class LengthOf extends SyntacticElement.Impl implements Expr { - public Expr src; - public Nominal.Array srcType; - - public LengthOf(Expr mhs, Attribute... attributes) { - super(attributes); - this.src = mhs; - } - - public LengthOf(Expr mhs, Collection attributes) { - super(attributes); - this.src = mhs; - } - - public Nominal result() { - return Nominal.T_INT; - } - - public String toString() { - return "|" + src.toString() + "|"; - } - } - public static class New extends SyntacticElement.Impl implements Expr,Stmt { public Expr expr; public Nominal.Reference type; diff --git a/modules/wyc/src/wyc/lang/Exprs.java b/modules/wyc/src/wyc/lang/Exprs.java index a83ffa0999..f4bf00b24f 100644 --- a/modules/wyc/src/wyc/lang/Exprs.java +++ b/modules/wyc/src/wyc/lang/Exprs.java @@ -45,10 +45,6 @@ private static void uses(Expr expr, Context context, HashSet> uses(e.lhs, context, uses); uses(e.rhs, context, uses); - } else if (expr instanceof Expr.LengthOf) { - Expr.LengthOf e = (Expr.LengthOf) expr; - uses(e.src, context, uses); - } else if (expr instanceof Expr.Dereference) { Expr.Dereference e = (Expr.Dereference) expr; uses(e.src, context, uses); @@ -159,10 +155,6 @@ public static boolean isPure(Expr expr, Context context) { Expr.BinOp e = (Expr.BinOp) expr; return isPure(e.lhs, context) && isPure(e.rhs, context); - } else if (expr instanceof Expr.LengthOf) { - Expr.LengthOf e = (Expr.LengthOf) expr; - return isPure(e.src, context); - } else if (expr instanceof Expr.Dereference) { Expr.Dereference e = (Expr.Dereference) expr; return isPure(e.src, context); From 976bdd522a0356b771650d5b250bbc54ed47b7d0 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Wed, 30 Mar 2016 18:50:29 +1300 Subject: [PATCH 21/43] Merged Codes into Bytecode #502 Having everything centered around the Bytecode class is not ideal, but I think it's more consistent and looks better. Realistically, we need a better language to get a better effect :) --- .../wyc/src/wyc/builder/CodeGenerator.java | 216 +- .../src/wyil/builders/VcExprGenerator.java | 71 +- .../wyil/src/wyil/builders/VcGenerator.java | 82 +- modules/wyil/src/wyil/builders/VcUtils.java | 31 +- .../wyil/src/wyil/checks/CoercionCheck.java | 4 +- .../wyil/checks/DefiniteAssignmentCheck.java | 14 +- modules/wyil/src/wyil/checks/ModuleCheck.java | 6 +- modules/wyil/src/wyil/io/WyilFilePrinter.java | 2 +- modules/wyil/src/wyil/io/WyilFileReader.java | 108 +- modules/wyil/src/wyil/io/WyilFileWriter.java | 62 +- modules/wyil/src/wyil/lang/Bytecode.java | 2025 ++++++++++++++++- modules/wyil/src/wyil/lang/CodeUtils.java | 35 +- modules/wyil/src/wyil/lang/Codes.java | 1974 ---------------- .../src/wyil/transforms/LoopVariants.java | 13 +- .../wyil/util/dfa/BackwardFlowAnalysis.java | 36 +- .../wyil/util/dfa/ForwardFlowAnalysis.java | 38 +- .../wyil/util/interpreter/Interpreter.java | 130 +- .../util/interpreter/StandardFunctions.java | 3 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 155 +- .../src/wyjc/util/BytecodeTranslators.java | 42 +- 20 files changed, 2485 insertions(+), 2562 deletions(-) delete mode 100644 modules/wyil/src/wyil/lang/Codes.java diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index de315704d3..ff1de6a10e 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -259,7 +259,7 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw // The following is sneaky. It guarantees that every method ends in a // return. For methods that actually need a value, this is either // removed as dead-code or remains and will cause an error. - body.add(Codes.Return(), attributes(fd)); + body.add(Bytecode.Return(), attributes(fd)); WyilFile.FunctionOrMethod declaration; @@ -294,9 +294,9 @@ private int generateInvariantBlock(Expr invariant, Environment environment, Code int index = forest.add(precondition); String endLab = CodeUtils.freshLabel(); generateCondition(endLab, invariant, environment, precondition, forest, context); - precondition.add(Codes.Fail(), attributes(invariant)); - precondition.add(Codes.Label(endLab)); - precondition.add(Codes.Return()); + precondition.add(Bytecode.Fail(), attributes(invariant)); + precondition.add(Bytecode.Label(endLab)); + precondition.add(Bytecode.Return()); return index; } @@ -451,7 +451,7 @@ private void generate(VariableDeclaration s, Environment environment, CodeForest // Second, translate initialiser expression if it exists. if (s.expr != null) { int[] operands = { generate(s.expr, environment, block, forest, context) }; - block.add(Codes.Operator(s.expr.result().raw(), targets, operands, Codes.OperatorKind.ASSIGN), + block.add(Bytecode.Operator(s.expr.result().raw(), targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(s)); } } @@ -545,7 +545,7 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme // for variable on the left-hand side. int[] targets = new int[] { environment.get(v.var) }; int[] operands = new int[] { operand }; - block.add(Codes.Operator(type, targets, operands, Codes.OperatorKind.ASSIGN), attributes(lval)); + block.add(Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); } else if (lval instanceof Expr.IndexOf || lval instanceof Expr.FieldAccess || lval instanceof Expr.Dereference) { // This is the more complicated case, since the left-hand side @@ -558,7 +558,7 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme ArrayList operands = new ArrayList(); Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, environment, block, forest, context); int target = environment.get(lhs.var); - block.add(Codes.Update(lhs.type.raw(), target, operands, operand, lhs.afterType.raw(), fields), + block.add(Bytecode.Update(lhs.type.raw(), target, operands, operand, lhs.afterType.raw(), fields), attributes(lval)); } else { WhileyFile.syntaxError("invalid assignment", context, lval); @@ -641,10 +641,10 @@ private void generate(Stmt.Assert s, Environment environment, CodeForest.Block b int body = forest.add(subblock); String endLab = CodeUtils.freshLabel(); generateCondition(endLab, s.expr, environment, subblock, forest, context); - subblock.add(Codes.Fail(), attributes(s.expr)); - subblock.add(Codes.Label(endLab)); + subblock.add(Bytecode.Fail(), attributes(s.expr)); + subblock.add(Bytecode.Label(endLab)); // Second, create assert bytecode - block.add(Codes.Assert(body), attributes(s)); + block.add(Bytecode.Assert(body), attributes(s)); } @@ -670,10 +670,10 @@ private void generate(Stmt.Assume s, Environment environment, CodeForest.Block b int body = forest.add(subblock); String endLab = CodeUtils.freshLabel(); generateCondition(endLab, s.expr, environment, subblock, forest, context); - subblock.add(Codes.Fail(), attributes(s.expr)); - subblock.add(Codes.Label(endLab)); + subblock.add(Bytecode.Fail(), attributes(s.expr)); + subblock.add(Bytecode.Label(endLab)); // Second, create assert bytecode - block.add(Codes.Assume(body), attributes(s)); + block.add(Bytecode.Assume(body), attributes(s)); } /** @@ -732,7 +732,7 @@ private void generate(Stmt.Return s, Environment environment, CodeForest.Block b operands[index++] = generate(e, environment, block, forest, context); } } - block.add(Codes.Return(types, operands), attributes(s)); + block.add(Bytecode.Return(types, operands), attributes(s)); } /** @@ -791,7 +791,7 @@ private void generate(Stmt.Skip s, Environment environment, CodeForest.Block blo private void generate(Stmt.Debug s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { int operand = generate(s.expr, environment, block, forest, context); - block.add(Codes.Debug(operand), attributes(s)); + block.add(Bytecode.Debug(operand), attributes(s)); } /** @@ -821,7 +821,7 @@ private void generate(Stmt.Debug s, Environment environment, CodeForest.Block bl */ private void generate(Stmt.Fail s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - block.add(Codes.Fail(), attributes(s)); + block.add(Bytecode.Fail(), attributes(s)); } /** @@ -881,14 +881,14 @@ private void generate(Stmt.IfElse s, Environment environment, CodeForest.Block b generate(st, environment, block, forest, context); } if (!s.falseBranch.isEmpty()) { - block.add(Codes.Goto(exitLab)); - block.add(Codes.Label(falseLab)); + block.add(Bytecode.Goto(exitLab)); + block.add(Bytecode.Label(falseLab)); for (Stmt st : s.falseBranch) { generate(st, environment, block, forest, context); } } - block.add(Codes.Label(exitLab)); + block.add(Bytecode.Label(exitLab)); } /** @@ -942,7 +942,7 @@ private void generate(Stmt.Break s, Environment environment, CodeForest.Block bl if (scope == null) { WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context, s); } - block.add(Codes.Goto(scope.breakLabel)); + block.add(Bytecode.Goto(scope.breakLabel)); } /** @@ -999,7 +999,7 @@ private void generate(Stmt.Continue s, Environment environment, CodeForest.Block if (scope == null) { WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context, s); } - block.add(Codes.Goto(scope.continueLabel)); + block.add(Bytecode.Goto(scope.continueLabel)); } /** @@ -1074,16 +1074,16 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context, c); } else { defaultTarget = CodeUtils.freshLabel(); - block.add(Codes.Label(defaultTarget), attributes(c)); + block.add(Bytecode.Label(defaultTarget), attributes(c)); for (Stmt st : c.stmts) { generate(st, environment, block, forest, context); } - block.add(Codes.Goto(exitLab), attributes(c)); + block.add(Bytecode.Goto(exitLab), attributes(c)); } } else if (defaultTarget == exitLab) { String target = CodeUtils.freshLabel(); - block.add(Codes.Label(target), attributes(c)); + block.add(Bytecode.Label(target), attributes(c)); // Case statements in Whiley may have multiple matching constant // values. Therefore, we iterate each matching value and @@ -1103,7 +1103,7 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b for (Stmt st : c.stmts) { generate(st, environment, block, forest, context); } - block.add(Codes.Goto(exitLab), attributes(c)); + block.add(Bytecode.Goto(exitLab), attributes(c)); } else { // This represents the case where we have another non-default @@ -1113,8 +1113,8 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b } } - block.add(start, Codes.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); - block.add(Codes.Label(exitLab), attributes(s)); + block.add(start, Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); + block.add(Bytecode.Label(exitLab), attributes(s)); } /** @@ -1173,7 +1173,7 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl for (Expr condition : s.invariants) { int invariant = generateInvariantBlock(condition, environment, forest, context); - bodyBlock.add(Codes.Invariant(invariant), attributes(condition)); + bodyBlock.add(Bytecode.Invariant(invariant), attributes(condition)); } generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); @@ -1184,9 +1184,9 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl } scopes.pop(); // break - bodyBlock.add(Codes.Label(continueLab), attributes(s)); - block.add(Codes.Loop(new int[] {}, body), attributes(s)); - block.add(Codes.Label(exitLab), attributes(s)); + bodyBlock.add(Bytecode.Label(continueLab), attributes(s)); + block.add(Bytecode.Loop(new int[] {}, body), attributes(s)); + block.add(Bytecode.Label(exitLab), attributes(s)); } /** @@ -1252,14 +1252,14 @@ private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block for (Expr condition : s.invariants) { int invariant = generateInvariantBlock(condition, environment, forest, context); - bodyBlock.add(Codes.Invariant(invariant), attributes(condition)); + bodyBlock.add(Bytecode.Invariant(invariant), attributes(condition)); } - bodyBlock.add(Codes.Label(continueLab), attributes(s)); + bodyBlock.add(Bytecode.Label(continueLab), attributes(s)); generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); - block.add(Codes.Loop(new int[] {}, body), attributes(s)); - block.add(Codes.Label(exitLab), attributes(s)); + block.add(Bytecode.Loop(new int[] {}, body), attributes(s)); + block.add(Bytecode.Label(exitLab), attributes(s)); } // ========================================================================= @@ -1343,8 +1343,8 @@ public void generateCondition(String target, Expr condition, Environment environ int r1 = generate(condition, environment, block, forest, context); int r2 = environment.allocate(Type.T_BOOL); - block.add(Codes.Const(r2, Constant.V_BOOL(true)), attributes(condition)); - block.add(Codes.If(Type.T_BOOL, r1, r2, Codes.Comparator.EQ, target), attributes(condition)); + block.add(Bytecode.Const(r2, Constant.V_BOOL(true)), attributes(condition)); + block.add(Bytecode.If(Type.T_BOOL, r1, r2, Bytecode.Comparator.EQ, target), attributes(condition)); } else { syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, condition); @@ -1390,7 +1390,7 @@ private void generateCondition(String target, Expr.Constant c, Environment envir CodeForest forest, Context context) { Constant.Bool b = (Constant.Bool) c.value; if (b.value) { - block.add(Codes.Goto(target)); + block.add(Bytecode.Goto(target)); } else { // do nout } @@ -1429,16 +1429,16 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm String exitLabel = CodeUtils.freshLabel(); generateCondition(exitLabel, invert(v.lhs), environment, block, forest, context); generateCondition(target, v.rhs, environment, block, forest, context); - block.add(Codes.Label(exitLabel)); + block.add(Bytecode.Label(exitLabel)); } else if (bop == Expr.BOp.IS) { generateTypeCondition(target, v, environment, block, forest, context); } else { - Codes.Comparator cop = OP2COP(bop, v, context); + Bytecode.Comparator cop = OP2COP(bop, v, context); - if (cop == Codes.Comparator.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant + if (cop == Bytecode.Comparator.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; @@ -1446,8 +1446,8 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); } int slot = environment.get(lhs.var); - block.add(Codes.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); - } else if (cop == Codes.Comparator.NEQ && v.lhs instanceof Expr.LocalVariable + block.add(Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); + } else if (cop == Bytecode.Comparator.NEQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. String exitLabel = CodeUtils.freshLabel(); @@ -1456,13 +1456,13 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); } int slot = environment.get(lhs.var); - block.add(Codes.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); - block.add(Codes.Goto(target)); - block.add(Codes.Label(exitLabel)); + block.add(Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); + block.add(Bytecode.Goto(target)); + block.add(Bytecode.Label(exitLabel)); } else { int lhs = generate(v.lhs, environment, block, forest, context); int rhs = generate(v.rhs, environment, block, forest, context); - block.add(Codes.If(v.srcType.raw(), lhs, rhs, cop, target), attributes(v)); + block.add(Bytecode.If(v.srcType.raw(), lhs, rhs, cop, target), attributes(v)); } } } @@ -1517,7 +1517,7 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Environm // following cast is always safe. Expr.TypeVal rhs = (Expr.TypeVal) condition.rhs; - block.add(Codes.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); + block.add(Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); } /** @@ -1554,8 +1554,8 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme String label = CodeUtils.freshLabel(); generateCondition(label, v.mhs, environment, block, forest, context); - block.add(Codes.Goto(target)); - block.add(Codes.Label(label)); + block.add(Bytecode.Goto(target)); + block.add(Bytecode.Label(label)); return; default: // Nothing else is a valud boolean condition here. @@ -1592,14 +1592,14 @@ private void generateCondition(String target, Expr.Quantifier e, Environment env switch (e.cop) { case NONE: - block.add(Codes.Goto(target)); - block.add(Codes.Label(exit)); + block.add(Bytecode.Goto(target)); + block.add(Bytecode.Label(exit)); break; case SOME: break; case ALL: - block.add(Codes.Goto(target)); - block.add(Codes.Label(exit)); + block.add(Bytecode.Goto(target)); + block.add(Bytecode.Label(exit)); break; } } @@ -1621,7 +1621,7 @@ private void generate(Iterator> srcIterator, String t int body = forest.add(bodyBlock); generate(srcIterator, trueLabel, falseLabel, e, environment, bodyBlock, forest, context); // Finally, create the forall loop bytecode - block.add(Codes.Quantify(startSlot, endSlot, varSlot, new int[0], body), attributes(e)); + block.add(Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], body), attributes(e)); } else { // This is the base case (i.e. the innermost loop) switch (e.cop) { @@ -1675,7 +1675,7 @@ public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment CodeForest forest, Context context, int... targets) throws ResolveError { // int[] operands = generate(expr.arguments, environment, block, forest, context); - block.add(Codes.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); + block.add(Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); } public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, CodeForest.Block block, @@ -1683,7 +1683,7 @@ public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment env // int operand = generate(expr.src, environment, block, forest, context); int[] operands = generate(expr.arguments, environment, block, forest, context); - block.add(Codes.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); + block.add(Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); } // ========================================================================= @@ -1779,7 +1779,7 @@ private int generate(Expr.Constant expr, Environment environment, CodeForest.Blo Context context) { Constant val = expr.value; int target = environment.allocate(val.type()); - block.add(Codes.Const(target, expr.value), attributes(expr)); + block.add(Bytecode.Const(target, expr.value), attributes(expr)); return target; } @@ -1788,7 +1788,7 @@ private int generate(Expr.FunctionOrMethod expr, Environment environment, CodeFo Type.FunctionOrMethod rawType = expr.type.raw(); Type.FunctionOrMethod nominalType = expr.type.nominal(); int target = environment.allocate(rawType); - block.add(Codes.Lambda(nominalType, target, Collections.EMPTY_LIST, expr.nid), attributes(expr)); + block.add(Bytecode.Lambda(nominalType, target, Collections.EMPTY_LIST, expr.nid), attributes(expr)); return target; } @@ -1825,10 +1825,10 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block CodeForest.Block bodyBlock = new CodeForest.Block(); bodyForest.addAsRoot(bodyBlock); if (tfm.returns().isEmpty()) { - bodyBlock.add(Codes.Return(), attributes(expr)); + bodyBlock.add(Bytecode.Return(), attributes(expr)); } else { int target = generate(expr.body, benv, bodyBlock, bodyForest, context); - bodyBlock.add(Codes.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), + bodyBlock.add(Bytecode.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), attributes(expr)); } @@ -1860,7 +1860,7 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block // Finally, create the lambda int target = environment.allocate(tfm); - block.add(Codes.Lambda(cfm, target, operands, nid), attributes(expr)); + block.add(Bytecode.Lambda(cfm, target, operands, nid), attributes(expr)); return target; } @@ -1868,7 +1868,7 @@ private int generate(Expr.ConstantAccess expr, Environment environment, CodeFore Context context) throws ResolveError { Constant val = expr.value; int target = environment.allocate(val.type()); - block.add(Codes.Const(target, val), attributes(expr)); + block.add(Bytecode.Const(target, val), attributes(expr)); return target; } @@ -1891,25 +1891,25 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b int[] targets = new int[] { environment.allocate(expr.result().raw()) }; switch (expr.op) { case NEG: - block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.NEG), + block.add(Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), attributes(expr)); break; case INVERT: - block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.BITWISEINVERT), + block.add(Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), attributes(expr)); break; case NOT: String falseLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); generateCondition(falseLabel, expr.mhs, environment, block, forest, context); - block.add(Codes.Const(targets[0], Constant.V_BOOL(true)), attributes(expr)); - block.add(Codes.Goto(exitLabel)); - block.add(Codes.Label(falseLabel)); - block.add(Codes.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); - block.add(Codes.Label(exitLabel)); + block.add(Bytecode.Const(targets[0], Constant.V_BOOL(true)), attributes(expr)); + block.add(Bytecode.Goto(exitLabel)); + block.add(Bytecode.Label(falseLabel)); + block.add(Bytecode.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); + block.add(Bytecode.Label(exitLabel)); break; case ARRAYLENGTH: - block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYLENGTH), attributes(expr)); + block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); break; default: // should be dead-code @@ -1923,7 +1923,7 @@ private int generate(Expr.Dereference expr, Environment environment, CodeForest. Context context) { int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.DEREFERENCE), + block.add(Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), attributes(expr)); return targets[0]; } @@ -1933,7 +1933,7 @@ private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Bloc int[] operands = { generate(expr.src, environment, block, forest, context), generate(expr.index, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.srcType.raw(), targets, operands, Codes.OperatorKind.ARRAYINDEX), attributes(expr)); + block.add(Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); return targets[0]; } @@ -1943,7 +1943,7 @@ private int generate(Expr.Cast expr, Environment environment, CodeForest.Block b Type from = expr.expr.result().raw(); Type to = expr.result().raw(); int target = environment.allocate(to); - block.add(Codes.Convert(from, target, operand, to), attributes(expr)); + block.add(Bytecode.Convert(from, target, operand, to), attributes(expr)); return target; } @@ -1957,11 +1957,11 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo String exitLabel = CodeUtils.freshLabel(); generateCondition(trueLabel, v, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(v)); - block.add(Codes.Goto(exitLabel)); - block.add(Codes.Label(trueLabel)); - block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(v)); - block.add(Codes.Label(exitLabel)); + block.add(Bytecode.Const(target, Constant.V_BOOL(false)), attributes(v)); + block.add(Bytecode.Goto(exitLabel)); + block.add(Bytecode.Label(trueLabel)); + block.add(Bytecode.Const(target, Constant.V_BOOL(true)), attributes(v)); + block.add(Bytecode.Label(exitLabel)); return target; } else { @@ -1972,7 +1972,7 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo generate(v.rhs, environment, block, forest, context) }; - block.add(Codes.Operator(result, targets, operands, OP2BOP(v.op, v, context)), + block.add(Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context)), attributes(v)); return targets[0]; @@ -1983,7 +1983,7 @@ private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeFo Context context) { int[] operands = generate(expr.arguments, environment, block, forest, context); int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYCONSTRUCTOR), + block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), attributes(expr)); return targets[0]; } @@ -1993,7 +1993,7 @@ private int generate(Expr.ArrayGenerator expr, Environment environment, CodeFore int[] operands = new int[] { generate(expr.element, environment, block, forest, context), generate(expr.count, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.ARRAYGENERATOR), attributes(expr)); + block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); return targets[0]; } @@ -2003,11 +2003,11 @@ private int generate(Expr.Quantifier e, Environment environment, CodeForest.Bloc String exitLabel = CodeUtils.freshLabel(); generateCondition(trueLabel, e, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - block.add(Codes.Const(target, Constant.V_BOOL(false)), attributes(e)); - block.add(Codes.Goto(exitLabel)); - block.add(Codes.Label(trueLabel)); - block.add(Codes.Const(target, Constant.V_BOOL(true)), attributes(e)); - block.add(Codes.Label(exitLabel)); + block.add(Bytecode.Const(target, Constant.V_BOOL(false)), attributes(e)); + block.add(Bytecode.Goto(exitLabel)); + block.add(Bytecode.Label(trueLabel)); + block.add(Bytecode.Const(target, Constant.V_BOOL(true)), attributes(e)); + block.add(Bytecode.Label(exitLabel)); return target; } @@ -2022,7 +2022,7 @@ private int generate(Expr.Record expr, Environment environment, CodeForest.Block operands[i] = generate(arg, environment, block, forest, context); } int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.result().raw(), targets, operands, Codes.OperatorKind.RECORDCONSTRUCTOR), + block.add(Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), attributes(expr)); return targets[0]; } @@ -2031,7 +2031,7 @@ private int generate(Expr.FieldAccess expr, Environment environment, CodeForest. Context context) { int operand = generate(expr.src, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - block.add(Codes.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), + block.add(Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), attributes(expr)); return target; } @@ -2040,7 +2040,7 @@ private int generate(Expr.New expr, Environment environment, CodeForest.Block bl Context context) throws ResolveError { int[] operands = new int[] { generate(expr.expr, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Codes.Operator(expr.type.raw(), targets, operands, Codes.OperatorKind.NEW)); + block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); return targets[0]; } @@ -2058,28 +2058,28 @@ private int[] generate(List arguments, Environment environment, CodeForest // Helpers // ========================================================================= - private Codes.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context context) { + private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context context) { switch (bop) { case ADD: - return Codes.OperatorKind.ADD; + return Bytecode.OperatorKind.ADD; case SUB: - return Codes.OperatorKind.SUB; + return Bytecode.OperatorKind.SUB; case MUL: - return Codes.OperatorKind.MUL; + return Bytecode.OperatorKind.MUL; case DIV: - return Codes.OperatorKind.DIV; + return Bytecode.OperatorKind.DIV; case REM: - return Codes.OperatorKind.REM; + return Bytecode.OperatorKind.REM; case BITWISEAND: - return Codes.OperatorKind.BITWISEAND; + return Bytecode.OperatorKind.BITWISEAND; case BITWISEOR: - return Codes.OperatorKind.BITWISEOR; + return Bytecode.OperatorKind.BITWISEOR; case BITWISEXOR: - return Codes.OperatorKind.BITWISEXOR; + return Bytecode.OperatorKind.BITWISEXOR; case LEFTSHIFT: - return Codes.OperatorKind.LEFTSHIFT; + return Bytecode.OperatorKind.LEFTSHIFT; case RIGHTSHIFT: - return Codes.OperatorKind.RIGHTSHIFT; + return Bytecode.OperatorKind.RIGHTSHIFT; default: syntaxError(errorMessage(INVALID_BINARY_EXPRESSION), context, elem); } @@ -2087,20 +2087,20 @@ private Codes.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context c return null; } - private Codes.Comparator OP2COP(Expr.BOp bop, SyntacticElement elem, Context context) { + private Bytecode.Comparator OP2COP(Expr.BOp bop, SyntacticElement elem, Context context) { switch (bop) { case EQ: - return Codes.Comparator.EQ; + return Bytecode.Comparator.EQ; case NEQ: - return Codes.Comparator.NEQ; + return Bytecode.Comparator.NEQ; case LT: - return Codes.Comparator.LT; + return Bytecode.Comparator.LT; case LTEQ: - return Codes.Comparator.LTEQ; + return Bytecode.Comparator.LTEQ; case GT: - return Codes.Comparator.GT; + return Bytecode.Comparator.GT; case GTEQ: - return Codes.Comparator.GTEQ; + return Bytecode.Comparator.GTEQ; default: syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, elem); } diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index caf1e47cf4..795bc0672b 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -22,7 +22,6 @@ import wyfs.util.Trie; import wyil.lang.Bytecode; import wyil.lang.CodeForest; -import wyil.lang.Codes; import wyil.lang.Type; import wyil.lang.WyilFile; import wyil.util.ErrorMessages; @@ -50,26 +49,26 @@ public VcExprGenerator(String filename, Builder builder, VcUtils utils) { */ public void transform(Bytecode code, CodeForest forest, VcBranch branch) { try { - if (code instanceof Codes.Operator) { - transform((Codes.Operator) code, forest, branch); - } else if (code instanceof Codes.Convert) { - transform((Codes.Convert) code, forest, branch); - } else if (code instanceof Codes.Const) { - transform((Codes.Const) code, forest, branch); - } else if (code instanceof Codes.Debug) { + if (code instanceof Bytecode.Operator) { + transform((Bytecode.Operator) code, forest, branch); + } else if (code instanceof Bytecode.Convert) { + transform((Bytecode.Convert) code, forest, branch); + } else if (code instanceof Bytecode.Const) { + transform((Bytecode.Const) code, forest, branch); + } else if (code instanceof Bytecode.Debug) { // skip - } else if (code instanceof Codes.FieldLoad) { - transform((Codes.FieldLoad) code, forest, branch); - } else if (code instanceof Codes.IndirectInvoke) { - transform((Codes.IndirectInvoke) code, forest, branch); - } else if (code instanceof Codes.Invoke) { - transform((Codes.Invoke) code, forest, branch); - } else if (code instanceof Codes.Label) { + } else if (code instanceof Bytecode.FieldLoad) { + transform((Bytecode.FieldLoad) code, forest, branch); + } else if (code instanceof Bytecode.IndirectInvoke) { + transform((Bytecode.IndirectInvoke) code, forest, branch); + } else if (code instanceof Bytecode.Invoke) { + transform((Bytecode.Invoke) code, forest, branch); + } else if (code instanceof Bytecode.Label) { // skip - } else if (code instanceof Codes.Update) { - transform((Codes.Update) code, forest, branch); - } else if (code instanceof Codes.Lambda) { - transform((Codes.Lambda) code, forest, branch); + } else if (code instanceof Bytecode.Update) { + transform((Bytecode.Update) code, forest, branch); + } else if (code instanceof Bytecode.Lambda) { + transform((Bytecode.Lambda) code, forest, branch); } else { internalFailure("unknown: " + code.getClass().getName(), filename, forest.get(branch.pc()).attributes()); @@ -114,7 +113,7 @@ public void transform(Bytecode code, CodeForest forest, VcBranch branch) { null // right shift }; - protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch) { + protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch branch) { switch(code.kind) { case ASSIGN: for (int i = 0; i != code.operands().length; ++i) { @@ -123,7 +122,7 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch break; case NEG: case ARRAYLENGTH: { - Codes.Operator bc = (Codes.Operator) code; + Bytecode.Operator bc = (Bytecode.Operator) code; transformUnary(unaryOperatorMap[code.kind.ordinal()], bc, branch, forest); break; } @@ -170,24 +169,24 @@ protected void transform(Codes.Operator code, CodeForest forest, VcBranch branch } } - protected void transform(Codes.Convert code, CodeForest forest, VcBranch branch) { + protected void transform(Bytecode.Convert code, CodeForest forest, VcBranch branch) { Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()); Expr result = branch.read(code.operand(0)); SyntacticType type = utils.convert(code.result(), forest.get(branch.pc()).attributes()); branch.write(code.target(0), new Expr.Cast(type, result, attributes)); } - protected void transform(Codes.Const code, CodeForest forest, VcBranch branch) { + protected void transform(Bytecode.Const code, CodeForest forest, VcBranch branch) { Value val = utils.convert(code.constant, forest, branch); branch.write(code.target(), new Expr.Constant(val, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); } - protected void transform(Codes.Debug code, CodeForest forest, + protected void transform(Bytecode.Debug code, CodeForest forest, VcBranch branch) { // do nout } - protected void transform(Codes.FieldLoad code, CodeForest forest, VcBranch branch) { + protected void transform(Bytecode.FieldLoad code, CodeForest forest, VcBranch branch) { Type.EffectiveRecord er = (Type.EffectiveRecord) code.type(0); ArrayList fields = new ArrayList(er.fields().keySet()); Collections.sort(fields); @@ -197,14 +196,14 @@ protected void transform(Codes.FieldLoad code, CodeForest forest, VcBranch branc branch.write(code.target(0), result); } - protected void transform(Codes.IndirectInvoke code, + protected void transform(Bytecode.IndirectInvoke code, CodeForest forest, VcBranch branch) { for(int target : code.targets()) { branch.havoc(target); } } - protected void transform(Codes.Invoke code, CodeForest forest, + protected void transform(Bytecode.Invoke code, CodeForest forest, VcBranch branch) throws Exception { Collection attributes = forest.get(branch.pc()).attributes(); Collection wyccAttributes = VcUtils.toWycsAttributes(attributes); @@ -256,7 +255,7 @@ protected void transform(Codes.Invoke code, CodeForest forest, } } - protected void transformArrayGenerator(Codes.Operator code, CodeForest forest, VcBranch branch) { + protected void transformArrayGenerator(Bytecode.Operator code, CodeForest forest, VcBranch branch) { Type elementType = ((Type.Array) code.type(0)).element(); Collection wyilAttributes = forest.get(branch.pc()).attributes(); Collection attributes = VcUtils.toWycsAttributes(wyilAttributes); @@ -271,27 +270,27 @@ protected void transformArrayGenerator(Codes.Operator code, CodeForest forest, V branch.assume(macro); } - protected void transform(Codes.Lambda code, CodeForest forest, VcBranch branch) { + protected void transform(Bytecode.Lambda code, CodeForest forest, VcBranch branch) { // TODO: implement lambdas somehow? branch.havoc(code.target(0)); } - protected void transform(Codes.Update code, CodeForest forest, VcBranch branch) { + protected void transform(Bytecode.Update code, CodeForest forest, VcBranch branch) { Expr result = branch.read(code.result()); Expr oldSource = branch.read(code.target(0)); Expr newSource = branch.havoc(code.target(0)); updateHelper(code.iterator(), oldSource, newSource, result, branch, forest); } - protected void updateHelper(Iterator iter, Expr oldSource, Expr newSource, Expr result, VcBranch branch, + protected void updateHelper(Iterator iter, Expr oldSource, Expr newSource, Expr result, VcBranch branch, CodeForest forest) { Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()); if (!iter.hasNext()) { branch.assume(new Expr.Binary(Expr.Binary.Op.EQ, newSource, result, attributes)); } else { - Codes.LVal lv = iter.next(); - if (lv instanceof Codes.RecordLVal) { - Codes.RecordLVal rlv = (Codes.RecordLVal) lv; + Bytecode.LVal lv = iter.next(); + if (lv instanceof Bytecode.RecordLVal) { + Bytecode.RecordLVal rlv = (Bytecode.RecordLVal) lv; ArrayList fields = new ArrayList(rlv.rawType().fields().keySet()); Collections.sort(fields); int index = fields.indexOf(rlv.field); @@ -305,8 +304,8 @@ protected void updateHelper(Iterator iter, Expr oldSource, Expr newS updateHelper(iter, oldS, newS, result, branch, forest); } } - } else if (lv instanceof Codes.ArrayLVal) { - Codes.ArrayLVal rlv = (Codes.ArrayLVal) lv; + } else if (lv instanceof Bytecode.ArrayLVal) { + Bytecode.ArrayLVal rlv = (Bytecode.ArrayLVal) lv; Expr index = branch.read(rlv.indexOperand); Expr oldS = new Expr.IndexOf(oldSource, index, attributes); Expr newS = new Expr.IndexOf(newSource, index, attributes); diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java index bf40dc7d82..0ef272b4c6 100644 --- a/modules/wyil/src/wyil/builders/VcGenerator.java +++ b/modules/wyil/src/wyil/builders/VcGenerator.java @@ -246,7 +246,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) { List attributes = forest.get(branch.pc()).attributes(); Collection wycsAttributes = VcUtils.toWycsAttributes(attributes); // Find the return statement in question - Codes.Return ret = (Codes.Return) forest.get(branch.pc()).code(); + Bytecode.Return ret = (Bytecode.Return) forest.get(branch.pc()).code(); // Construct verification check to ensure that return // type invariant holds // FIXME: need proper support for multiple returns @@ -424,8 +424,8 @@ protected Pair> transform(int block, int offset, CodeFo Bytecode code = forest.get(pc).code(); // Now, dispatch statements. Control statements are treated // specially from unit statements. - if (code instanceof Codes.AssertOrAssume) { - if (breakOnInvariant && code instanceof Codes.Invariant) { + if (code instanceof Bytecode.AssertOrAssume) { + if (breakOnInvariant && code instanceof Bytecode.Invariant) { // In this special case, we have reached the invariant // bytecode and, hence, we break out of this loop. This // is needed for handling loop invariants where we need @@ -433,43 +433,43 @@ protected Pair> transform(int block, int offset, CodeFo // encountered. fallThruBranches.add(branch); } else { - boolean isAssert = code instanceof Codes.Assert; - Pair> p = transform((Codes.AssertOrAssume) code, isAssert, branch, + boolean isAssert = code instanceof Bytecode.Assert; + Pair> p = transform((Bytecode.AssertOrAssume) code, isAssert, branch, environment, labels, forest); if(p.first() != null) { worklist.add(p.first()); } worklist.addAll(p.second()); } - } else if (code instanceof Codes.If - || code instanceof Codes.IfIs - || code instanceof Codes.Switch + } else if (code instanceof Bytecode.If + || code instanceof Bytecode.IfIs + || code instanceof Bytecode.Switch || code instanceof Bytecode.Compound) { List bs; - if (code instanceof Codes.If) { - bs = transform((Codes.If) code, branch, labels, forest); - } else if (code instanceof Codes.IfIs) { - bs = transform((Codes.IfIs) code, branch, labels, forest); - } else if (code instanceof Codes.Switch) { - bs = transform((Codes.Switch) code, branch, labels, + if (code instanceof Bytecode.If) { + bs = transform((Bytecode.If) code, branch, labels, forest); + } else if (code instanceof Bytecode.IfIs) { + bs = transform((Bytecode.IfIs) code, branch, labels, forest); + } else if (code instanceof Bytecode.Switch) { + bs = transform((Bytecode.Switch) code, branch, labels, forest); - } else if (code instanceof Codes.Quantify) { - bs = transform((Codes.Quantify) code, branch, + } else if (code instanceof Bytecode.Quantify) { + bs = transform((Bytecode.Quantify) code, branch, isInvariant, environment, labels, forest); } else { - bs = transform((Codes.Loop) code, branch, environment, + bs = transform((Bytecode.Loop) code, branch, environment, labels, forest); } worklist.addAll(bs); - } else if (code instanceof Codes.Goto) { - transform((Codes.Goto) code, branch, labels, forest); + } else if (code instanceof Bytecode.Goto) { + transform((Bytecode.Goto) code, branch, labels, forest); worklist.push(branch); - } else if (code instanceof Codes.Return) { - transform((Codes.Return) code, branch); + } else if (code instanceof Bytecode.Return) { + transform((Bytecode.Return) code, branch); exitBranches.add(branch); - } else if (code instanceof Codes.Fail) { - transform((Codes.Fail) code, branch, forest); + } else if (code instanceof Bytecode.Fail) { + transform((Bytecode.Fail) code, branch, forest); exitBranches.add(branch); } else { // Unit statement. First, check whether or not there are any @@ -615,7 +615,7 @@ private void joinAll(ArrayList branches) { * The enclosing code block. This is needed to access source * location information. */ - protected List transform(Codes.Loop code, VcBranch branch, + protected List transform(Bytecode.Loop code, VcBranch branch, Type[] environment, Map labels, CodeForest forest) { return transformLoopHelper(code, branch, environment, labels, forest).second(); @@ -649,7 +649,7 @@ protected List transform(Codes.Loop code, VcBranch branch, * @param block * The block being transformed over. */ - protected List transform(Codes.Quantify code, VcBranch branch, + protected List transform(Bytecode.Quantify code, VcBranch branch, boolean isInvariant, Type[] environment, Map labels, CodeForest forest) { // Write an arbitrary value to the index operand. This is necessary to @@ -678,7 +678,7 @@ protected List transform(Codes.Quantify code, VcBranch branch, * @param exitBranches * @return */ - protected List extractQuantifiers(Codes.Quantify code, + protected List extractQuantifiers(Bytecode.Quantify code, VcBranch root, VcBranch fallThru, List exitBranches) { // First, setup some helper variables for use in the remainder. SyntacticType elementType = utils.convert(Type.T_INT, @@ -735,7 +735,7 @@ protected List extractQuantifiers(Codes.Quantify code, * @return */ protected Pair> transformQuantifierHelper( - Codes.Loop code, VcBranch branch, boolean isInvariant, + Bytecode.Loop code, VcBranch branch, boolean isInvariant, Type[] environment, Map labels, CodeForest forest) { // The loopPc gives the block index of the loop bytecode. @@ -773,7 +773,7 @@ protected Pair> transformQuantifierHelper( * location information. * @return */ - protected Pair> transformLoopHelper(Codes.Loop code, VcBranch branch, Type[] environment, + protected Pair> transformLoopHelper(Bytecode.Loop code, VcBranch branch, Type[] environment, Map labels, CodeForest forest) { // The loopPc gives the block index of the loop bytecode. CodeForest.Index loopPc = branch.pc(); @@ -790,7 +790,7 @@ protected Pair> transformLoopHelper(Codes.Loop code, Vc CodeForest.Block block = forest.get(code.block()); int numberOfInvariants = 0; for (int i = invariantOffset; i < block.size() - && block.get(i).first() instanceof Codes.Invariant; ++i) { + && block.get(i).first() instanceof Bytecode.Invariant; ++i) { numberOfInvariants = numberOfInvariants+1; } // @@ -901,7 +901,7 @@ protected Pair> transformLoopHelper(Codes.Loop code, Vc * @return */ protected Pair> transformLoopWithoutInvariant( - Codes.Loop code, VcBranch branch, Type[] environment, + Bytecode.Loop code, VcBranch branch, Type[] environment, Map labels, CodeForest forest) { CodeForest.Index loopPc = branch.pc(); // This is the easy case, as there is no loop invariant. Therefore, @@ -943,7 +943,7 @@ protected Pair> transformLoopWithoutInvariant( */ private void buildInvariantMacro(CodeForest.Index invariantPC, boolean[] variables, Type[] environment, CodeForest forest) { - Codes.Invariant code = (Codes.Invariant) forest.get(invariantPC).first(); + Bytecode.Invariant code = (Bytecode.Invariant) forest.get(invariantPC).first(); // FIXME: we don't need to include all variables, only those which are // "active". ArrayList types = new ArrayList(); @@ -996,10 +996,10 @@ protected Expr.Invoke buildInvariantCall(VcBranch branch, String name, * @param branch * @return */ - private int getInvariantOffset(Codes.Loop loop, CodeForest forest) { + private int getInvariantOffset(Bytecode.Loop loop, CodeForest forest) { CodeForest.Block block = forest.get(loop.block()); for (int i = 0; i != block.size(); ++i) { - if (block.get(i).first() instanceof Codes.Invariant) { + if (block.get(i).first() instanceof Bytecode.Invariant) { return i; } } @@ -1049,7 +1049,7 @@ public void havocVariables(int[] variables, VcBranch branch) { * @param branches * The list of branches currently being managed. */ - protected List transform(Codes.If code, VcBranch branch, + protected List transform(Bytecode.If code, VcBranch branch, Map labels, CodeForest forest) { // First, clone and register the true branch VcBranch trueBranch = branch.fork(); @@ -1086,7 +1086,7 @@ protected List transform(Codes.If code, VcBranch branch, * @param branches * The list of branches currently being managed. */ - protected List transform(Codes.IfIs code, VcBranch branch, + protected List transform(Bytecode.IfIs code, VcBranch branch, Map labels, CodeForest forest) { ArrayList exitBranches = new ArrayList(); // In this case, both branches are reachable. @@ -1127,7 +1127,7 @@ protected List transform(Codes.IfIs code, VcBranch branch, * @param branches * The list of branches currently being managed. */ - protected List transform(Codes.Switch code, VcBranch branch, + protected List transform(Bytecode.Switch code, VcBranch branch, Map labels, CodeForest forest) { ArrayList exitBranches = new ArrayList(); VcBranch defaultBranch = branch.fork(); @@ -1193,7 +1193,7 @@ protected List transform(Codes.Switch code, VcBranch branch, * branches which have terminated or failed. */ protected Pair> transform( - Codes.AssertOrAssume code, boolean isAssert, VcBranch branch, + Bytecode.AssertOrAssume code, boolean isAssert, VcBranch branch, Type[] environment, Map labels, CodeForest forest) { int start = wyalFile.declarations().size(); @@ -1248,7 +1248,7 @@ protected Pair> transform( * @param branches * The list of branches currently being managed. */ - protected void transform(Codes.Goto code, final VcBranch branch, + protected void transform(Bytecode.Goto code, final VcBranch branch, Map labels, CodeForest forest) { branch.goTo(labels.get(code.destination())); } @@ -1269,7 +1269,7 @@ protected void transform(Codes.Goto code, final VcBranch branch, * @param branches * The list of branches currently being managed. */ - protected void transform(Codes.Fail code, VcBranch branch, + protected void transform(Bytecode.Fail code, VcBranch branch, CodeForest forest) { // Update status of this branch to failed. This simply indicates that // this branch's location should be unreachable and, hence, we need a @@ -1291,7 +1291,7 @@ protected void transform(Codes.Fail code, VcBranch branch, * @param branches * The list of branches currently being managed. */ - protected void transform(Codes.Return code, VcBranch branch) { + protected void transform(Bytecode.Return code, VcBranch branch) { // Marking the branch as terminated indicates that it is no longer // active. Thus, the original callers of this block transformation can // subsequently extract the constraints which hold at the point of the @@ -1564,7 +1564,7 @@ private Expr generateAssumptionsHelper(VcBranch b, VcBranch end) { * @param elem * @return */ - private Expr.Binary buildTest(Codes.Comparator cop, int leftOperand, + private Expr.Binary buildTest(Bytecode.Comparator cop, int leftOperand, int rightOperand, Type type, CodeForest forest, VcBranch branch) { Expr lhs = branch.read(leftOperand); diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java index d6a6c3b1cd..176d218af0 100644 --- a/modules/wyil/src/wyil/builders/VcUtils.java +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -28,7 +28,6 @@ import wyil.attributes.VariableDeclarations; import wyil.lang.Bytecode; import wyil.lang.CodeForest; -import wyil.lang.Codes; import wyil.lang.Constant; import wyil.lang.Type; import wyil.lang.WyilFile; @@ -368,15 +367,15 @@ public Pair[] getPreconditions(Bytecode code, VcBranch branch, switch (code.opcode()) { case Bytecode.OPCODE_div: case Bytecode.OPCODE_rem: - return divideByZeroCheck((Codes.Operator) code, branch); + return divideByZeroCheck((Bytecode.Operator) code, branch); case Bytecode.OPCODE_arrayindex: - return indexOutOfBoundsChecks((Codes.Operator) code, branch); + return indexOutOfBoundsChecks((Bytecode.Operator) code, branch); case Bytecode.OPCODE_arrygen: - return arrayGeneratorChecks((Codes.Operator) code, branch); + return arrayGeneratorChecks((Bytecode.Operator) code, branch); case Bytecode.OPCODE_update: - return updateChecks((Codes.Update) code, branch); + return updateChecks((Bytecode.Update) code, branch); case Bytecode.OPCODE_invoke: - return preconditionCheck((Codes.Invoke) code, branch, environment, forest); + return preconditionCheck((Bytecode.Invoke) code, branch, environment, forest); } return new Pair[0]; } catch (Exception e) { @@ -395,7 +394,7 @@ public Pair[] getPreconditions(Bytecode code, VcBranch branch, * --- The branch the division is on. * @return */ - public Pair[] divideByZeroCheck(Codes.Operator binOp, VcBranch branch) { + public Pair[] divideByZeroCheck(Bytecode.Operator binOp, VcBranch branch) { Expr rhs = branch.read(binOp.operand(1)); Value zero; if (binOp.type(0) instanceof Type.Int) { @@ -419,7 +418,7 @@ public Pair[] divideByZeroCheck(Codes.Operator binOp, VcBranch bra * --- The branch the bytecode is on. * @return */ - public Pair[] indexOutOfBoundsChecks(Codes.Operator code, VcBranch branch) { + public Pair[] indexOutOfBoundsChecks(Bytecode.Operator code, VcBranch branch) { if (code.type(0) instanceof Type.EffectiveArray) { Expr src = branch.read(code.operand(0)); Expr idx = branch.read(code.operand(1)); @@ -450,7 +449,7 @@ public Pair[] indexOutOfBoundsChecks(Codes.Operator code, VcBranch * --- The branch the bytecode is on. * @return */ - public Pair[] arrayGeneratorChecks(Codes.Operator code, VcBranch branch) { + public Pair[] arrayGeneratorChecks(Bytecode.Operator code, VcBranch branch) { Expr idx = branch.read(code.operand(1)); Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), idx.attributes()); @@ -473,7 +472,7 @@ public Pair[] arrayGeneratorChecks(Codes.Operator code, VcBranch br * @return * @throws Exception */ - public Pair[] preconditionCheck(Codes.Invoke code, VcBranch branch, + public Pair[] preconditionCheck(Bytecode.Invoke code, VcBranch branch, Type[] environment, CodeForest forest) throws Exception { ArrayList> preconditions = new ArrayList<>(); // @@ -526,14 +525,14 @@ public Pair[] preconditionCheck(Codes.Invoke code, VcBranch branch, * --- The branch containing the update bytecode. * @return */ - public Pair[] updateChecks(Codes.Update code, VcBranch branch) { + public Pair[] updateChecks(Bytecode.Update code, VcBranch branch) { ArrayList> preconditions = new ArrayList>(); Expr src = branch.read(code.target(0)); - for (Codes.LVal lval : code) { - if (lval instanceof Codes.ArrayLVal) { - Codes.ArrayLVal lv = (Codes.ArrayLVal) lval; + for (Bytecode.LVal lval : code) { + if (lval instanceof Bytecode.ArrayLVal) { + Bytecode.ArrayLVal lv = (Bytecode.ArrayLVal) lval; Expr idx = branch.read(lv.indexOperand); Expr zero = new Expr.Constant(Value.Integer(BigInteger.ZERO), idx.attributes()); @@ -547,8 +546,8 @@ public Pair[] updateChecks(Codes.Update code, VcBranch branch) { new Expr.Binary(Expr.Binary.Op.LT, idx, length, idx .attributes()))); src = new Expr.IndexOf(src, idx); - } else if (lval instanceof Codes.RecordLVal) { - Codes.RecordLVal lv = (Codes.RecordLVal) lval; + } else if (lval instanceof Bytecode.RecordLVal) { + Bytecode.RecordLVal lv = (Bytecode.RecordLVal) lval; ArrayList fields = new ArrayList(lv.rawType() .fields().keySet()); Collections.sort(fields); diff --git a/modules/wyil/src/wyil/checks/CoercionCheck.java b/modules/wyil/src/wyil/checks/CoercionCheck.java index 17ffe0d070..87e9ac3040 100755 --- a/modules/wyil/src/wyil/checks/CoercionCheck.java +++ b/modules/wyil/src/wyil/checks/CoercionCheck.java @@ -99,8 +99,8 @@ protected void check(int blockID, CodeForest forest, WyilFile.FunctionOrMethod m for (int i = 0; i != block.size(); ++i) { CodeForest.Entry e = block.get(i); Bytecode code = e.code(); - if (code instanceof Codes.Convert) { - Codes.Convert conv = (Codes.Convert) code; + if (code instanceof Bytecode.Convert) { + Bytecode.Convert conv = (Bytecode.Convert) code; check(conv.type(0), conv.result(), new HashSet>(), e.attribute(SourceLocation.class)); } } diff --git a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java index 83fdd24727..480b1ccc2f 100755 --- a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java +++ b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java @@ -119,7 +119,7 @@ public HashSet propagate(CodeForest.Index index, Bytecode code, HashSet @Override public Pair, HashSet> propagate(CodeForest.Index index, - Codes.If igoto, HashSet in) { + Bytecode.If igoto, HashSet in) { if (!in.contains(igoto.operand(0)) || !in.contains(igoto.operand(1))) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, @@ -130,7 +130,7 @@ public Pair, HashSet> propagate(CodeForest.Index index } @Override - public Pair, HashSet> propagate(CodeForest.Index index, Codes.IfIs iftype, + public Pair, HashSet> propagate(CodeForest.Index index, Bytecode.IfIs iftype, HashSet in) { if (!in.contains(iftype.operand(0))) { @@ -142,7 +142,7 @@ public Pair, HashSet> propagate(CodeForest.Index index } @Override - public List> propagate(CodeForest.Index index, Codes.Switch sw, HashSet in) { + public List> propagate(CodeForest.Index index, Bytecode.Switch sw, HashSet in) { if (!in.contains(sw.operand(0))) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, @@ -157,9 +157,9 @@ public List> propagate(CodeForest.Index index, Codes.Switch sw, } @Override - public HashSet propagate(CodeForest.Index index, Codes.Loop loop, HashSet in) { - if (loop instanceof Codes.Quantify) { - Codes.Quantify fall = (Codes.Quantify) loop; + public HashSet propagate(CodeForest.Index index, Bytecode.Loop loop, HashSet in) { + if (loop instanceof Bytecode.Quantify) { + Bytecode.Quantify fall = (Bytecode.Quantify) loop; if (!in.contains(fall.startOperand()) || !in.contains(fall.endOperand())) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, @@ -193,7 +193,7 @@ public void checkUses(CodeForest.Index index, Bytecode code, HashSet in forest.get(index).attribute(SourceLocation.class)); } } - if (code instanceof Codes.Update && !in.contains(code.target(0))) { + if (code instanceof Bytecode.Update && !in.contains(code.target(0))) { // In this case, we are assigning to an index or field. // Therefore, the target register must already be defined. syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, diff --git a/modules/wyil/src/wyil/checks/ModuleCheck.java b/modules/wyil/src/wyil/checks/ModuleCheck.java index 8960c2ccae..6ec6f0f14c 100755 --- a/modules/wyil/src/wyil/checks/ModuleCheck.java +++ b/modules/wyil/src/wyil/checks/ModuleCheck.java @@ -34,7 +34,7 @@ import wyil.attributes.SourceLocation; import wyil.lang.*; import wyil.lang.CodeForest.Index; -import wyil.lang.Codes.*; +import wyil.lang.Bytecode.*; import static wyil.util.ErrorMessages.*; /** @@ -133,10 +133,10 @@ protected void checkFunctionPure(int blockID, CodeForest forest) { for (int i = 0; i != block.size(); ++i) { CodeForest.Entry e = block.get(i); Bytecode code = e.first(); - if(code instanceof Codes.Invoke && ((Codes.Invoke)code).type(0) instanceof Type.Method) { + if(code instanceof Bytecode.Invoke && ((Bytecode.Invoke)code).type(0) instanceof Type.Method) { // internal message send syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); - } else if (code instanceof Codes.IndirectInvoke && ((Codes.IndirectInvoke)code).type(0) instanceof Type.Method) { + } else if (code instanceof Bytecode.IndirectInvoke && ((Bytecode.IndirectInvoke)code).type(0) instanceof Type.Method) { syntaxError(errorMessage(METHODCALL_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); } else if (code.opcode() == Bytecode.OPCODE_newobject) { syntaxError(errorMessage(ALLOCATION_NOT_PERMITTED_IN_FUNCTION), filename, e.attribute(SourceLocation.class)); diff --git a/modules/wyil/src/wyil/io/WyilFilePrinter.java b/modules/wyil/src/wyil/io/WyilFilePrinter.java index eed5b834e6..de4aba836f 100755 --- a/modules/wyil/src/wyil/io/WyilFilePrinter.java +++ b/modules/wyil/src/wyil/io/WyilFilePrinter.java @@ -190,7 +190,7 @@ private void write(int indent, int blockID, CodeForest forest, PrintWriter out) CodeForest.Block block = forest.get(blockID); for(int i=0;i!=block.size();++i) { Bytecode code = block.get(i).code(); - if(code instanceof Codes.Label) { + if(code instanceof Bytecode.Label) { write(indent-1,code,forest,out); } else { write(indent,code,forest,out); diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index db9171d5ee..491ed91c7a 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -614,7 +614,7 @@ private CodeForest readCodeForestBlock() throws IOException { forest.addRoot(root); } - HashMap labels = new HashMap(); + HashMap labels = new HashMap(); int offset = 0; for(int i=0;i!=nBlocks;++i) { @@ -672,13 +672,13 @@ private CodeForest.Register readCodeRegister() throws IOException { * The map of offsets to labels being inserted. * @return */ - private int insertLabels(CodeForest forest, HashMap labels) { + private int insertLabels(CodeForest forest, HashMap labels) { int offset = 0; for (int i = 0; i != forest.numBlocks(); ++i) { CodeForest.Block block = forest.get(i); for (int j = 0; j != block.size(); ++j) { // First, check whether there is a label to insert - Codes.Label label = labels.get(offset++); + Bytecode.Label label = labels.get(offset++); if (label != null) { block.add(++j, label); } @@ -717,7 +717,7 @@ private int insertLabels(CodeForest forest, HashMap labels * @return * @throws IOException */ - public CodeForest.Block readCodeBlock(int offset, HashMap labels) + public CodeForest.Block readCodeBlock(int offset, HashMap labels) throws IOException { int nCodes = input.read_uv(); int nAttrs = input.read_uv(); @@ -731,7 +731,7 @@ public CodeForest.Block readCodeBlock(int offset, HashMap return new CodeForest.Block(bytecodes); } - private Bytecode readBytecode(int offset, HashMap labels) throws IOException { + private Bytecode readBytecode(int offset, HashMap labels) throws IOException { int opcode = input.read_u8(); Bytecode.Schema schema = schemas[opcode]; @@ -759,7 +759,7 @@ private Bytecode readBytecode(int offset, HashMap labels) * @return * @throws IOException */ - private Object[] readExtras(Bytecode.Schema schema, HashMap labels) + private Object[] readExtras(Bytecode.Schema schema, HashMap labels) throws IOException { Extras[] extras = schema.extras(); Object[] results = new Object[extras.length]; @@ -787,7 +787,7 @@ private Object[] readExtras(Bytecode.Schema schema, HashMap labels) { - Codes.Label label = labels.get(target); + private static Bytecode.Label findLabel(int target, HashMap labels) { + Bytecode.Label label = labels.get(target); if (label == null) { - label = Codes.Label("label" + labelCount++); + label = Bytecode.Label("label" + labelCount++); labels.put(target, label); } return label; @@ -860,27 +860,27 @@ private static Codes.Label findLabel(int target, HashMap l // schemas[Bytecode.OPCODE_goto] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Goto((String) extras[0]); + return Bytecode.Goto((String) extras[0]); } }; schemas[Bytecode.OPCODE_fail] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Fail(); + return Bytecode.Fail(); } }; schemas[Bytecode.OPCODE_assert] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Assert((Integer) extras[0]); + return Bytecode.Assert((Integer) extras[0]); } }; schemas[Bytecode.OPCODE_assume] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Assume((Integer) extras[0]); + return Bytecode.Assume((Integer) extras[0]); } }; schemas[Bytecode.OPCODE_invariant] = new Schema(Targets.ZERO, Operands.ZERO, Types.ZERO, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Invariant((Integer) extras[0]); + return Bytecode.Invariant((Integer) extras[0]); } }; @@ -889,24 +889,24 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_debug] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Debug(operands[0]); + return Bytecode.Debug(operands[0]); } }; schemas[Bytecode.OPCODE_return] = new Schema(Targets.ZERO, Operands.MANY, Types.MANY){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Return(types,operands); + return Bytecode.Return(types,operands); } }; schemas[Bytecode.OPCODE_switch] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE, Extras.TARGET, Extras.SWITCH_ARRAY){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { String defaultTarget = (String) extras[0]; Pair[] cases = (Pair[]) extras[1]; - return Codes.Switch(types[0], operands[0], defaultTarget, Arrays.asList(cases)); + return Bytecode.Switch(types[0], operands[0], defaultTarget, Arrays.asList(cases)); } }; schemas[Bytecode.OPCODE_ifis] = new Schema(Targets.ZERO, Operands.ONE, Types.TWO, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.IfIs(types[0], operands[0], types[1], (String)extras[0]); + return Bytecode.IfIs(types[0], operands[0], types[1], (String)extras[0]); } }; @@ -915,47 +915,47 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_assign] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ASSIGN); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ASSIGN); } }; schemas[Bytecode.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.NEW); + return Bytecode.Operator(types[0],targets,operands,Bytecode.OperatorKind.NEW); } }; schemas[Bytecode.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DEREFERENCE); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DEREFERENCE); } }; schemas[Bytecode.OPCODE_arrayinvert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEINVERT); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEINVERT); } }; schemas[Bytecode.OPCODE_arraylength] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { public Bytecode construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYLENGTH); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ARRAYLENGTH); } }; schemas[Bytecode.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.NEG); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEG); } }; schemas[Bytecode.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); + return Bytecode.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); } }; schemas[Bytecode.OPCODE_convert] = new Schema(Targets.ONE, Operands.ONE, Types.TWO){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Convert(types[0], targets[0], operands[0], types[1]); + return Bytecode.Convert(types[0], targets[0], operands[0], types[1]); } }; schemas[Bytecode.OPCODE_const] = new Schema(Targets.ONE, Operands.ZERO, Types.ZERO, Extras.CONSTANT){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Const(targets[0], (Constant) extras[0]); + return Bytecode.Const(targets[0], (Constant) extras[0]); } }; @@ -964,32 +964,32 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_ifeq] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.EQ, (String) extras[0]); + return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.EQ, (String) extras[0]); } }; schemas[Bytecode.OPCODE_ifne] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.NEQ, (String) extras[0]); + return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.NEQ, (String) extras[0]); } }; schemas[Bytecode.OPCODE_iflt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.LT, (String) extras[0]); + return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.LT, (String) extras[0]); } }; schemas[Bytecode.OPCODE_ifle] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.LTEQ, (String) extras[0]); + return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.LTEQ, (String) extras[0]); } }; schemas[Bytecode.OPCODE_ifgt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.GT, (String) extras[0]); + return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.GT, (String) extras[0]); } }; schemas[Bytecode.OPCODE_ifge] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.If(types[0], operands[0], operands[1], Codes.Comparator.GTEQ, (String) extras[0]); + return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.GTEQ, (String) extras[0]); } }; @@ -998,68 +998,68 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ADD); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ADD); } }; schemas[Bytecode.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.SUB); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.SUB); } }; schemas[Bytecode.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.MUL); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.MUL); } }; schemas[Bytecode.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.DIV); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DIV); } }; schemas[Bytecode.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.REM); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.REM); } }; schemas[Bytecode.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEOR); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEOR); } }; schemas[Bytecode.OPCODE_bitwisexor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEXOR); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEXOR); } }; schemas[Bytecode.OPCODE_bitwiseand] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.BITWISEAND); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEAND); } }; schemas[Bytecode.OPCODE_lshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.LEFTSHIFT); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LEFTSHIFT); } }; schemas[Bytecode.OPCODE_rshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RIGHTSHIFT); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.RIGHTSHIFT); } }; schemas[Bytecode.OPCODE_arrayindex] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0],targets,operands,Codes.OperatorKind.ARRAYINDEX); + return Bytecode.Operator(types[0],targets,operands,Bytecode.OperatorKind.ARRAYINDEX); } }; schemas[Bytecode.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands,Codes.OperatorKind.ARRAYGENERATOR); + return Bytecode.Operator(types[0], targets, operands,Bytecode.OperatorKind.ARRAYGENERATOR); } }; schemas[Bytecode.OPCODE_array] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.ARRAYCONSTRUCTOR); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR); } }; @@ -1068,34 +1068,34 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_record] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Operator(types[0], targets, operands, Codes.OperatorKind.RECORDCONSTRUCTOR); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR); } }; schemas[Bytecode.OPCODE_invoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Invoke((Type.FunctionOrMethod) types[0], targets, operands, (NameID) extras[0]); + return Bytecode.Invoke((Type.FunctionOrMethod) types[0], targets, operands, (NameID) extras[0]); } }; schemas[Bytecode.OPCODE_indirectinvoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { int src = operands[0]; operands = Arrays.copyOfRange(operands,1,operands.length); - return Codes.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, src, operands); + return Bytecode.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, src, operands); } }; schemas[Bytecode.OPCODE_lambda] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, (NameID) extras[0]); + return Bytecode.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, (NameID) extras[0]); } }; schemas[Bytecode.OPCODE_loop] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Loop(targets, (Integer) extras[0]); + return Bytecode.Loop(targets, (Integer) extras[0]); } }; schemas[Bytecode.OPCODE_quantify] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Codes.Quantify(operands[0],operands[1],operands[2], targets, (Integer) extras[0]); + return Bytecode.Quantify(operands[0],operands[1],operands[2], targets, (Integer) extras[0]); } }; schemas[Bytecode.OPCODE_update] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.STRING_ARRAY){ @@ -1105,7 +1105,7 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types for(int i=0;i!=strings.length;++i) { fields.add(strings[i]); } int operand = operands[operands.length-1]; operands = Arrays.copyOf(operands, operands.length-1); - return Codes.Update(types[0], targets[0], operands, operand, types[1], fields); + return Bytecode.Update(types[0], targets[0], operands, operand, types[1], fields); } }; } diff --git a/modules/wyil/src/wyil/io/WyilFileWriter.java b/modules/wyil/src/wyil/io/WyilFileWriter.java index 6e64390856..c8448a4325 100644 --- a/modules/wyil/src/wyil/io/WyilFileWriter.java +++ b/modules/wyil/src/wyil/io/WyilFileWriter.java @@ -595,8 +595,8 @@ private HashMap buildLabelsMap(CodeForest forest) { CodeForest.Block block = forest.get(i); for (int j = 0; j != block.size(); ++j) { Bytecode code = block.get(j).code(); - if (code instanceof Codes.Label) { - Codes.Label l = (Codes.Label) code; + if (code instanceof Bytecode.Label) { + Bytecode.Label l = (Bytecode.Label) code; labels.put(l.label, offset-1); } else { offset = offset + 1; @@ -679,7 +679,7 @@ private int writeCodeBlock(CodeForest.Block block, int offset, HashMap label Bytecode.Branching bb = (Bytecode.Branching) code; int destination = labels.get(bb.destination()); output.write_uv(destination); - } else if (code instanceof Codes.Const) { - Codes.Const c = (Codes.Const) code; + } else if (code instanceof Bytecode.Const) { + Bytecode.Const c = (Bytecode.Const) code; output.write_uv(constantCache.get(c.constant)); - } else if (code instanceof Codes.FieldLoad) { - Codes.FieldLoad c = (Codes.FieldLoad) code; + } else if (code instanceof Bytecode.FieldLoad) { + Bytecode.FieldLoad c = (Bytecode.FieldLoad) code; output.write_uv(stringCache.get(c.field)); - } else if (code instanceof Codes.Invoke) { - Codes.Invoke c = (Codes.Invoke) code; + } else if (code instanceof Bytecode.Invoke) { + Bytecode.Invoke c = (Bytecode.Invoke) code; output.write_uv(nameCache.get(c.name)); - } else if (code instanceof Codes.Lambda) { - Codes.Lambda c = (Codes.Lambda) code; + } else if (code instanceof Bytecode.Lambda) { + Bytecode.Lambda c = (Bytecode.Lambda) code; output.write_uv(nameCache.get(c.name)); - } else if (code instanceof Codes.Update) { - Codes.Update c = (Codes.Update) code; + } else if (code instanceof Bytecode.Update) { + Bytecode.Update c = (Bytecode.Update) code; List fields = c.fields; output.write_uv(fields.size()); for (int i = 0; i != fields.size(); ++i) { output.write_uv(stringCache.get(fields.get(i))); } - } else if (code instanceof Codes.Switch) { - Codes.Switch c = (Codes.Switch) code; + } else if (code instanceof Bytecode.Switch) { + Bytecode.Switch c = (Bytecode.Switch) code; List> branches = c.branches; int target = labels.get(c.defaultTarget); output.write_uv(target); @@ -871,7 +871,7 @@ private int countLabels(CodeForest.Block block) { int nlabels = 0; for (int i = 0; i != block.size(); ++i) { Bytecode code = block.get(i).code(); - if (code instanceof Codes.Label) { + if (code instanceof Bytecode.Label) { nlabels++; } } @@ -954,28 +954,28 @@ private void buildPools(CodeForest.Block block) { private void buildPools(Bytecode code) { // First, deal with special cases - if (code instanceof Codes.Const) { - Codes.Const c = (Codes.Const) code; + if (code instanceof Bytecode.Const) { + Bytecode.Const c = (Bytecode.Const) code; addConstantItem(c.constant); - } else if (code instanceof Codes.FieldLoad) { - Codes.FieldLoad c = (Codes.FieldLoad) code; + } else if (code instanceof Bytecode.FieldLoad) { + Bytecode.FieldLoad c = (Bytecode.FieldLoad) code; addStringItem(c.field); - }else if (code instanceof Codes.Invoke) { - Codes.Invoke c = (Codes.Invoke) code; + }else if (code instanceof Bytecode.Invoke) { + Bytecode.Invoke c = (Bytecode.Invoke) code; addNameItem(c.name); - } else if (code instanceof Codes.Lambda) { - Codes.Lambda c = (Codes.Lambda) code; + } else if (code instanceof Bytecode.Lambda) { + Bytecode.Lambda c = (Bytecode.Lambda) code; addNameItem(c.name); - } else if (code instanceof Codes.Update) { - Codes.Update c = (Codes.Update) code; - for (Codes.LVal l : c) { - if (l instanceof Codes.RecordLVal) { - Codes.RecordLVal lv = (Codes.RecordLVal) l; + } else if (code instanceof Bytecode.Update) { + Bytecode.Update c = (Bytecode.Update) code; + for (Bytecode.LVal l : c) { + if (l instanceof Bytecode.RecordLVal) { + Bytecode.RecordLVal lv = (Bytecode.RecordLVal) l; addStringItem(lv.field); } } - } else if (code instanceof Codes.Switch) { - Codes.Switch s = (Codes.Switch) code; + } else if (code instanceof Bytecode.Switch) { + Bytecode.Switch s = (Bytecode.Switch) code; for (Pair b : s.branches) { addConstantItem(b.first()); } diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java index 4c7404f23a..d2378da2dc 100755 --- a/modules/wyil/src/wyil/lang/Bytecode.java +++ b/modules/wyil/src/wyil/lang/Bytecode.java @@ -27,7 +27,11 @@ import java.util.*; +import wycc.lang.NameID; import wycc.lang.SyntacticElement; +import wycc.util.Pair; +import wyil.lang.Bytecode.Branching; +import wyil.lang.Bytecode.Compound; import static wyil.lang.CodeUtils.*; @@ -219,86 +223,2011 @@ public int[] operands() { public int operand(int i) { return operands[i]; } + + /** + * Return the opcode value of this bytecode. + * @return + */ + public abstract int opcode(); + + /** + * A compound bytecode represents a bytecode that contains sequence of zero + * or more bytecodes. For example, the loop bytecode contains its loop body. + * The nested block of bytecodes is represented as a block identifier in the + * enclosing forest. + * + * @author David J. Pearce + * + */ + public static abstract class Compound extends Bytecode { + protected final int block; + + public Compound(int block, Type[] types, int[] targets, int... operands) { + super(types, targets, operands); + this.block = block; + } + + public int block() { + return block; + } + + public int hashCode() { + return super.hashCode() ^ block; + } + + public boolean equals(Object o) { + if(o instanceof Compound) { + Compound abc = (Compound) o; + return block == abc.block && super.equals(o); + } + return false; + } + } + + /** + * A compound bytecode represents a bytecode that contains sequence of zero + * or more bytecodes. For example, the loop bytecode contains its loop body. + * The nested block of bytecodes is represented as a block identifier in the + * enclosing forest. + * + * @author David J. Pearce + * + */ + public static abstract class Branching extends Bytecode { + protected final String destination; + + public Branching(String destination, Type[] types, int[] targets, int... operands) { + super(types, targets, operands); + this.destination = destination; + } + + public String destination() { + return destination; + } + + public int hashCode() { + return super.hashCode() ^ destination.hashCode(); + } + + public boolean equals(Object o) { + if(o instanceof Branching) { + Branching abc = (Branching) o; + return destination.equals(abc.destination) && super.equals(o); + } + return false; + } + } + + // =============================================================== + // Bytecode Constructors + // =============================================================== + + /** + * Construct an assert bytecode which represents a user-defined + * assertion check. + * + * @param message + * --- message to report upon failure. + * @return + */ + public static Assert Assert(int block) { + return new Assert(block); + } + + /** + * Construct an assume bytecode which represents a user-defined + * assumption. + * + * @param message + * --- message to report upon failure. + * @return + */ + public static Assume Assume(int block) { + return new Assume(block); + } + + public static Operator Operator(Type type, int[] targets, int[] operands, OperatorKind op) { + return new Operator(type, targets, operands, op); + } + + /** + * Construct a const bytecode which loads a given constant onto + * the stack. + * + * @param afterType + * --- record type. + * @param field + * --- field to write. + * @return + */ + public static Const Const(int target, Constant constant) { + return new Const(target, constant); + } + + public static Convert Convert(Type from, int target, int operand, Type to) { + return new Convert(from, target, operand, to); + } + + public static final Debug Debug(int operand) { + return new Debug(operand); + } + + /** + * Construct a fail bytecode which halts execution by raising a + * fault. + * + * @param string + * --- Message to give on error. + * @return + */ + public static Fail Fail() { + return new Fail(); + } + + /** + * Construct a fieldload bytecode which reads a given field + * from a record of a given type. + * + * @param type + * --- record type. + * @param field + * --- field to load. + * @return + */ + public static FieldLoad FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { + return new FieldLoad(type, target, operand, field); + } + + /** + * Construct a goto bytecode which branches unconditionally to + * a given label. + * + * @param label + * --- destination label. + * @return + */ + public static Goto Goto(String label) { + return new Goto(label); + } + + public static Invoke Invoke(Type.FunctionOrMethod fun, Collection targets, Collection operands, + NameID name) { + return new Invoke(fun, CodeUtils.toIntArray(targets), CodeUtils.toIntArray(operands), name); + } + + public static Invoke Invoke(Type.FunctionOrMethod fun, int[] targets, int[] operands, NameID name) { + return new Invoke(fun, targets, operands, name); + } + + /** + * Construct an invariant bytecode which represents a + * user-defined loop invariant. + * + * @param message + * --- message to report upon failure. + * @return + */ + public static Invariant Invariant(int block) { + return new Invariant(block); + } + + public static Lambda Lambda(Type.FunctionOrMethod fun, int target, Collection operands, NameID name) { + return new Lambda(fun, target, CodeUtils.toIntArray(operands), name); + } + + public static Lambda Lambda(Type.FunctionOrMethod fun, int target, int[] operands, NameID name) { + return new Lambda(fun, target, operands, name); + } + + public static Loop Loop(int[] modifiedOperands, int block) { + return new Loop(modifiedOperands, block); + } + + /** + * Construct a return bytecode which does return a value and, hence, its + * type automatically defaults to void. + * + * @return + */ + public static Return Return() { + return new Return(new Type[0]); + } + + /** + * Construct a return bytecode which reads a value from the operand register + * and returns it. + * + * @param type + * --- type of the value to be returned (cannot be void). + * @param operand + * --- register to read return value from. + * @return + */ + public static Return Return(Type[] types, int... operands) { + return new Return(types, operands); + } + + public static If If(Type type, int leftOperand, int rightOperand, Comparator cop, String label) { + return new If(type, leftOperand, rightOperand, cop, label); + } + + public static IfIs IfIs(Type type, int leftOperand, Type rightOperand, String label) { + return new IfIs(type, leftOperand, rightOperand, label); + } + + public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, int[] targets, int operand, + Collection operands) { + return new IndirectInvoke(fun, targets, operand, CodeUtils.toIntArray(operands)); + } + + public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, int[] targets, int operand, int[] operands) { + return new IndirectInvoke(fun, targets, operand, operands); + } + + public static Label Label(String label) { + return new Label(label); + } + + /** + * Construct a switch bytecode which pops a value off the + * stack, and switches to a given label based on it. + * + * @param type + * --- value type to switch on. + * @param defaultLabel + * --- target for the default case. + * @param cases + * --- map from values to destination labels. + * @return + */ + public static Switch Switch(Type type, int operand, String defaultLabel, Collection> cases) { + return new Switch(type, operand, defaultLabel, cases); + } + + public static Quantify Quantify(int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, + int block) { + return new Quantify(startOperand, endOperand, indexOperand, modifiedOperands, block); + } + + public static Update Update(Type beforeType, int target, Collection operands, int operand, Type afterType, + Collection fields) { + return new Update(beforeType, target, CodeUtils.toIntArray(operands), operand, afterType, fields); + } + + public static Update Update(Type beforeType, int target, int[] operands, int operand, Type afterType, + Collection fields) { + return new Update(beforeType, target, operands, operand, afterType, fields); + } + // =============================================================== - // Abstract Methods + // Bytecode Implementations // =============================================================== /** - * Return the opcode value of this bytecode. - * @return + * Represents a binary operator (e.g. '+','-',etc) that is provided to a + * BinOp bytecode. + * + * @author David J. Pearce + * + */ + public enum OperatorKind { + // Unary + NEG(0) { + public String toString() { + return "neg"; + } + }, + BITWISEINVERT(1) { + public String toString() { + return "invert"; + } + }, + DEREFERENCE(2) { + public String toString() { + return "deref"; + } + }, + ARRAYLENGTH(3) { + public String toString() { + return "length"; + } + }, + // Binary + ADD(4) { + public String toString() { + return "add"; + } + }, + SUB(5) { + public String toString() { + return "sub"; + } + }, + MUL(6) { + public String toString() { + return "mul"; + } + }, + DIV(7) { + public String toString() { + return "div"; + } + }, + REM(8) { + public String toString() { + return "rem"; + } + }, + BITWISEOR(9) { + public String toString() { + return "or"; + } + }, + BITWISEXOR(10) { + public String toString() { + return "xor"; + } + }, + BITWISEAND(11) { + public String toString() { + return "and"; + } + }, + LEFTSHIFT(12) { + public String toString() { + return "shl"; + } + }, + RIGHTSHIFT(13) { + public String toString() { + return "shr"; + } + }, + ARRAYINDEX(14) { + public String toString() { + return "indexof"; + } + }, + ARRAYGENERATOR(15) { + public String toString() { + return "arraygen"; + } + }, + ARRAYCONSTRUCTOR(16) { + public String toString() { + return "array"; + } + }, + RECORDCONSTRUCTOR(17) { + public String toString() { + return "record"; + } + }, + NEW(18) { + public String toString() { + return "new"; + } + }, + ASSIGN(19) { + public String toString() { + return "assign"; + } + }; + public int offset; + + private OperatorKind(int offset) { + this.offset = offset; + } + }; + + /** + *

+ * A binary operation which reads two numeric values from the operand + * registers, performs an operation on them and writes the result to the + * target register. The binary operators are: + *

+ *
    + *
  • add, subtract, multiply, divide, remainder. Both operands must + * be either integers or reals (but not one or the other). A value of the + * same type is produced.
  • + *
  • bitwiseor, bitwisexor, bitwiseand
  • + *
  • leftshift,rightshift
  • + *
+ * For example, the following Whiley code: + * + *
+	 * function f(int x, int y) -> int:
+	 *     return ((x * y) + 1) / 2
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int x, int y) -> int:
+	 * body:
+	 *     mul %2 = %0, %1   : int
+	 *     const %3 = 1      : int
+	 *     add %2 = %2, %3   : int
+	 *     const %3 = 2      : int
+	 *     const %4 = 0      : int
+	 *     assertne %3, %4 "division by zero" : int
+	 *     div %2 = %2, %3   : int
+	 *     return %2         : int
+	 * 
+ * + * Here, the assertne bytecode has been included to check + * against division-by-zero. In this particular case the assertion is known + * true at compile time and, in practice, would be compiled away. + * + * @author David J. Pearce + * + */ + public static final class Operator extends Bytecode { + public final OperatorKind kind; + + private Operator(Type type, int[] targets, int[] operands, OperatorKind bop) { + super(new Type[] { type }, targets, operands); + if (bop == null) { + throw new IllegalArgumentException("Operator kind cannot be null"); + } + this.kind = bop; + } + + @Override + public int opcode() { + return OPCODE_neg + kind.offset; + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return Operator(type(0), nTargets, nOperands, kind); + } + + public int hashCode() { + return kind.hashCode() + super.hashCode(); + } + + public boolean equals(Object o) { + if (o instanceof Operator) { + Operator bo = (Operator) o; + return kind.equals(bo.kind) && super.equals(bo); + } + return false; + } + + public String toString() { + return kind + " %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0); + } + } + + /** + * Reads a value from the operand register, converts it to a given type and + * writes the result to the target register. This bytecode is the only way + * to change the type of a value. It's purpose is to simplify + * implementations which have different representations of data types. A + * convert bytecode must be inserted whenever the type of a register + * changes. This includes at control-flow meet points, when the value is + * passed as a parameter, assigned to a field, etc. For example, the + * following Whiley code: + * + *
+	 * function f(int x) -> real:
+	 *     return x + 1
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int x) -> real:
+	 * body:
+	 *     const %2 = 1           : int
+	 *     add %1 = %0, %2        : int
+	 *     convert %1 = %1 real   : int
+	 *     return %1              : real
+	 * 
+ *

+ * Here, we see that the int value in register %1 + * must be explicitly converted into a real value before it can + * be returned from this function. + *

+ *

+ * NOTE: In many cases, this bytecode may correspond to a nop on the + * hardware. Consider converting from [any] to any + * . On the JVM, any translates to Object, whilst + * [any] translates to List (which is an instance + * of Object). Thus, no conversion is necessary since + * List can safely flow into Object. + *

+ * + */ + public static final class Convert extends Bytecode { + + private Convert(Type from, int target, int operand, Type result) { + super(new Type[] { from, result }, new int[] { target }, operand); + } + + public Bytecode clone(int[] nTargets, int[] nOperands) { + return Convert(type(0), nTargets[0], nOperands[0], type(1)); + } + + public Type result() { + return type(1); + } + + public int opcode() { + return OPCODE_convert; + } + + public boolean equals(Object o) { + return o instanceof Convert && super.equals(o); + } + + public String toString() { + return "convert %" + target(0) + " = %" + operand(0) + " " + result() + " : " + type(0); + } + } + + /** + * Writes a constant value to a target register. This includes + * integers, rationals, lists, sets, maps + * , etc. For example, the following Whiley code: + * + *
+	 * function f(int x) -> int:
+	 *     xs = {1,2.12}
+	 *     return |xs| + 1
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int x) -> int:
+	 * body:
+	 *     var xs
+	 *     const %2 = 1               : int
+	 *     convert %2 = % 2 int|real  : int
+	 *     const %3 = 2.12            : real
+	 *     convert %3 = % 3 int|real  : real
+	 *     newset %1 = (%2, %3)       : {int|real}
+	 *     assign %3 = %1             : {int|real}
+	 *     lengthof %3 = % 3          : {int|real}
+	 *     const %4 = 1               : int
+	 *     add %2 = % 3, %4           : int
+	 *     return %2                  : int
+	 * 
+ * + * Here, we see two kinds of constants values being used: integers (i.e. + * const %2 = 1) and rationals (i.e. + * const %3 = 2.12). + * + * @author David J. Pearce + * + */ + public static final class Const extends Bytecode { + public final Constant constant; + + private Const(int target, Constant constant) { + super(new Type[0], new int[] { target }, new int[0]); + this.constant = constant; + } + + public int opcode() { + return OPCODE_const; + } + + public int target() { + return targets()[0]; + } + + public int hashCode() { + return constant.hashCode() + targets()[0]; + } + + public boolean equals(Object o) { + if (o instanceof Const) { + Const c = (Const) o; + return constant.equals(c.constant) && Arrays.equals(targets(), c.targets()); + } + return false; + } + + public String toString() { + return "const %" + targets()[0] + " = " + constant + " : " + constant.type(); + } + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return new Const(nTargets[0], constant); + } + } + + /** + * Read a string from the operand register and prints it to the debug + * console. For example, the following Whiley code: + * + *
+	 * method f(int x):
+	 *     debug "X = " + x
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * method f(int x):
+	 * body:
+	 *     const %2 = "X = "       : string
+	 *     convert %0 = %0 any     : int
+	 *     invoke %0 (%0) whiley/lang/Any:toString : string(any)
+	 *     strappend %1 = %2, %0   : string
+	 *     debug %1                : string
+	 *     return
+	 * 
+ * + * NOTE This bytecode is not intended to form part of the program's + * operation. Rather, it is to facilitate debugging within functions (since + * they cannot have side-effects). Furthermore, if debugging is disabled, + * this bytecode is a nop. + * + * @author David J. Pearce + * + */ + public static final class Debug extends Bytecode { + + private Debug(int operand) { + super(new Type[] { Type.Array(Type.T_INT, false) }, new int[0], operand); + } + + public int opcode() { + return OPCODE_debug; + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return Debug(nOperands[0]); + } + + public boolean equals(Object o) { + return o instanceof Debug && super.equals(o); + } + + public String toString() { + return "debug %" + operands[0] + " " + " : " + types[0]; + } + } + + /** + * An abstract class representing either an assert or + * assume bytecode block. + * + * @author David J. Pearce + * + */ + public static abstract class AssertOrAssume extends Compound { + private AssertOrAssume(int block) { + super(block, new Type[0], new int[0], new int[0]); + } + } + + /** + * Represents a block of bytecode instructions representing an assertion. + * + * @author David J. Pearce + * + */ + public static class Assert extends AssertOrAssume { + + private Assert(int block) { + super(block); + } + + public int opcode() { + return OPCODE_assert; + } + + public String toString() { + return "assert #" + block; + } + + public boolean equals(Object o) { + return o instanceof Assume && super.equals(o); + } + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return this; + } + } + + /** + * Represents a block of bytecode instructions representing an assumption. + * + * @author David J. Pearce + * + */ + public static final class Assume extends AssertOrAssume { + + private Assume(int block) { + super(block); + } + + public int opcode() { + return OPCODE_assume; + } + + public String toString() { + return "assume #" + block; + } + + public boolean equals(Object o) { + return o instanceof Assume && super.equals(o); + } + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return this; + } + } + + /** + * A bytecode that halts execution by raising a runtime fault. This bytecode + * signals that some has gone wrong, and is typically used to signal an + * assertion failure. + * + * @author David J. Pearce + * + */ + public static final class Fail extends Bytecode { + private Fail() { + super(new Type[0], new int[0]); + } + + @Override + public int opcode() { + return OPCODE_fail; + } + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return this; + } + + public String toString() { + return "fail"; + } + + } + + /** + * Reads a record value from an operand register, extracts the value of a + * given field and writes this to the target register. For example, the + * following Whiley code: + * + *
+	 * type Point is {int x, int y}
+	 *
+	 * function f(Point p) -> int:
+	 *     return p.x + p.y
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f({int x,int y} p) -> int:
+	 * body:
+	 *     fieldload %2 = %0 x    : {int x,int y}
+	 *     fieldload %3 = %0 y    : {int x,int y}
+	 *     add %1 = %2, %3        : int
+	 *     return %1              : int
+	 * 
+ * + * @author David J. Pearce + * + */ + public static final class FieldLoad extends Bytecode { + public final String field; + + private FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { + super((Type) type, target, operand); + if (field == null) { + throw new IllegalArgumentException("FieldLoad field argument cannot be null"); + } + this.field = field; + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return FieldLoad((Type.EffectiveRecord) type(0), nTargets[0], nOperands[0], field); + } + + public int opcode() { + return OPCODE_fieldload; + } + + public int hashCode() { + return super.hashCode() + field.hashCode(); + } + + public Type fieldType() { + Type.EffectiveRecord er = (Type.EffectiveRecord) type(0); + return er.fields().get(field); + } + + public boolean equals(Object o) { + if (o instanceof FieldLoad) { + FieldLoad i = (FieldLoad) o; + return super.equals(i) && field.equals(i.field); + } + return false; + } + + public String toString() { + return "fieldload %" + target(0) + " = %" + operand(0) + " " + field + " : " + type(0); + } + } + + /** + * Branches unconditionally to the given label. This is typically used for + * if/else statements. For example, the following Whiley code: + * + *
+	 * function f(int x) -> int:
+	 *     if x >= 0:
+	 *         x = 1
+	 *     else:
+	 *         x = -1
+	 *     return x
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int x) -> int:
+	 * body:
+	 *     const %1 = 0             : int
+	 *     iflt %0, %1 goto blklab0 : int
+	 *     const %0 = 1             : int
+	 *     goto blklab1
+	 * .blklab0
+	 *     const %0 = 1             : int
+	 *     neg %0 = % 0             : int
+	 * .blklab1
+	 *     return %0                : int
+	 * 
+ * + * Here, we see the goto bytecode being used to jump from the + * end of the true branch over the false branch. + * + *

+ * Note: in WyIL bytecode, such branches may only go forward. + * Thus, a goto bytecode cannot be used to implement the + * back-edge of a loop. Rather, a loop block must be used for this purpose. + *

+ * + * @author David J. Pearce + * + */ + public static final class Goto extends Branching { + private Goto(String target) { + super(target, new Type[0], new int[0]); + } + + public int opcode() { + return OPCODE_goto; + } + + public Goto relabel(Map labels) { + String nlabel = labels.get(destination()); + if (nlabel == null) { + return this; + } else { + return Goto(nlabel); + } + } + + public boolean equals(Object o) { + return o instanceof Goto && super.equals(o); + } + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return this; + } + + public String toString() { + return "goto " + destination(); + } + } + + /** + *

+ * Branches conditionally to the given label by reading the values from two + * operand registers and comparing them. The possible comparators are: + *

+ *
    + *
  • equals (eq) and not-equals (ne). Both operands must have the + * given type.
  • + *
  • less-than (lt), less-than-or-equals (le), greater-than (gt) and + * great-than-or-equals (ge). Both operands must have the given type, + * which additionally must by either char, int or + * real.
  • + *
  • element of (in). The second operand must be a set whose + * element type is that of the first.
  • + *
+ * For example, the following Whiley code: + * + *
+	 * function f(int x, int y) -> int:
+	 *     if x < y:
+	 *         return -1
+	 *     else if x > y:
+	 *         return 1
+	 *     else:
+	 *         return 0
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int x, int y) -> int:
+	 * body:
+	 *     ifge %0, %1 goto blklab0 : int
+	 *     const %2 = -1 : int
+	 *     return %2 : int
+	 * .blklab0
+	 *     ifle %0, %1 goto blklab2 : int
+	 *     const %2 = 1 : int
+	 *     return %2 : int
+	 * .blklab2
+	 *     const %2 = 0 : int
+	 *     return %2 : int
+	 * 
+ * + * Note: in WyIL bytecode, such branches may only go forward. + * Thus, an ifgoto bytecode cannot be used to implement the + * back-edge of a loop. Rather, a loop block must be used for this purpose. + * + * @author David J. Pearce + * + */ + public static final class If extends Branching { + public final Comparator op; + + private If(Type type, int leftOperand, int rightOperand, Comparator op, String target) { + super(target, new Type[] { type }, new int[0], leftOperand, rightOperand); + this.op = op; + } + + public If relabel(Map labels) { + String nlabel = labels.get(destination()); + if (nlabel == null) { + return this; + } else { + return If(types[0], operands[0], operands[1], op, nlabel); + } + } + + public int opcode() { + return OPCODE_ifeq + op.offset; + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return If(types[0], nOperands[0], nOperands[1], op, destination()); + } + + public int hashCode() { + return super.hashCode() + op.hashCode(); + } + + public boolean equals(Object o) { + if (o instanceof If) { + If ig = (If) o; + return op == ig.op && super.equals(ig); + } + return false; + } + + public String toString() { + return "if" + op + " %" + operands[0] + ", %" + operands[1] + " goto " + destination() + " : " + types[0]; + } + } + + /** + * Represents a comparison operator (e.g. '==','!=',etc) that is provided to + * a IfGoto bytecode. + * + * @author David J. Pearce + * + */ + public enum Comparator { + EQ(0) { + public String toString() { + return "eq"; + } + }, + NEQ(1) { + public String toString() { + return "ne"; + } + }, + LT(2) { + public String toString() { + return "lt"; + } + }, + LTEQ(3) { + public String toString() { + return "le"; + } + }, + GT(4) { + public String toString() { + return "gt"; + } + }, + GTEQ(5) { + public String toString() { + return "ge"; + } + }; + public int offset; + + private Comparator(int offset) { + this.offset = offset; + } + }; + + /** + * Branches conditionally to the given label based on the result of a + * runtime type test against a value from the operand register. More + * specifically, it checks whether the value is a subtype of the type test. + * The operand register is automatically retyped as a result of the + * type test. On the true branch, its type is intersected with type test. On + * the false branch, its type is intersected with the negation of the + * type test. For example, the following Whiley code: + * + *
+	 * function f(int|int[] x) -> int:
+	 *     if x is int[]:
+	 *         return |x|
+	 *     else:
+	 *         return x
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int|int[] x) -> int:
+	 * body:
+	 *     ifis %0, int[] goto lab    : int|int[]
+	 *     return %0                  : int
+	 * .lab
+	 *     lengthof %0 = %0           : int[]
+	 *     return %0                  : int
+	 * 
+ * + * Here, we see that, on the false branch, register %0 is + * automatically given type int, whilst on the true branch it + * is automatically given type int[]. + * + *

+ * Note: in WyIL bytecode, such branches may only go forward. + * Thus, an ifis bytecode cannot be used to implement the + * back-edge of a loop. Rather, a loop block must be used for this purpose. + *

+ * + * @author David J. Pearce + * + */ + public static final class IfIs extends Branching { + private IfIs(Type type, int leftOperand, Type rightOperand, String target) { + super(target, new Type[] { type, rightOperand }, new int[0], leftOperand); + } + + public int opcode() { + return OPCODE_ifis; + } + + public Type rightOperand() { + return type(1); + } + + public IfIs relabel(Map labels) { + String nlabel = labels.get(destination()); + if (nlabel == null) { + return this; + } else { + return IfIs(types[0], operands[0], types[1], nlabel); + } + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return IfIs(types[0], nOperands[0], types[1], destination()); + } + + public boolean equals(Object o) { + return o instanceof IfIs && super.equals(o); + } + + public String toString() { + return "ifis" + " %" + operands[0] + ", " + types[1] + " goto " + destination() + " : " + types[0]; + } + } + + /** + * Represents an indirect function call. For example, consider the + * following: + * + *
+	 * function fun(function (int)->int f, int x) -> int:
+	 *    return f(x)
+	 * 
+ * + * Here, the function call f(x) is indirect as the called + * function is determined by the variable f. + * + * @author David J. Pearce + * + */ + public static final class IndirectInvoke extends Bytecode { + + /** + * Construct an indirect invocation bytecode which assigns to an + * optional target register the result from indirectly invoking a + * function in a given operand with a given set of parameter operands. + * + * @param type + * @param target + * Register (optional) to which result of invocation is + * assigned. + * @param operand + * Register holding function point through which indirect + * invocation is made. + * @param operands + * Registers holding parameters for the invoked function + */ + private IndirectInvoke(Type.FunctionOrMethod type, int[] targets, int operand, int[] operands) { + super(new Type.FunctionOrMethod[] { type }, targets, append(operand, operands)); + } + + /** + * Return register holding the indirect function/method reference. + * + * @return + */ + public int reference() { + return operands()[0]; + } + + /** + * Return register holding the ith parameter for the invoked function. + * + * @param i + * @return + */ + public int parameter(int i) { + return operands()[i + 1]; + } + + /** + * Return registers holding parameters for the invoked function. + * + * @param i + * @return + */ + public int[] parameters() { + return Arrays.copyOfRange(operands(), 1, operands().length); + } + + public int opcode() { + return OPCODE_indirectinvoke; + } + + @Override + public Type.FunctionOrMethod type(int i) { + return (Type.FunctionOrMethod) super.type(i); + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return IndirectInvoke(type(0), nTargets, nOperands[0], Arrays.copyOfRange(nOperands, 1, nOperands.length)); + } + + public boolean equals(Object o) { + return o instanceof IndirectInvoke && super.equals(o); + } + + public String toString() { + return "indirectinvoke " + arrayToString(targets()) + " = %" + reference() + " " + + arrayToString(parameters()) + " : " + type(0); + } + } + + /** + * Represents a block of bytecode instructions representing an assertion. + * + * @author David J. Pearce + * + */ + public static class Invariant extends Assert { + + private Invariant(int block) { + super(block); + } + + public int opcode() { + return OPCODE_invariant; + } + + public String toString() { + return "invariant"; + } + + public int hashCode() { + return block; + } + + public boolean equals(Object o) { + if (o instanceof Invariant) { + Invariant f = (Invariant) o; + return block == f.block; + } + return false; + } + + @Override + public Invariant clone() { + return this; + } + } + + /** + * Corresponds to a function or method call whose parameters are read from + * zero or more operand registers. If a return value is required, this is + * written to a target register afterwards. For example, the following + * Whiley code: + * + *
+	 * function g(int x, int y, int z) -> int:
+	 *     return x * y * z
+	 *
+	 * function f(int x, int y) -> int:
+	 *     r = g(x,y,3)
+	 *     return r + 1
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function g(int x, int y, int z) -> int:
+	 * body:
+	 *     mul %3 = %0, %1   : int
+	 *     mul %3 = %3, %2   : int
+	 *     return %3         : int
+	 *
+	 * function f(int x, int y) -> int:
+	 * body:
+	 *     const %2 = 3                    : int
+	 *     invoke %2 = (%0, %1, %2) test:g   : int(int,int,int)
+	 *     const %3 = 1                    : int
+	 *     add %2 = (%2, %3)                : int
+	 *     return %2                       : int
+	 * 
+ * + * Here, we see that arguments to the invoke bytecode are + * supplied in the order they are given in the function or method's + * declaration. + * + * @author David J. Pearce + * + */ + public static final class Invoke extends Bytecode { + public final NameID name; + + private Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, NameID name) { + super(new Type.FunctionOrMethod[] { type }, targets, operands); + this.name = name; + } + + public int opcode() { + return OPCODE_invoke; + } + + public int hashCode() { + return name.hashCode() + super.hashCode(); + } + + @Override + public Type.FunctionOrMethod type(int i) { + return (Type.FunctionOrMethod) super.type(i); + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return Invoke(type(0), nTargets, nOperands, name); + } + + public boolean equals(Object o) { + if (o instanceof Invoke) { + Invoke i = (Invoke) o; + return name.equals(i.name) && super.equals(i); + } + return false; + } + + public String toString() { + return "invoke " + arrayToString(targets()) + " = " + arrayToString(operands()) + " " + name + " : " + + type(0); + } + } + + public static final class Lambda extends Bytecode { + public final NameID name; + + private Lambda(Type.FunctionOrMethod type, int target, int[] operands, NameID name) { + super(type, target, operands); + this.name = name; + } + + public int opcode() { + return OPCODE_lambda; + } + + public int hashCode() { + return name.hashCode() + super.hashCode(); + } + + @Override + public Type.FunctionOrMethod type(int i) { + return (Type.FunctionOrMethod) super.type(i); + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return Lambda((Type.FunctionOrMethod) type(0), nTargets[0], nOperands, name); + } + + public boolean equals(Object o) { + if (o instanceof Lambda) { + Lambda i = (Lambda) o; + return name.equals(i.name) && super.equals(i); + } + return false; + } + + public String toString() { + return "lambda %" + target(0) + " = " + arrayToString(operands()) + " " + name + " : " + type(0); + } + } + + /** + * Represents the labelled destination of a branch or loop statement. + * + * @author David J. Pearce + * */ - public abstract int opcode(); - + public static class Label extends Bytecode { + public final String label; + + private Label(String label) { + super(new Type[0], new int[0], new int[0]); + this.label = label; + } + + public int opcode() { + return -1; + } + + public Label relabel(Map labels) { + String nlabel = labels.get(label); + if (nlabel == null) { + return this; + } else { + return Label(nlabel); + } + } + + @Override + public Bytecode remap(Map binding) { + return this; + } + + public int hashCode() { + return label.hashCode(); + } + + public boolean equals(Object o) { + if (o instanceof Label) { + return label.equals(((Label) o).label); + } + return false; + } + + public String toString() { + return "." + label; + } + + @Override + public void registers(Set register) { + // TODO Auto-generated method stub + } + + @Override + protected Bytecode clone(int[] nTargets, int[] nOperands) { + return new Label(label); + } + } + /** - * A compound bytecode represents a bytecode that contains sequence of zero - * or more bytecodes. For example, the loop bytecode contains its loop body. - * The nested block of bytecodes is represented as a block identifier in the - * enclosing forest. + * Represents a block of code which loops continuously until e.g. a + * conditional branch is taken out of the block. For example: + * + *
+	 * function f() -> int:
+	 *     r = 0
+	 *     while r < 10:
+	 *         r = r + 1
+	 *     return r
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f() -> int:
+	 * body:
+	 *     const %0 = 0             : int
+	 *     loop (%0)
+	 *         const %1 = 10        : int
+	 *         ifge %0, %1 goto blklab0 : int
+	 *         const %1 = 1         : int
+	 *         add %0 = %0, %1      : int
+	 * .blklab0
+	 *     return %0                : int
+	 * 
+ * + *

+ * Here, we see a loop which increments an accumulator register + * %0 until it reaches 10, and then exits the loop + * block. + *

+ *

+ * The modified operands of a loop bytecode (shown in brackets + * alongside the bytecode) indicate those operands which are modified at + * some point within the loop. + *

* * @author David J. Pearce * */ - public static abstract class Compound extends Bytecode { - protected final int block; + public static class Loop extends Compound { - public Compound(int block, Type[] types, int[] targets, int... operands) { - super(types, targets, operands); - this.block = block; + private Loop(int[] targets, int block, int... operands) { + super(block, new Type[0], targets, operands); } - public int block() { - return block; + public int opcode() { + return OPCODE_loop; } - - public int hashCode() { - return super.hashCode() ^ block; + + public boolean equals(Object o) { + if (o instanceof Loop) { + Loop f = (Loop) o; + return block == f.block && super.equals(f); + } + return false; } - + + public int[] modifiedOperands() { + return targets(); + } + + @Override + public Loop clone(int[] nTargets, int[] nOperands) { + return new Loop(nTargets, block, nOperands); + } + + public String toString() { + return "loop " + arrayToString(targets()) + " = " + block; + } + } + + public static final class Quantify extends Loop { + + private Quantify(int startOperand, int endOperand, int indexOperand, int[] targets, int block) { + super(targets, block, startOperand, endOperand, indexOperand); + } + + public int opcode() { + return OPCODE_quantify; + } + + public int startOperand() { + return operands[0]; + } + + public int endOperand() { + return operands[1]; + } + + public int indexOperand() { + return operands[2]; + } + public boolean equals(Object o) { - if(o instanceof Compound) { - Compound abc = (Compound) o; - return block == abc.block && super.equals(o); + if (o instanceof Quantify) { + Quantify f = (Quantify) o; + return super.equals(f); } return false; } + + public String toString() { + return "quantify " + arrayToString(targets()) + " = #" + block() + arrayToString(operands()); + } } - + /** - * A compound bytecode represents a bytecode that contains sequence of zero - * or more bytecodes. For example, the loop bytecode contains its loop body. - * The nested block of bytecodes is represented as a block identifier in the - * enclosing forest. + * Represents a type which may appear on the left of an assignment + * expression. Arrays, Records and References are the only valid types for + * an lval. * * @author David J. Pearce * */ - public static abstract class Branching extends Bytecode { - protected final String destination; + public static abstract class LVal { + protected T type; - public Branching(String destination, Type[] types, int[] targets, int... operands) { + public LVal(T t) { + this.type = t; + } + + public T rawType() { + return type; + } + } + + /** + * An LVal with array type. + * + * @author David J. Pearce + * + */ + public static final class ArrayLVal extends LVal { + public final int indexOperand; + + public ArrayLVal(Type.EffectiveArray t, int indexOperand) { + super(t); + this.indexOperand = indexOperand; + } + } + + /** + * An LVal with list type. + * + * @author David J. Pearce + * + */ + public static final class ReferenceLVal extends LVal { + public ReferenceLVal(Type.Reference t) { + super(t); + } + } + + /** + * An LVal with record type. + * + * @author David J. Pearce + * + */ + public static final class RecordLVal extends LVal { + public final String field; + + public RecordLVal(Type.EffectiveRecord t, String field) { + super(t); + this.field = field; + if (!t.fields().containsKey(field)) { + throw new IllegalArgumentException("invalid Record Type"); + } + } + } + + private static final class UpdateIterator implements Iterator { + private final ArrayList fields; + private final int[] operands; + private Type iter; + private int fieldIndex; + private int operandIndex; + private int index; + + public UpdateIterator(Type type, int level, int[] operands, ArrayList fields) { + this.fields = fields; + this.iter = type; + this.index = level; + this.operands = operands; + } + + public LVal next() { + Type raw = iter; + index--; + if (iter instanceof Type.Reference) { + Type.Reference ref = (Type.Reference) iter; + iter = ref.element(); + return new ReferenceLVal(ref); + } else if (iter instanceof Type.EffectiveArray) { + Type.EffectiveArray list = (Type.EffectiveArray) iter; + iter = list.element(); + return new ArrayLVal(list, operands[operandIndex++]); + } else if (iter instanceof Type.EffectiveRecord) { + Type.EffectiveRecord rec = (Type.EffectiveRecord) iter; + String field = fields.get(fieldIndex++); + iter = rec.fields().get(field); + return new RecordLVal(rec, field); + } else { + throw new IllegalArgumentException("Invalid type for Update: " + iter); + } + } + + public boolean hasNext() { + return index > 0; + } + + public void remove() { + throw new UnsupportedOperationException("UpdateIterator is unmodifiable"); + } + } + + /** + *

+ * Pops a compound structure, zero or more indices and a value from the + * stack and updates the compound structure with the given value. Valid + * compound structures are lists, dictionaries, strings, records and + * references. + *

+ *

+ * Ideally, this operation is done in-place, meaning the operation is + * constant time. However, to support Whiley's value semantics this bytecode + * may require (in some cases) a clone of the underlying data-structure. + * Thus, the worst-case runtime of this operation is linear in the size of + * the compound structure. + *

+ * + * @author David J. Pearce + * + */ + public static final class Update extends Bytecode implements Iterable { + public final ArrayList fields; + + /** + * Construct an Update bytecode which assigns to a given operand to a + * set of target registers. For indirect map/list updates, an additional + * set of operands is used to generate the appropriate keys. For field + * assignments, a set of fields is provided. + * + * @param beforeType + * @param target + * Register being assigned + * @param operands + * Registers used for keys on left-hand side in map/list + * updates + * @param operand + * Register on right-hand side whose value is assigned + * @param afterType + * @param fields + * Fields for record updates + */ + private Update(Type beforeType, int target, int[] operands, int operand, Type afterType, + Collection fields) { + super(new Type[] { beforeType, afterType }, new int[] { target }, append(operands, operand)); + if (fields == null) { + throw new IllegalArgumentException("FieldStore fields argument cannot be null"); + } + this.fields = new ArrayList(fields); + } + + // Helper used for clone() + private Update(Type[] types, int[] targets, int[] operands, Collection fields) { super(types, targets, operands); - this.destination = destination; + if (fields == null) { + throw new IllegalArgumentException("FieldStore fields argument cannot be null"); + } + this.fields = new ArrayList(fields); } - public String destination() { - return destination; + public int opcode() { + return OPCODE_update; } - - public int hashCode() { - return super.hashCode() ^ destination.hashCode(); + + /** + * Returns register from which assigned value is read. This is also + * known as the "right-hand side". + * + * @return + */ + public int result() { + return operands()[operands().length - 1]; } - + + /** + * Get the given key register (in order of appearance from the left) + * used in a map or list update. + * + * @param index + * @return + */ + public int key(int index) { + return operands()[index]; + } + + /** + * Return the registers used to hold key values for map or list updates. + * + * @return + */ + public int[] keys() { + return Arrays.copyOf(operands(), operands().length - 1); + } + + public int level() { + int base = -1; // because last operand is rhs + if (type(0) instanceof Type.Reference) { + base++; + } + return base + fields.size() + operands().length; + } + + public Iterator iterator() { + return new UpdateIterator(afterType(), level(), keys(), fields); + } + + public Type afterType() { + return types[1]; + } + + /** + * Extract the type for the right-hand side of this assignment. + * + * @return + */ + public Type rhs() { + Type iter = afterType(); + + int fieldIndex = 0; + for (int i = 0; i != level(); ++i) { + if (iter instanceof Type.Reference) { + Type.Reference proc = Type.effectiveReference(iter); + iter = proc.element(); + } else if (iter instanceof Type.EffectiveArray) { + Type.EffectiveArray list = (Type.EffectiveArray) iter; + iter = list.element(); + } else if (iter instanceof Type.EffectiveRecord) { + Type.EffectiveRecord rec = (Type.EffectiveRecord) iter; + String field = fields.get(fieldIndex++); + iter = rec.fields().get(field); + } else { + throw new IllegalArgumentException("Invalid type for Update: " + iter); + } + } + return iter; + } + + @Override + public final Bytecode clone(int[] nTargets, int[] nOperands) { + return new Update(types, nTargets, nOperands, fields); + } + public boolean equals(Object o) { - if(o instanceof Branching) { - Branching abc = (Branching) o; - return destination.equals(abc.destination) && super.equals(o); + if (o instanceof Update) { + Update i = (Update) o; + return super.equals(o) && fields.equals(i.fields); + } + return false; + } + + public String toString() { + String r = "%" + target(0); + for (LVal lv : this) { + if (lv instanceof ArrayLVal) { + ArrayLVal l = (ArrayLVal) lv; + r = r + "[%" + l.indexOperand + "]"; + } else if (lv instanceof RecordLVal) { + RecordLVal l = (RecordLVal) lv; + r = r + "." + l.field; + } else { + ReferenceLVal l = (ReferenceLVal) lv; + r = "(*" + r + ")"; + } + } + return "update " + r + " = %" + result() + " : " + type(0) + " -> " + afterType(); + } + } + + /** + * Returns from the enclosing function or method, possibly returning a + * value. For example, the following Whiley code: + * + *
+	 * function f(int x, int y) -> int:
+	 *     return x + y
+	 * 
+ * + * can be translated into the following WyIL: + * + *
+	 * function f(int x, int y) -> int:
+	 * body:
+	 *     assign %3 = %0    : int
+	 *     assign %4 = %1    : int
+	 *     add %2 = % 3, %4  : int
+	 *     return %2         : int
+	 * 
+ * + * Here, the return bytecode returns the value of its operand + * register. + * + * @author David J. Pearce + * + */ + public static final class Return extends Bytecode { + + private Return(Type[] types, int... operands) { + super(types, new int[0], operands); + } + + @Override + public int opcode() { + return OPCODE_return; + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return new Return(Arrays.copyOf(types, types.length), nOperands); + } + + public boolean equals(Object o) { + if (o instanceof Return) { + return super.equals(o); + } + return false; + } + + public String toString() { + String r = "return"; + for (int i = 0; i != operands.length; ++i) { + if (i != 0) { + r += ","; + } + r += " %" + operands[i]; + } + return r; + } + } + + /** + * Performs a multi-way branch based on the value contained in the operand + * register. A dispatch table is provided which maps individual + * matched values to their destination labels. For example, the following + * Whiley code: + * + *
+	 * function f(int x) -> string:
+	 *     switch x:
+	 *         case 1:
+	 *             return "ONE"
+	 *         case 2:
+	 *             return "TWO"
+	 *         default:
+	 *             return "OTHER"
+	 * 
+ * + * can be translated into the following WyIL code: + * + *
+	 * function f(int x) -> string:
+	 * body:
+	 *     switch %0 1->blklab1, 2->blklab2, *->blklab3
+	 * .blklab1
+	 *     const %1 = "ONE" : string
+	 *     return %1 : string
+	 * .blklab2
+	 *     const %1 = "TWO" : string
+	 *     return %1 : string
+	 * .blklab3
+	 *     const %1 = "OTHER" : string
+	 *     return %1 : string
+	 * 
+ * + * Here, we see how e.g. value 1 is mapped to the label + * blklab1. Thus, if the operand register %0 + * contains value 1, then control will be transferred to that + * label. The final mapping *->blklab3 covers the default case + * where the value in the operand is not otherwise matched. + * + * @author David J. Pearce + * + */ + public static final class Switch extends Bytecode { + public final ArrayList> branches; + public final String defaultTarget; + + Switch(Type type, int operand, String defaultTarget, Collection> branches) { + super(new Type[] { type }, new int[0], operand); + this.branches = new ArrayList>(branches); + this.defaultTarget = defaultTarget; + } + + @Override + public int opcode() { + return OPCODE_switch; + } + + public Switch relabel(Map labels) { + ArrayList> nbranches = new ArrayList(); + for (Pair p : branches) { + String nlabel = labels.get(p.second()); + if (nlabel == null) { + nbranches.add(p); + } else { + nbranches.add(new Pair(p.first(), nlabel)); + } + } + + String nlabel = labels.get(defaultTarget); + if (nlabel == null) { + return Switch(types[0], operands[0], defaultTarget, nbranches); + } else { + return Switch(types[0], operands[0], nlabel, nbranches); + } + } + + public boolean equals(Object o) { + if (o instanceof Switch) { + Switch ig = (Switch) o; + return operands[0] == ig.operands[0] && defaultTarget.equals(ig.defaultTarget) + && branches.equals(ig.branches) && types[0].equals(ig.types[0]); } return false; } + + public String toString() { + String table = ""; + boolean firstTime = true; + for (Pair p : branches) { + if (!firstTime) { + table += ", "; + } + firstTime = false; + table += p.first() + "->" + p.second(); + } + table += ", *->" + defaultTarget; + return "switch %" + operands[0] + " " + table; + } + + @Override + public Bytecode clone(int[] nTargets, int[] nOperands) { + return new Switch(types[0], nOperands[0], defaultTarget, branches); + } + + } + + // ============================================================= + // Helpers + // ============================================================= + private static int[] append(int[] operands, int operand) { + int[] noperands = Arrays.copyOf(operands, operands.length + 1); + noperands[operands.length] = operand; + return noperands; + } + + private static int[] append(int operand, int[] operands) { + int[] noperands = new int[operands.length + 1]; + System.arraycopy(operands, 0, noperands, 1, operands.length); + noperands[0] = operand; + return noperands; } // ========================================================================= - // Empty Bytecodes + // Opcodes // ========================================================================= public static final int OPCODE_goto = 1; @@ -307,9 +2236,7 @@ public boolean equals(Object o) { public static final int OPCODE_assume = 5; public static final int OPCODE_invariant = 6; - // ========================================================================= - // Unary Operators. - // ========================================================================= + // Unary Operators public static final int UNARY_OPERATOR = 7; public static final int OPCODE_debug = UNARY_OPERATOR+0; @@ -317,18 +2244,14 @@ public boolean equals(Object o) { public static final int OPCODE_ifis = UNARY_OPERATOR+2; public static final int OPCODE_switch = UNARY_OPERATOR+3; - // ========================================================================= // Unary Assignables - // ========================================================================= public static final int UNARY_ASSIGNABLE = UNARY_OPERATOR+5; public static final int OPCODE_fieldload = UNARY_ASSIGNABLE+8; public static final int OPCODE_convert = UNARY_ASSIGNABLE+9; public static final int OPCODE_const = UNARY_ASSIGNABLE+10; - // ========================================================================= // Binary Operators - // ========================================================================= public static final int BINARY_OPERATOR = UNARY_ASSIGNABLE+11; public static final int OPCODE_ifeq = BINARY_OPERATOR+0; @@ -338,9 +2261,7 @@ public boolean equals(Object o) { public static final int OPCODE_ifgt = BINARY_OPERATOR+4; public static final int OPCODE_ifge = BINARY_OPERATOR+5; - // ========================================================================= // Binary Assignables - // ========================================================================= public static final int BINARY_ASSIGNABLE = BINARY_OPERATOR+6; public static final int OPCODE_neg = BINARY_ASSIGNABLE+0; @@ -364,9 +2285,7 @@ public boolean equals(Object o) { public static final int OPCODE_newobject = BINARY_ASSIGNABLE+18; public static final int OPCODE_assign = BINARY_ASSIGNABLE+19; - // ========================================================================= // Nary Assignables - // ========================================================================= public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+20; public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java index 718b738585..bcabacbd88 100644 --- a/modules/wyil/src/wyil/lang/CodeUtils.java +++ b/modules/wyil/src/wyil/lang/CodeUtils.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.Map; -import wyil.lang.Codes.Comparator; +import wyil.lang.Bytecode.Comparator; public class CodeUtils { @@ -55,20 +55,20 @@ public static int[] remapOperands(Map binding, int[] operands) * @param cop * @return */ - public static Codes.Comparator invert(Codes.Comparator cop) { + public static Bytecode.Comparator invert(Bytecode.Comparator cop) { switch (cop) { case EQ: - return Codes.Comparator.NEQ; + return Bytecode.Comparator.NEQ; case NEQ: - return Codes.Comparator.EQ; + return Bytecode.Comparator.EQ; case LT: - return Codes.Comparator.GTEQ; + return Bytecode.Comparator.GTEQ; case LTEQ: - return Codes.Comparator.GT; + return Bytecode.Comparator.GT; case GT: - return Codes.Comparator.LTEQ; + return Bytecode.Comparator.LTEQ; case GTEQ: - return Codes.Comparator.LT; + return Bytecode.Comparator.LT; } return null; } @@ -87,28 +87,13 @@ public static Map buildLabelMap(CodeForest forest) { CodeForest.Block block = forest.get(i); for (int j = 0; j != block.size(); ++j) { Bytecode code = block.get(j).code(); - if (code instanceof Codes.Label) { + if (code instanceof Bytecode.Label) { // Found a label, so register it in the labels map - Codes.Label label = (Codes.Label) code; + Bytecode.Label label = (Bytecode.Label) code; labels.put(label.label, new CodeForest.Index(i, j)); } } } return labels; } - - /** - * Helper function for buildLabelMap - * - * @param index - * Current block index being traversed. - * @param labels - * Labels map being constructed - * @param block - * Root block - */ - private static void buildLabelMap(int blockID, Map labels, CodeForest forest) { - // - - } } diff --git a/modules/wyil/src/wyil/lang/Codes.java b/modules/wyil/src/wyil/lang/Codes.java deleted file mode 100644 index fb6ae00cde..0000000000 --- a/modules/wyil/src/wyil/lang/Codes.java +++ /dev/null @@ -1,1974 +0,0 @@ -package wyil.lang; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import wycc.lang.NameID; -import wycc.util.Pair; -import wyil.lang.Bytecode.*; -import static wyil.lang.Bytecode.*; -import static wyil.lang.CodeUtils.*; - -public abstract class Codes { - - // =============================================================== - // Bytecode Constructors - // =============================================================== - - /** - * Construct an assert bytecode which represents a user-defined - * assertion check. - * - * @param message - * --- message to report upon failure. - * @return - */ - public static Assert Assert(int block) { - return new Assert(block); - } - - /** - * Construct an assume bytecode which represents a user-defined - * assumption. - * - * @param message - * --- message to report upon failure. - * @return - */ - public static Assume Assume(int block) { - return new Assume(block); - } - - public static Operator Operator(Type type, int[] targets, int[] operands, OperatorKind op) { - return new Operator(type, targets, operands, op); - } - - /** - * Construct a const bytecode which loads a given constant onto - * the stack. - * - * @param afterType - * --- record type. - * @param field - * --- field to write. - * @return - */ - public static Const Const(int target, Constant constant) { - return new Const(target, constant); - } - - public static Convert Convert(Type from, int target, int operand, Type to) { - return new Convert(from, target, operand, to); - } - - public static final Debug Debug(int operand) { - return new Debug(operand); - } - - /** - * Construct a fail bytecode which halts execution by raising a - * fault. - * - * @param string - * --- Message to give on error. - * @return - */ - public static Fail Fail() { - return new Fail(); - } - - /** - * Construct a fieldload bytecode which reads a given field - * from a record of a given type. - * - * @param type - * --- record type. - * @param field - * --- field to load. - * @return - */ - public static FieldLoad FieldLoad(Type.EffectiveRecord type, int target, - int operand, String field) { - return new FieldLoad(type, target, operand, field); - } - - /** - * Construct a goto bytecode which branches unconditionally to - * a given label. - * - * @param label - * --- destination label. - * @return - */ - public static Goto Goto(String label) { - return new Goto(label); - } - - public static Invoke Invoke(Type.FunctionOrMethod fun, Collection targets, Collection operands, - NameID name) { - return new Invoke(fun, CodeUtils.toIntArray(targets), CodeUtils.toIntArray(operands), name); - } - - public static Invoke Invoke(Type.FunctionOrMethod fun, int[] targets, - int[] operands, NameID name) { - return new Invoke(fun, targets, operands, name); - } - - /** - * Construct an invariant bytecode which represents a user-defined - * loop invariant. - * - * @param message - * --- message to report upon failure. - * @return - */ - public static Invariant Invariant(int block) { - return new Invariant(block); - } - - public static Lambda Lambda(Type.FunctionOrMethod fun, int target, - Collection operands, NameID name) { - return new Lambda(fun, target, CodeUtils.toIntArray(operands), name); - } - - public static Lambda Lambda(Type.FunctionOrMethod fun, int target, - int[] operands, NameID name) { - return new Lambda(fun, target, operands, name); - } - - public static Loop Loop(int[] modifiedOperands, int block) { - return new Loop(modifiedOperands,block); - } - - /** - * Construct a return bytecode which does return a value and, hence, its - * type automatically defaults to void. - * - * @return - */ - public static Return Return() { - return new Return(new Type[0]); - } - - /** - * Construct a return bytecode which reads a value from the operand register - * and returns it. - * - * @param type - * --- type of the value to be returned (cannot be void). - * @param operand - * --- register to read return value from. - * @return - */ - public static Return Return(Type[] types, int... operands) { - return new Return(types, operands); - } - - public static If If(Type type, int leftOperand, int rightOperand, - Comparator cop, String label) { - return new If(type, leftOperand, rightOperand, cop, label); - } - - public static IfIs IfIs(Type type, int leftOperand, Type rightOperand, - String label) { - return new IfIs(type, leftOperand, rightOperand, label); - } - - public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, - int[] targets, int operand, Collection operands) { - return new IndirectInvoke(fun, targets, operand, CodeUtils - .toIntArray(operands)); - } - - public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, - int[] targets, int operand, int[] operands) { - return new IndirectInvoke(fun, targets, operand, operands); - } - - public static Label Label(String label) { - return new Label(label); - } - - /** - * Construct a switch bytecode which pops a value off the - * stack, and switches to a given label based on it. - * - * @param type - * --- value type to switch on. - * @param defaultLabel - * --- target for the default case. - * @param cases - * --- map from values to destination labels. - * @return - */ - public static Switch Switch(Type type, int operand, String defaultLabel, - Collection> cases) { - return new Switch(type, operand, defaultLabel, cases); - } - - public static Quantify Quantify(int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, - int block) { - return new Quantify(startOperand, endOperand, indexOperand, modifiedOperands, block); - } - - public static Update Update(Type beforeType, int target, - Collection operands, int operand, Type afterType, - Collection fields) { - return new Update(beforeType, target, - CodeUtils.toIntArray(operands), operand, afterType, fields); - } - - public static Update Update(Type beforeType, int target, int[] operands, - int operand, Type afterType, Collection fields) { - return new Update(beforeType, target, operands, operand, - afterType, fields); - } - - // =============================================================== - // Bytecode Implementations - // =============================================================== - - /** - * Represents a binary operator (e.g. '+','-',etc) that is provided to a - * BinOp bytecode. - * - * @author David J. Pearce - * - */ - public enum OperatorKind { - // Unary - NEG(0) { - public String toString() { - return "neg"; - } - }, - BITWISEINVERT(1) { - public String toString() { - return "invert"; - } - }, - DEREFERENCE(2) { - public String toString() { - return "deref"; - } - }, - ARRAYLENGTH(3) { - public String toString() { - return "length"; - } - }, - // Binary - ADD(4) { - public String toString() { - return "add"; - } - }, - SUB(5) { - public String toString() { - return "sub"; - } - }, - MUL(6) { - public String toString() { - return "mul"; - } - }, - DIV(7) { - public String toString() { - return "div"; - } - }, - REM(8) { - public String toString() { - return "rem"; - } - }, - BITWISEOR(9) { - public String toString() { - return "or"; - } - }, - BITWISEXOR(10) { - public String toString() { - return "xor"; - } - }, - BITWISEAND(11) { - public String toString() { - return "and"; - } - }, - LEFTSHIFT(12) { - public String toString() { - return "shl"; - } - }, - RIGHTSHIFT(13) { - public String toString() { - return "shr"; - } - }, - ARRAYINDEX(14) { - public String toString() { - return "indexof"; - } - }, - ARRAYGENERATOR(15) { - public String toString() { - return "arraygen"; - } - }, - ARRAYCONSTRUCTOR(16) { - public String toString() { - return "array"; - } - }, - RECORDCONSTRUCTOR(17) { - public String toString() { - return "record"; - } - }, - NEW(18) { - public String toString() { - return "new"; - } - }, - ASSIGN(19) { - public String toString() { - return "assign"; - } - }; - public int offset; - - private OperatorKind(int offset) { - this.offset = offset; - } - }; - - /** - *

- * A binary operation which reads two numeric values from the operand - * registers, performs an operation on them and writes the result to the - * target register. The binary operators are: - *

- *
    - *
  • add, subtract, multiply, divide, remainder. Both operands must - * be either integers or reals (but not one or the other). A value of the - * same type is produced.
  • - *
  • bitwiseor, bitwisexor, bitwiseand
  • - *
  • leftshift,rightshift
  • - *
- * For example, the following Whiley code: - * - *
-	 * function f(int x, int y) -> int:
-	 *     return ((x * y) + 1) / 2
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x, int y) -> int:
-	 * body:
-	 *     mul %2 = %0, %1   : int
-	 *     const %3 = 1      : int
-	 *     add %2 = %2, %3   : int
-	 *     const %3 = 2      : int
-	 *     const %4 = 0      : int
-	 *     assertne %3, %4 "division by zero" : int
-	 *     div %2 = %2, %3   : int
-	 *     return %2         : int
-	 * 
- * - * Here, the assertne bytecode has been included to check - * against division-by-zero. In this particular case the assertion is known - * true at compile time and, in practice, would be compiled away. - * - * @author David J. Pearce - * - */ - public static final class Operator extends Bytecode { - public final OperatorKind kind; - - private Operator(Type type, int[] targets, int[] operands, - OperatorKind bop) { - super(new Type[]{ type }, targets, operands); - if (bop == null) { - throw new IllegalArgumentException( - "Operator kind cannot be null"); - } - this.kind = bop; - } - - @Override - public int opcode() { - return OPCODE_neg + kind.offset; - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return Operator(type(0), nTargets, nOperands, kind); - } - - public int hashCode() { - return kind.hashCode() + super.hashCode(); - } - - public boolean equals(Object o) { - if (o instanceof Operator) { - Operator bo = (Operator) o; - return kind.equals(bo.kind) && super.equals(bo); - } - return false; - } - - public String toString() { - return kind + " %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0); - } - } - - /** - * Reads a value from the operand register, converts it to a given type and - * writes the result to the target register. This bytecode is the only way - * to change the type of a value. It's purpose is to simplify - * implementations which have different representations of data types. A - * convert bytecode must be inserted whenever the type of a register - * changes. This includes at control-flow meet points, when the value is - * passed as a parameter, assigned to a field, etc. For example, the - * following Whiley code: - * - *
-	 * function f(int x) -> real:
-	 *     return x + 1
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x) -> real:
-	 * body:
-	 *     const %2 = 1           : int
-	 *     add %1 = %0, %2        : int
-	 *     convert %1 = %1 real   : int
-	 *     return %1              : real
-	 * 
- *

- * Here, we see that the int value in register %1 - * must be explicitly converted into a real value before it can - * be returned from this function. - *

- *

- * NOTE: In many cases, this bytecode may correspond to a nop on the - * hardware. Consider converting from [any] to any - * . On the JVM, any translates to Object, whilst - * [any] translates to List (which is an instance - * of Object). Thus, no conversion is necessary since - * List can safely flow into Object. - *

- * - */ - public static final class Convert extends Bytecode { - - private Convert(Type from, int target, int operand, Type result) { - super(new Type[]{from,result}, new int[]{target}, operand); - } - - public Bytecode clone(int[] nTargets, int[] nOperands) { - return Convert(type(0), nTargets[0], nOperands[0], type(1)); - } - - public Type result() { - return type(1); - } - - public int opcode() { - return OPCODE_convert; - } - - public boolean equals(Object o) { - return o instanceof Convert && super.equals(o); - } - - public String toString() { - return "convert %" + target(0) + " = %" + operand(0) + " " + result() + " : " + type(0); - } - } - - /** - * Writes a constant value to a target register. This includes - * integers, rationals, lists, sets, - * maps, etc. For example, the following Whiley code: - * - *
-	 * function f(int x) -> int:
-	 *     xs = {1,2.12}
-	 *     return |xs| + 1
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x) -> int:
-	 * body:
-	 *     var xs
-	 *     const %2 = 1               : int
-	 *     convert %2 = % 2 int|real  : int
-	 *     const %3 = 2.12            : real
-	 *     convert %3 = % 3 int|real  : real
-	 *     newset %1 = (%2, %3)       : {int|real}
-	 *     assign %3 = %1             : {int|real}
-	 *     lengthof %3 = % 3          : {int|real}
-	 *     const %4 = 1               : int
-	 *     add %2 = % 3, %4           : int
-	 *     return %2                  : int
-	 * 
- * - * Here, we see two kinds of constants values being used: integers (i.e. - * const %2 = 1) and rationals (i.e. - * const %3 = 2.12). - * - * @author David J. Pearce - * - */ - public static final class Const extends Bytecode { - public final Constant constant; - - private Const(int target, Constant constant) { - super(new Type[0],new int[]{target}, new int[0]); - this.constant = constant; - } - - public int opcode() { - return OPCODE_const; - } - - public int target() { - return targets()[0]; - } - - public int hashCode() { - return constant.hashCode() + targets()[0]; - } - - public boolean equals(Object o) { - if (o instanceof Const) { - Const c = (Const) o; - return constant.equals(c.constant) && Arrays.equals(targets(),c.targets()); - } - return false; - } - - public String toString() { - return "const %" + targets()[0] + " = " + constant + " : " - + constant.type(); - } - - @Override - protected Bytecode clone(int[] nTargets, int[] nOperands) { - return new Const(nTargets[0],constant); - } - } - - /** - * Read a string from the operand register and prints it to the debug - * console. For example, the following Whiley code: - * - *
-	 * method f(int x):
-	 *     debug "X = " + x
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * method f(int x):
-	 * body:
-	 *     const %2 = "X = "       : string
-	 *     convert %0 = %0 any     : int
-	 *     invoke %0 (%0) whiley/lang/Any:toString : string(any)
-	 *     strappend %1 = %2, %0   : string
-	 *     debug %1                : string
-	 *     return
-	 * 
- * - * NOTE This bytecode is not intended to form part of the program's - * operation. Rather, it is to facilitate debugging within functions (since - * they cannot have side-effects). Furthermore, if debugging is disabled, - * this bytecode is a nop. - * - * @author David J. Pearce - * - */ - public static final class Debug extends Bytecode { - - private Debug(int operand) { - super(new Type[]{Type.Array(Type.T_INT,false)}, new int[0], operand); - } - - public int opcode() { - return OPCODE_debug; - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return Debug(nOperands[0]); - } - - public boolean equals(Object o) { - return o instanceof Debug && super.equals(o); - } - - public String toString() { - return "debug %" + operands[0] + " " + " : " + types[0]; - } - } - - /** - * An abstract class representing either an assert or - * assume bytecode block. - * - * @author David J. Pearce - * - */ - public static abstract class AssertOrAssume extends Compound { - private AssertOrAssume(int block) { - super(block, new Type[0], new int[0],new int[0]); - } - } - /** - * Represents a block of bytecode instructions representing an assertion. - * - * @author David J. Pearce - * - */ - public static class Assert extends AssertOrAssume { - - private Assert(int block) { - super(block); - } - - public int opcode() { - return OPCODE_assert; - } - - public String toString() { - return "assert #" + block; - } - - public boolean equals(Object o) { - return o instanceof Assume && super.equals(o); - } - - @Override - protected Bytecode clone(int[] nTargets, int[] nOperands) { - return this; - } - } - - /** - * Represents a block of bytecode instructions representing an assumption. - * - * @author David J. Pearce - * - */ - public static final class Assume extends AssertOrAssume { - - private Assume(int block) { - super(block); - } - - public int opcode() { - return OPCODE_assume; - } - - public String toString() { - return "assume #" + block; - } - - public boolean equals(Object o) { - return o instanceof Assume && super.equals(o); - } - - @Override - protected Bytecode clone(int[] nTargets, int[] nOperands) { - return this; - } - } - - /** - * A bytecode that halts execution by raising a runtime fault. This bytecode - * signals that some has gone wrong, and is typically used to signal an - * assertion failure. - * - * @author David J. Pearce - * - */ - public static final class Fail extends Bytecode { - private Fail() { - super(new Type[0],new int[0]); - } - - @Override - public int opcode() { - return OPCODE_fail; - } - - @Override - protected Bytecode clone(int[] nTargets, int[] nOperands) { - return this; - } - - public String toString() { - return "fail"; - } - - - } - - /** - * Reads a record value from an operand register, extracts the value of a - * given field and writes this to the target register. For example, the - * following Whiley code: - * - *
-	 * type Point is {int x, int y}
-	 *
-	 * function f(Point p) -> int:
-	 *     return p.x + p.y
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f({int x,int y} p) -> int:
-	 * body:
-	 *     fieldload %2 = %0 x    : {int x,int y}
-	 *     fieldload %3 = %0 y    : {int x,int y}
-	 *     add %1 = %2, %3        : int
-	 *     return %1              : int
-	 * 
- * - * @author David J. Pearce - * - */ - public static final class FieldLoad extends Bytecode{ - public final String field; - - private FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { - super((Type) type, target, operand); - if (field == null) { - throw new IllegalArgumentException( - "FieldLoad field argument cannot be null"); - } - this.field = field; - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return FieldLoad((Type.EffectiveRecord) type(0), nTargets[0], nOperands[0], field); - } - - public int opcode() { - return OPCODE_fieldload; - } - - public int hashCode() { - return super.hashCode() + field.hashCode(); - } - - public Type fieldType() { - Type.EffectiveRecord er = (Type.EffectiveRecord) type(0); - return er.fields().get(field); - } - - public boolean equals(Object o) { - if (o instanceof FieldLoad) { - FieldLoad i = (FieldLoad) o; - return super.equals(i) && field.equals(i.field); - } - return false; - } - - public String toString() { - return "fieldload %" + target(0) + " = %" + operand(0) + " " + field - + " : " + type(0); - } - } - - /** - * Branches unconditionally to the given label. This is typically used for - * if/else statements. For example, the following Whiley code: - * - *
-	 * function f(int x) -> int:
-	 *     if x >= 0:
-	 *         x = 1
-	 *     else:
-	 *         x = -1
-	 *     return x
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x) -> int:
-	 * body:
-	 *     const %1 = 0             : int
-	 *     iflt %0, %1 goto blklab0 : int
-	 *     const %0 = 1             : int
-	 *     goto blklab1
-	 * .blklab0
-	 *     const %0 = 1             : int
-	 *     neg %0 = % 0             : int
-	 * .blklab1
-	 *     return %0                : int
-	 * 
- * - * Here, we see the goto bytecode being used to jump from the - * end of the true branch over the false branch. - * - *

- * Note: in WyIL bytecode, such branches may only go forward. - * Thus, a goto bytecode cannot be used to implement the - * back-edge of a loop. Rather, a loop block must be used for this purpose. - *

- * - * @author David J. Pearce - * - */ - public static final class Goto extends Branching { - private Goto(String target) { - super(target,new Type[0],new int[0]); - } - - public int opcode() { - return OPCODE_goto; - } - - public Goto relabel(Map labels) { - String nlabel = labels.get(destination()); - if (nlabel == null) { - return this; - } else { - return Goto(nlabel); - } - } - - public boolean equals(Object o) { - return o instanceof Goto && super.equals(o); - } - - @Override - protected Bytecode clone(int[] nTargets, int[] nOperands) { - return this; - } - - public String toString() { - return "goto " + destination(); - } - } - - /** - *

- * Branches conditionally to the given label by reading the values from two - * operand registers and comparing them. The possible comparators are: - *

- *
    - *
  • equals (eq) and not-equals (ne). Both operands must have the - * given type.
  • - *
  • less-than (lt), less-than-or-equals (le), greater-than (gt) and - * great-than-or-equals (ge). Both operands must have the given type, - * which additionally must by either char, int or - * real.
  • - *
  • element of (in). The second operand must be a set whose - * element type is that of the first.
  • - *
- * For example, the following Whiley code: - * - *
-	 * function f(int x, int y) -> int:
-	 *     if x < y:
-	 *         return -1
-	 *     else if x > y:
-	 *         return 1
-	 *     else:
-	 *         return 0
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x, int y) -> int:
-	 * body:
-	 *     ifge %0, %1 goto blklab0 : int
-	 *     const %2 = -1 : int
-	 *     return %2 : int
-	 * .blklab0
-	 *     ifle %0, %1 goto blklab2 : int
-	 *     const %2 = 1 : int
-	 *     return %2 : int
-	 * .blklab2
-	 *     const %2 = 0 : int
-	 *     return %2 : int
-	 * 
- * - * Note: in WyIL bytecode, such branches may only go forward. - * Thus, an ifgoto bytecode cannot be used to implement the - * back-edge of a loop. Rather, a loop block must be used for this purpose. - * - * @author David J. Pearce - * - */ - public static final class If extends Branching { - public final Comparator op; - - private If(Type type, int leftOperand, int rightOperand, Comparator op, - String target) { - super(target,new Type[]{type}, new int[0], leftOperand, rightOperand); - this.op = op; - } - - public If relabel(Map labels) { - String nlabel = labels.get(destination()); - if (nlabel == null) { - return this; - } else { - return If(types[0], operands[0], operands[1], op, nlabel); - } - } - - public int opcode() { - return OPCODE_ifeq + op.offset; - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return If(types[0], nOperands[0], nOperands[1], op, destination()); - } - - public int hashCode() { - return super.hashCode() + op.hashCode(); - } - - public boolean equals(Object o) { - if (o instanceof If) { - If ig = (If) o; - return op == ig.op && super.equals(ig); - } - return false; - } - - public String toString() { - return "if" + op + " %" + operands[0] + ", %" + operands[1] + " goto " + destination() + " : " + types[0]; - } - } - - /** - * Represents a comparison operator (e.g. '==','!=',etc) that is provided to - * a IfGoto bytecode. - * - * @author David J. Pearce - * - */ - public enum Comparator { - EQ(0) { - public String toString() { - return "eq"; - } - }, - NEQ(1) { - public String toString() { - return "ne"; - } - }, - LT(2) { - public String toString() { - return "lt"; - } - }, - LTEQ(3) { - public String toString() { - return "le"; - } - }, - GT(4) { - public String toString() { - return "gt"; - } - }, - GTEQ(5) { - public String toString() { - return "ge"; - } - }; - public int offset; - - private Comparator(int offset) { - this.offset = offset; - } - }; - - /** - * Branches conditionally to the given label based on the result of a - * runtime type test against a value from the operand register. More - * specifically, it checks whether the value is a subtype of the type test. - * The operand register is automatically retyped as a result of the - * type test. On the true branch, its type is intersected with type test. On - * the false branch, its type is intersected with the negation of the - * type test. For example, the following Whiley code: - * - *
-	 * function f(int|int[] x) -> int:
-	 *     if x is int[]:
-	 *         return |x|
-	 *     else:
-	 *         return x
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int|int[] x) -> int:
-	 * body:
-	 *     ifis %0, int[] goto lab    : int|int[]
-	 *     return %0                  : int
-	 * .lab
-	 *     lengthof %0 = %0           : int[]
-	 *     return %0                  : int
-	 * 
- * - * Here, we see that, on the false branch, register %0 is - * automatically given type int, whilst on the true branch it - * is automatically given type int[]. - * - *

- * Note: in WyIL bytecode, such branches may only go forward. - * Thus, an ifis bytecode cannot be used to implement the - * back-edge of a loop. Rather, a loop block must be used for this purpose. - *

- * - * @author David J. Pearce - * - */ - public static final class IfIs extends Branching { - private IfIs(Type type, int leftOperand, Type rightOperand, String target) { - super(target, new Type[] { type, rightOperand }, new int[0], leftOperand); - } - - public int opcode() { - return OPCODE_ifis; - } - - public Type rightOperand() { - return type(1); - } - - public IfIs relabel(Map labels) { - String nlabel = labels.get(destination()); - if (nlabel == null) { - return this; - } else { - return IfIs(types[0], operands[0], types[1], nlabel); - } - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return IfIs(types[0], nOperands[0], types[1], destination()); - } - - public boolean equals(Object o) { - return o instanceof IfIs && super.equals(o); - } - - public String toString() { - return "ifis" + " %" + operands[0] + ", " + types[1] + " goto " + destination() + " : " + types[0]; - } - } - - /** - * Represents an indirect function call. For example, consider the - * following: - * - *
-	 * function fun(function (int)->int f, int x) -> int:
-	 *    return f(x)
-	 * 
- * - * Here, the function call f(x) is indirect as the called - * function is determined by the variable f. - * - * @author David J. Pearce - * - */ - public static final class IndirectInvoke extends Bytecode { - - /** - * Construct an indirect invocation bytecode which assigns to an - * optional target register the result from indirectly invoking a - * function in a given operand with a given set of parameter operands. - * - * @param type - * @param target Register (optional) to which result of invocation is assigned. - * @param operand Register holding function point through which indirect invocation is made. - * @param operands Registers holding parameters for the invoked function - */ - private IndirectInvoke(Type.FunctionOrMethod type, int[] targets, - int operand, int[] operands) { - super(new Type.FunctionOrMethod[]{type}, targets, append(operand,operands)); - } - - /** - * Return register holding the indirect function/method reference. - * - * @return - */ - public int reference() { - return operands()[0]; - } - - /** - * Return register holding the ith parameter for the invoked function. - * - * @param i - * @return - */ - public int parameter(int i) { - return operands()[i + 1]; - } - - /** - * Return registers holding parameters for the invoked function. - * - * @param i - * @return - */ - public int[] parameters() { - return Arrays.copyOfRange(operands(),1,operands().length); - } - - public int opcode() { - return OPCODE_indirectinvoke; - } - - @Override - public Type.FunctionOrMethod type(int i) { - return (Type.FunctionOrMethod) super.type(i); - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return IndirectInvoke(type(0), nTargets, nOperands[0], - Arrays.copyOfRange(nOperands, 1, nOperands.length)); - } - - public boolean equals(Object o) { - return o instanceof IndirectInvoke && super.equals(o); - } - - public String toString() { - return "indirectinvoke " + arrayToString(targets()) + " = %" + reference() + " " - + arrayToString(parameters()) + " : " + type(0); - } - } - - /** - * Represents a block of bytecode instructions representing an assertion. - * - * @author David J. Pearce - * - */ - public static class Invariant extends Assert { - - private Invariant(int block) { - super(block); - } - - public int opcode() { - return OPCODE_invariant; - } - - public String toString() { - return "invariant"; - } - - public int hashCode() { - return block; - } - - public boolean equals(Object o) { - if (o instanceof Invariant) { - Invariant f = (Invariant) o; - return block == f.block; - } - return false; - } - - @Override - public Invariant clone() { - return this; - } - } - - /** - * Corresponds to a function or method call whose parameters are read from - * zero or more operand registers. If a return value is required, this is - * written to a target register afterwards. For example, the following - * Whiley code: - * - *
-	 * function g(int x, int y, int z) -> int:
-	 *     return x * y * z
-	 *
-	 * function f(int x, int y) -> int:
-	 *     r = g(x,y,3)
-	 *     return r + 1
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function g(int x, int y, int z) -> int:
-	 * body:
-	 *     mul %3 = %0, %1   : int
-	 *     mul %3 = %3, %2   : int
-	 *     return %3         : int
-	 *
-	 * function f(int x, int y) -> int:
-	 * body:
-	 *     const %2 = 3                    : int
-	 *     invoke %2 = (%0, %1, %2) test:g   : int(int,int,int)
-	 *     const %3 = 1                    : int
-	 *     add %2 = (%2, %3)                : int
-	 *     return %2                       : int
-	 * 
- * - * Here, we see that arguments to the invoke bytecode are - * supplied in the order they are given in the function or method's - * declaration. - * - * @author David J. Pearce - * - */ - public static final class Invoke extends Bytecode { - public final NameID name; - - private Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, - NameID name) { - super(new Type.FunctionOrMethod[]{type}, targets, operands); - this.name = name; - } - - public int opcode() { - return OPCODE_invoke; - } - - public int hashCode() { - return name.hashCode() + super.hashCode(); - } - - @Override - public Type.FunctionOrMethod type(int i) { - return (Type.FunctionOrMethod) super.type(i); - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return Invoke(type(0), nTargets, nOperands, name); - } - - public boolean equals(Object o) { - if (o instanceof Invoke) { - Invoke i = (Invoke) o; - return name.equals(i.name) && super.equals(i); - } - return false; - } - - public String toString() { - return "invoke " + arrayToString(targets()) + " = " + arrayToString(operands()) + " " + name + " : " - + type(0); - } - } - - public static final class Lambda extends Bytecode { - public final NameID name; - - private Lambda(Type.FunctionOrMethod type, int target, int[] operands, - NameID name) { - super(type, target, operands); - this.name = name; - } - - public int opcode() { - return OPCODE_lambda; - } - - public int hashCode() { - return name.hashCode() + super.hashCode(); - } - - @Override - public Type.FunctionOrMethod type(int i) { - return (Type.FunctionOrMethod) super.type(i); - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return Lambda((Type.FunctionOrMethod) type(0), nTargets[0], nOperands, name); - } - - public boolean equals(Object o) { - if (o instanceof Lambda) { - Lambda i = (Lambda) o; - return name.equals(i.name) && super.equals(i); - } - return false; - } - - public String toString() { - return "lambda %" + target(0) + " = " + arrayToString(operands()) + " " - + name + " : " + type(0); - } - } - - /** - * Represents the labelled destination of a branch or loop statement. - * - * @author David J. Pearce - * - */ - public static class Label extends Bytecode { - public final String label; - - private Label(String label) { - super(new Type[0],new int[0],new int[0]); - this.label = label; - } - - public int opcode() { - return -1; - } - - public Label relabel(Map labels) { - String nlabel = labels.get(label); - if (nlabel == null) { - return this; - } else { - return Label(nlabel); - } - } - - @Override - public Bytecode remap(Map binding) { - return this; - } - - public int hashCode() { - return label.hashCode(); - } - - public boolean equals(Object o) { - if (o instanceof Label) { - return label.equals(((Label) o).label); - } - return false; - } - - public String toString() { - return "." + label; - } - - @Override - public void registers(Set register) { - // TODO Auto-generated method stub - } - - - @Override - protected Bytecode clone(int[] nTargets, int[] nOperands) { - return new Label(label); - } - } - - /** - * Represents a block of code which loops continuously until e.g. a - * conditional branch is taken out of the block. For example: - * - *
-	 * function f() -> int:
-	 *     r = 0
-	 *     while r < 10:
-	 *         r = r + 1
-	 *     return r
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f() -> int:
-	 * body:
-	 *     const %0 = 0             : int
-	 *     loop (%0)
-	 *         const %1 = 10        : int
-	 *         ifge %0, %1 goto blklab0 : int
-	 *         const %1 = 1         : int
-	 *         add %0 = %0, %1      : int
-	 * .blklab0
-	 *     return %0                : int
-	 * 
- * - *

- * Here, we see a loop which increments an accumulator register - * %0 until it reaches 10, and then exits the loop - * block. - *

- *

- * The modified operands of a loop bytecode (shown in brackets - * alongside the bytecode) indicate those operands which are modified at - * some point within the loop. - *

- * - * @author David J. Pearce - * - */ - public static class Loop extends Compound { - - private Loop(int[] targets, int block, int... operands) { - super(block, new Type[0], targets, operands); - } - - public int opcode() { - return OPCODE_loop; - } - - public boolean equals(Object o) { - if (o instanceof Loop) { - Loop f = (Loop) o; - return block == f.block && super.equals(f); - } - return false; - } - - public int[] modifiedOperands() { - return targets(); - } - - @Override - public Loop clone(int[] nTargets, int[] nOperands) { - return new Loop(nTargets, block, nOperands); - } - - public String toString() { - return "loop " + arrayToString(targets()) + " = " + block; - } - } - - public static final class Quantify extends Loop { - - private Quantify(int startOperand,int endOperand, - int indexOperand, int[] targets, int block) { - super(targets, block, startOperand, endOperand, indexOperand); - } - - public int opcode() { - return OPCODE_quantify; - } - - public int startOperand() { - return operands[0]; - } - - public int endOperand() { - return operands[1]; - } - - public int indexOperand() { - return operands[2]; - } - - public boolean equals(Object o) { - if (o instanceof Quantify) { - Quantify f = (Quantify) o; - return super.equals(f); - } - return false; - } - - public String toString() { - return "quantify " + arrayToString(targets()) + " = #" + block() + arrayToString(operands()); - } - } - - /** - * Represents a type which may appear on the left of an assignment - * expression. Arrays, Records and References are the - * only valid types for an lval. - * - * @author David J. Pearce - * - */ - public static abstract class LVal { - protected T type; - - public LVal(T t) { - this.type = t; - } - - public T rawType() { - return type; - } - } - - /** - * An LVal with array type. - * - * @author David J. Pearce - * - */ - public static final class ArrayLVal extends LVal { - public final int indexOperand; - - public ArrayLVal(Type.EffectiveArray t, int indexOperand) { - super(t); - this.indexOperand = indexOperand; - } - } - - /** - * An LVal with list type. - * - * @author David J. Pearce - * - */ - public static final class ReferenceLVal extends LVal { - public ReferenceLVal(Type.Reference t) { - super(t); - } - } - - /** - * An LVal with record type. - * - * @author David J. Pearce - * - */ - public static final class RecordLVal extends LVal { - public final String field; - - public RecordLVal(Type.EffectiveRecord t, String field) { - super(t); - this.field = field; - if (!t.fields().containsKey(field)) { - throw new IllegalArgumentException("invalid Record Type"); - } - } - } - - private static final class UpdateIterator implements Iterator { - private final ArrayList fields; - private final int[] operands; - private Type iter; - private int fieldIndex; - private int operandIndex; - private int index; - - public UpdateIterator(Type type, int level, int[] operands, - ArrayList fields) { - this.fields = fields; - this.iter = type; - this.index = level; - this.operands = operands; - } - - public LVal next() { - Type raw = iter; - index--; - if (iter instanceof Type.Reference) { - Type.Reference ref = (Type.Reference) iter; - iter = ref.element(); - return new ReferenceLVal(ref); - } else if (iter instanceof Type.EffectiveArray) { - Type.EffectiveArray list = (Type.EffectiveArray) iter; - iter = list.element(); - return new ArrayLVal(list, operands[operandIndex++]); - } else if (iter instanceof Type.EffectiveRecord) { - Type.EffectiveRecord rec = (Type.EffectiveRecord) iter; - String field = fields.get(fieldIndex++); - iter = rec.fields().get(field); - return new RecordLVal(rec, field); - } else { - throw new IllegalArgumentException("Invalid type for Update: " + iter); - } - } - - public boolean hasNext() { - return index > 0; - } - - public void remove() { - throw new UnsupportedOperationException( - "UpdateIterator is unmodifiable"); - } - } - - /** - *

- * Pops a compound structure, zero or more indices and a value from the - * stack and updates the compound structure with the given value. Valid - * compound structures are lists, dictionaries, strings, records and - * references. - *

- *

- * Ideally, this operation is done in-place, meaning the operation is - * constant time. However, to support Whiley's value semantics this bytecode - * may require (in some cases) a clone of the underlying data-structure. - * Thus, the worst-case runtime of this operation is linear in the size of - * the compound structure. - *

- * - * @author David J. Pearce - * - */ - public static final class Update extends Bytecode - implements Iterable { - public final ArrayList fields; - - /** - * Construct an Update bytecode which assigns to a given operand to a - * set of target registers. For indirect map/list updates, an additional - * set of operands is used to generate the appropriate keys. For field - * assignments, a set of fields is provided. - * - * @param beforeType - * @param target - * Register being assigned - * @param operands - * Registers used for keys on left-hand side in map/list - * updates - * @param operand - * Register on right-hand side whose value is assigned - * @param afterType - * @param fields - * Fields for record updates - */ - private Update(Type beforeType, int target, int[] operands, - int operand, Type afterType, Collection fields) { - super(new Type[]{beforeType,afterType}, new int[]{target}, append(operands,operand)); - if (fields == null) { - throw new IllegalArgumentException( - "FieldStore fields argument cannot be null"); - } - this.fields = new ArrayList(fields); - } - - // Helper used for clone() - private Update(Type[] types, int[] targets, int[] operands, Collection fields) { - super(types,targets,operands); - if (fields == null) { - throw new IllegalArgumentException( - "FieldStore fields argument cannot be null"); - } - this.fields = new ArrayList(fields); - } - - public int opcode() { - return OPCODE_update; - } - - /** - * Returns register from which assigned value is read. This is also - * known as the "right-hand side". - * - * @return - */ - public int result() { - return operands()[operands().length-1]; - } - - /** - * Get the given key register (in order of appearance from the left) - * used in a map or list update. - * - * @param index - * @return - */ - public int key(int index) { - return operands()[index]; - } - - /** - * Return the registers used to hold key values for map or list updates. - * - * @return - */ - public int[] keys() { - return Arrays.copyOf(operands(),operands().length-1); - } - - public int level() { - int base = -1; // because last operand is rhs - if (type(0) instanceof Type.Reference) { - base++; - } - return base + fields.size() + operands().length; - } - - public Iterator iterator() { - return new UpdateIterator(afterType(), level(), keys(), fields); - } - - public Type afterType() { - return types[1]; - } - - /** - * Extract the type for the right-hand side of this assignment. - * - * @return - */ - public Type rhs() { - Type iter = afterType(); - - int fieldIndex = 0; - for (int i = 0; i != level(); ++i) { - if (iter instanceof Type.Reference) { - Type.Reference proc = Type.effectiveReference(iter); - iter = proc.element(); - } else if (iter instanceof Type.EffectiveArray) { - Type.EffectiveArray list = (Type.EffectiveArray) iter; - iter = list.element(); - } else if (iter instanceof Type.EffectiveRecord) { - Type.EffectiveRecord rec = (Type.EffectiveRecord) iter; - String field = fields.get(fieldIndex++); - iter = rec.fields().get(field); - } else { - throw new IllegalArgumentException("Invalid type for Update: " + iter); - } - } - return iter; - } - - @Override - public final Bytecode clone(int[] nTargets, int[] nOperands) { - return new Update(types, nTargets, nOperands, fields); - } - - public boolean equals(Object o) { - if (o instanceof Update) { - Update i = (Update) o; - return super.equals(o) && fields.equals(i.fields); - } - return false; - } - - public String toString() { - String r = "%" + target(0); - for (LVal lv : this) { - if (lv instanceof ArrayLVal) { - ArrayLVal l = (ArrayLVal) lv; - r = r + "[%" + l.indexOperand + "]"; - } else if (lv instanceof RecordLVal) { - RecordLVal l = (RecordLVal) lv; - r = r + "." + l.field; - } else { - ReferenceLVal l = (ReferenceLVal) lv; - r = "(*" + r + ")"; - } - } - return "update " + r + " = %" + result() + " : " + type(0) + " -> " + afterType(); - } - } - - /** - * Returns from the enclosing function or method, possibly returning a - * value. For example, the following Whiley code: - * - *
-	 * function f(int x, int y) -> int:
-	 *     return x + y
-	 * 
- * - * can be translated into the following WyIL: - * - *
-	 * function f(int x, int y) -> int:
-	 * body:
-	 *     assign %3 = %0    : int
-	 *     assign %4 = %1    : int
-	 *     add %2 = % 3, %4  : int
-	 *     return %2         : int
-	 * 
- * - * Here, the - * return bytecode returns the value of its operand register. - * - * @author David J. Pearce - * - */ - public static final class Return extends Bytecode { - - private Return(Type[] types, int... operands) { - super(types, new int[0], operands); - } - - @Override - public int opcode() { - return OPCODE_return; - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return new Return(Arrays.copyOf(types, types.length), nOperands); - } - - public boolean equals(Object o) { - if (o instanceof Return) { - return super.equals(o); - } - return false; - } - - public String toString() { - String r = "return"; - for(int i=0;i!=operands.length;++i) { - if(i!=0) { - r += ","; - } - r += " %" + operands[i]; - } - return r; - } - } - - /** - * Performs a multi-way branch based on the value contained in the operand - * register. A dispatch table is provided which maps individual - * matched values to their destination labels. For example, the following - * Whiley code: - * - *
-	 * function f(int x) -> string:
-	 *     switch x:
-	 *         case 1:
-	 *             return "ONE"
-	 *         case 2:
-	 *             return "TWO"
-	 *         default:
-	 *             return "OTHER"
-	 * 
- * - * can be translated into the following WyIL code: - * - *
-	 * function f(int x) -> string:
-	 * body:
-	 *     switch %0 1->blklab1, 2->blklab2, *->blklab3
-	 * .blklab1
-	 *     const %1 = "ONE" : string
-	 *     return %1 : string
-	 * .blklab2
-	 *     const %1 = "TWO" : string
-	 *     return %1 : string
-	 * .blklab3
-	 *     const %1 = "OTHER" : string
-	 *     return %1 : string
-	 * 
- * - * Here, we see how e.g. value 1 is mapped to the label - * blklab1. Thus, if the operand register %0 - * contains value 1, then control will be transferred to that - * label. The final mapping *->blklab3 covers the default case - * where the value in the operand is not otherwise matched. - * - * @author David J. Pearce - * - */ - public static final class Switch extends Bytecode { - public final ArrayList> branches; - public final String defaultTarget; - - Switch(Type type, int operand, String defaultTarget, - Collection> branches) { - super(new Type[]{type}, new int[0], operand); - this.branches = new ArrayList>(branches); - this.defaultTarget = defaultTarget; - } - - @Override - public int opcode() { - return OPCODE_switch; - } - - public Switch relabel(Map labels) { - ArrayList> nbranches = new ArrayList(); - for (Pair p : branches) { - String nlabel = labels.get(p.second()); - if (nlabel == null) { - nbranches.add(p); - } else { - nbranches.add(new Pair(p.first(), nlabel)); - } - } - - String nlabel = labels.get(defaultTarget); - if (nlabel == null) { - return Switch(types[0], operands[0], defaultTarget, nbranches); - } else { - return Switch(types[0], operands[0], nlabel, nbranches); - } - } - - public boolean equals(Object o) { - if (o instanceof Switch) { - Switch ig = (Switch) o; - return operands[0] == ig.operands[0] - && defaultTarget.equals(ig.defaultTarget) - && branches.equals(ig.branches) && types[0].equals(ig.types[0]); - } - return false; - } - - public String toString() { - String table = ""; - boolean firstTime = true; - for (Pair p : branches) { - if (!firstTime) { - table += ", "; - } - firstTime = false; - table += p.first() + "->" + p.second(); - } - table += ", *->" + defaultTarget; - return "switch %" + operands[0] + " " + table; - } - - @Override - public Bytecode clone(int[] nTargets, int[] nOperands) { - return new Switch(types[0], nOperands[0], defaultTarget, branches); - } - - } - - // ============================================================= - // Helpers - // ============================================================= - private static int[] append(int[] operands, int operand) { - int[] noperands = Arrays.copyOf(operands, operands.length+1); - noperands[operands.length] = operand; - return noperands; - } - - private static int[] append(int operand, int[] operands) { - int[] noperands = new int[operands.length+1]; - System.arraycopy(operands,0,noperands,1,operands.length); - noperands[0] = operand; - return noperands; - } -} diff --git a/modules/wyil/src/wyil/transforms/LoopVariants.java b/modules/wyil/src/wyil/transforms/LoopVariants.java index 05ab4399a7..93fb16f0f5 100644 --- a/modules/wyil/src/wyil/transforms/LoopVariants.java +++ b/modules/wyil/src/wyil/transforms/LoopVariants.java @@ -9,7 +9,6 @@ import wycc.lang.Transform; import wyil.lang.CodeForest; import wyil.lang.Bytecode; -import wyil.lang.Codes; import wyil.lang.Type; import wyil.lang.WyilFile; @@ -120,17 +119,17 @@ protected BitSet infer(int blockID, CodeForest forest) { if (code instanceof Bytecode.Compound) { Bytecode.Compound body = (Bytecode.Compound) code; BitSet loopModified = infer(body.block(), forest); - if (code instanceof Codes.Quantify) { + if (code instanceof Bytecode.Quantify) { // Unset the modified status of the index operand, it is // already implied that this is modified. - Codes.Quantify qc = (Codes.Quantify) code; + Bytecode.Quantify qc = (Bytecode.Quantify) code; loopModified.clear(qc.indexOperand()); - code = Codes.Quantify(qc.startOperand(), qc.endOperand(), qc.indexOperand(), toArray(loopModified), + code = Bytecode.Quantify(qc.startOperand(), qc.endOperand(), qc.indexOperand(), toArray(loopModified), qc.block()); block.set(i, code, e.attributes()); - } else if (code instanceof Codes.Loop) { - Codes.Loop loop = (Codes.Loop) code; - code = Codes.Loop(toArray(loopModified), loop.block()); + } else if (code instanceof Bytecode.Loop) { + Bytecode.Loop loop = (Bytecode.Loop) code; + code = Bytecode.Loop(toArray(loopModified), loop.block()); block.set(i, code, e.attributes()); } diff --git a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java index 8322e5666f..5d6c99b6d8 100755 --- a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java +++ b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java @@ -127,23 +127,23 @@ protected T propagate(int blockID, T store, List> handlers) { try { // First, check for a label which may have incoming information. - if (code instanceof Codes.Loop) { - Codes.Loop loop = (Codes.Loop) code; + if (code instanceof Bytecode.Loop) { + Bytecode.Loop loop = (Bytecode.Loop) code; store = propagate(id, loop, store, handlers); continue; - } else if (code instanceof Codes.Label) { - Codes.Label l = (Codes.Label) code; + } else if (code instanceof Bytecode.Label) { + Bytecode.Label l = (Bytecode.Label) code; stores.put(l.label,store); - } else if (code instanceof Codes.If) { - Codes.If ifgoto = (Codes.If) code; + } else if (code instanceof Bytecode.If) { + Bytecode.If ifgoto = (Bytecode.If) code; T trueStore = stores.get(ifgoto.destination()); store = propagate(id, ifgoto, trueStore, store); - } else if (code instanceof Codes.IfIs) { - Codes.IfIs iftype = (Codes.IfIs) code; + } else if (code instanceof Bytecode.IfIs) { + Bytecode.IfIs iftype = (Bytecode.IfIs) code; T trueStore = stores.get(iftype.destination()); store = propagate(id, iftype, trueStore, store); - } else if (code instanceof Codes.Switch) { - Codes.Switch sw = (Codes.Switch) code; + } else if (code instanceof Bytecode.Switch) { + Bytecode.Switch sw = (Bytecode.Switch) code; ArrayList swStores = new ArrayList(); for(int j=0;j!=sw.branches.size();++j){ @@ -153,13 +153,13 @@ protected T propagate(int blockID, T store, List> handlers) { T defStore = stores.get(sw.defaultTarget); store = propagate(id, sw, swStores, defStore); - } else if (code instanceof Codes.Goto) { - Codes.Goto gto = (Codes.Goto) code; + } else if (code instanceof Bytecode.Goto) { + Bytecode.Goto gto = (Bytecode.Goto) code; store = stores.get(gto.destination()); } else { // This indicates a sequential statement was encountered. - if (code instanceof Codes.Return - || code instanceof Codes.Fail) { + if (code instanceof Bytecode.Return + || code instanceof Bytecode.Fail) { store = lastStore(); } store = propagate(id, code, store); @@ -194,7 +194,7 @@ protected T propagate(int blockID, T store, List> handlers) { * statement on the false branch. * @return */ - protected abstract T propagate(CodeForest.Index index, Codes.If ifgoto, + protected abstract T propagate(CodeForest.Index index, Bytecode.If ifgoto, T trueStore, T falseStore); /** @@ -216,7 +216,7 @@ protected abstract T propagate(CodeForest.Index index, Codes.If ifgoto, * statement on the false branch. * @return */ - protected abstract T propagate(CodeForest.Index index, Codes.IfIs iftype, T trueStore, + protected abstract T propagate(CodeForest.Index index, Bytecode.IfIs iftype, T trueStore, T falseStore); /** @@ -236,7 +236,7 @@ protected abstract T propagate(CodeForest.Index index, Codes.IfIs iftype, T true * --- abstract store coming from default branch * @return */ - protected abstract T propagate(CodeForest.Index index, Codes.Switch sw, + protected abstract T propagate(CodeForest.Index index, Bytecode.Switch sw, List stores, T defStore); /** @@ -254,7 +254,7 @@ protected abstract T propagate(CodeForest.Index index, Codes.Switch sw, * statement. * @return */ - protected abstract T propagate(CodeForest.Index index, Codes.Loop code, T store, + protected abstract T propagate(CodeForest.Index index, Bytecode.Loop code, T store, List> handlers); /** diff --git a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java index 1ad033912c..b622bdd99d 100755 --- a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java +++ b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java @@ -126,8 +126,8 @@ protected T propagate(int blockID, T store) { try { // First, check for a label which may have incoming information. - if (code instanceof Codes.Label) { - Codes.Label l = (Codes.Label) code; + if (code instanceof Bytecode.Label) { + Bytecode.Label l = (Bytecode.Label) code; T tmp = stores.get(l.label); if (tmp != null && store != null) { store = join(store, tmp); @@ -141,28 +141,28 @@ protected T propagate(int blockID, T store) { if (store == null) { // this indicates dead-code has been reached. continue; - } else if (code instanceof Codes.Loop) { - Codes.Loop loop = (Codes.Loop) code; + } else if (code instanceof Bytecode.Loop) { + Bytecode.Loop loop = (Bytecode.Loop) code; // propagate through the loop body store = propagate(id, loop, store); continue; - } else if (code instanceof Codes.If) { - Codes.If ifgoto = (Codes.If) code; + } else if (code instanceof Bytecode.If) { + Bytecode.If ifgoto = (Bytecode.If) code; Pair r = propagate(id, ifgoto, store); store = r.second(); merge(ifgoto.destination(), r.first(), stores); - } else if (code instanceof Codes.IfIs) { - Codes.IfIs ifgoto = (Codes.IfIs) code; + } else if (code instanceof Bytecode.IfIs) { + Bytecode.IfIs ifgoto = (Bytecode.IfIs) code; Pair r = propagate(id, ifgoto, store); store = r.second(); merge(ifgoto.destination(), r.first(), stores); - } else if (code instanceof Codes.Switch) { - Codes.Switch sw = (Codes.Switch) code; + } else if (code instanceof Bytecode.Switch) { + Bytecode.Switch sw = (Bytecode.Switch) code; List r = propagate(id, sw, store); // assert r.second().size() == nsw.branches.size() - Codes.Switch nsw = (Codes.Switch) code; + Bytecode.Switch nsw = (Bytecode.Switch) code; for (int j = 0; j != nsw.branches.size(); ++j) { String target = nsw.branches.get(j).second(); T nstore = r.get(j); @@ -170,15 +170,15 @@ protected T propagate(int blockID, T store) { } merge(sw.defaultTarget, store, stores); store = null; - } else if (code instanceof Codes.Goto) { - Codes.Goto gto = (Codes.Goto) code; + } else if (code instanceof Bytecode.Goto) { + Bytecode.Goto gto = (Bytecode.Goto) code; merge(gto.destination(), store, stores); store = null; } else { // This indicates a sequential statement was encountered. store = propagate(id, code, store); - if (code instanceof Codes.Return - || code instanceof Codes.Fail) { + if (code instanceof Bytecode.Return + || code instanceof Bytecode.Fail) { store = null; } } @@ -220,7 +220,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract Pair propagate(CodeForest.Index index, Codes.If ifgoto, T store); + protected abstract Pair propagate(CodeForest.Index index, Bytecode.If ifgoto, T store); /** *

@@ -240,7 +240,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract Pair propagate(CodeForest.Index index, Codes.IfIs iftype, T store); + protected abstract Pair propagate(CodeForest.Index index, Bytecode.IfIs iftype, T store); /** *

@@ -257,7 +257,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract List propagate(CodeForest.Index index, Codes.Switch sw, T store); + protected abstract List propagate(CodeForest.Index index, Bytecode.Switch sw, T store); /** *

@@ -280,7 +280,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract T propagate(CodeForest.Index index, Codes.Loop code, T store); + protected abstract T propagate(CodeForest.Index index, Bytecode.Loop code, T store); /** *

diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index a661f3d47a..d2fbd013b6 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -155,47 +155,47 @@ private Object executeAllWithin(Constant[] frame, Context context) { private Object execute(Constant[] frame, Context context) { Bytecode bytecode = context.forest.get(context.pc).code(); // FIXME: turn this into a switch statement? - if (bytecode instanceof Codes.Invariant) { - return execute((Codes.Invariant) bytecode, frame, context); - } else if (bytecode instanceof Codes.AssertOrAssume) { - return execute((Codes.AssertOrAssume) bytecode, frame, context); - } else if (bytecode instanceof Codes.Operator) { - return execute((Codes.Operator) bytecode, frame, context); - } else if (bytecode instanceof Codes.Const) { - return execute((Codes.Const) bytecode, frame, context); - } else if (bytecode instanceof Codes.Convert) { - return execute((Codes.Convert) bytecode, frame, context); - } else if (bytecode instanceof Codes.Debug) { - return execute((Codes.Debug) bytecode, frame, context); - } else if (bytecode instanceof Codes.Fail) { - return execute((Codes.Fail) bytecode, frame, context); - } else if (bytecode instanceof Codes.FieldLoad) { - return execute((Codes.FieldLoad) bytecode, frame, context); - } else if (bytecode instanceof Codes.Goto) { - return execute((Codes.Goto) bytecode, frame, context); - } else if (bytecode instanceof Codes.If) { - return execute((Codes.If) bytecode, frame, context); - } else if (bytecode instanceof Codes.IfIs) { - return execute((Codes.IfIs) bytecode, frame, context); - } else if (bytecode instanceof Codes.IndirectInvoke) { - return execute((Codes.IndirectInvoke) bytecode, frame, context); - } else if (bytecode instanceof Codes.Invoke) { - return execute((Codes.Invoke) bytecode, frame, context); - } else if (bytecode instanceof Codes.Label) { + if (bytecode instanceof Bytecode.Invariant) { + return execute((Bytecode.Invariant) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.AssertOrAssume) { + return execute((Bytecode.AssertOrAssume) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Operator) { + return execute((Bytecode.Operator) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Const) { + return execute((Bytecode.Const) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Convert) { + return execute((Bytecode.Convert) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Debug) { + return execute((Bytecode.Debug) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Fail) { + return execute((Bytecode.Fail) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.FieldLoad) { + return execute((Bytecode.FieldLoad) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Goto) { + return execute((Bytecode.Goto) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.If) { + return execute((Bytecode.If) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.IfIs) { + return execute((Bytecode.IfIs) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.IndirectInvoke) { + return execute((Bytecode.IndirectInvoke) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Invoke) { + return execute((Bytecode.Invoke) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Label) { // essentially do nout return context.pc.next(); - } else if (bytecode instanceof Codes.Lambda) { - return execute((Codes.Lambda) bytecode, frame, context); - } else if (bytecode instanceof Codes.Quantify) { - return execute((Codes.Quantify) bytecode, frame, context); - } else if (bytecode instanceof Codes.Loop) { - return execute((Codes.Loop) bytecode, frame, context); - } else if (bytecode instanceof Codes.Return) { - return execute((Codes.Return) bytecode, frame, context); - } else if (bytecode instanceof Codes.Switch) { - return execute((Codes.Switch) bytecode, frame, context); - } else if (bytecode instanceof Codes.Update) { - return execute((Codes.Update) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Lambda) { + return execute((Bytecode.Lambda) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Quantify) { + return execute((Bytecode.Quantify) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Loop) { + return execute((Bytecode.Loop) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Return) { + return execute((Bytecode.Return) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Switch) { + return execute((Bytecode.Switch) bytecode, frame, context); + } else if (bytecode instanceof Bytecode.Update) { + return execute((Bytecode.Update) bytecode, frame, context); } else { throw new IllegalArgumentException("Unknown bytecode encountered: " + bytecode); } @@ -212,7 +212,7 @@ private Object execute(Constant[] frame, Context context) { * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.AssertOrAssume bytecode, Constant[] frame, Context context) { // CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); Object r = executeAllWithin(frame, new Context(pc, context.forest)); @@ -237,7 +237,7 @@ private Object execute(Codes.AssertOrAssume bytecode, Constant[] frame, Context * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Operator bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Operator bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] values = new Constant[operands.length]; // Read all operands @@ -264,13 +264,13 @@ private Object execute(Codes.Operator bytecode, Constant[] frame, Context contex * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Const bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Const bytecode, Constant[] frame, Context context) { Constant c = cleanse(bytecode.constant, context); frame[bytecode.target()] = c; return context.pc.next(); } - private Object execute(Codes.Convert bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Convert bytecode, Constant[] frame, Context context) { try { Constant operand = frame[bytecode.operand(0)]; Type target = expander.getUnderlyingType(bytecode.result()); @@ -411,7 +411,7 @@ private Constant convert(Constant value, Type.FunctionOrMethod to, Context conte * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Debug bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Debug bytecode, Constant[] frame, Context context) { // Constant.Array list = (Constant.Array) frame[bytecode.operand(0)]; for (Constant item : list.values) { @@ -435,17 +435,17 @@ private Object execute(Codes.Debug bytecode, Constant[] frame, Context context) * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Fail bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Fail bytecode, Constant[] frame, Context context) { throw new Error("Runtime fault occurred"); } - private Object execute(Codes.FieldLoad bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.FieldLoad bytecode, Constant[] frame, Context context) { Constant.Record rec = (Constant.Record) frame[bytecode.operand(0)]; frame[bytecode.target(0)] = rec.values.get(bytecode.field); return context.pc.next(); } - private Object execute(Codes.Quantify bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Quantify bytecode, Constant[] frame, Context context) { Constant startOperand = frame[bytecode.startOperand()]; Constant endOperand = frame[bytecode.endOperand()]; checkType(startOperand, context, Constant.Integer.class); @@ -470,11 +470,11 @@ private Object execute(Codes.Quantify bytecode, Constant[] frame, Context contex return context.pc.next(); } - private Object execute(Codes.Goto bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Goto bytecode, Constant[] frame, Context context) { return context.getLabel(bytecode.destination()); } - private Object execute(Codes.If bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.If bytecode, Constant[] frame, Context context) { Constant op1 = frame[bytecode.operand(0)]; Constant op2 = frame[bytecode.operand(1)]; boolean result; @@ -533,7 +533,7 @@ private boolean lessThan(Constant lhs, Constant rhs, boolean isStrict, Context c } } - private Object execute(Codes.IfIs bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.IfIs bytecode, Constant[] frame, Context context) { Type typeOperand = bytecode.type(1); Constant op = frame[bytecode.operand(0)]; if (isMemberOfType(op, typeOperand, context)) { @@ -675,7 +675,7 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.IndirectInvoke bytecode, Constant[] frame, Context context) { Constant operand = frame[bytecode.operand(0)]; // Check that we have a function reference checkType(operand, context, Constant.Lambda.class); @@ -708,7 +708,7 @@ private Object execute(Codes.IndirectInvoke bytecode, Constant[] frame, Context return context.pc.next(); } - private Object execute(Codes.Invariant bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Invariant bytecode, Constant[] frame, Context context) { // FIXME: currently implemented as a NOP because of #480 return context.pc.next(); } @@ -727,7 +727,7 @@ private Object execute(Codes.Invariant bytecode, Constant[] frame, Context conte * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Invoke bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Invoke bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] arguments = new Constant[operands.length]; for (int i = 0; i != arguments.length; ++i) { @@ -741,7 +741,7 @@ private Object execute(Codes.Invoke bytecode, Constant[] frame, Context context) return context.pc.next(); } - private Object execute(Codes.Lambda bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Lambda bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] arguments = new Constant[operands.length]; @@ -756,7 +756,7 @@ private Object execute(Codes.Lambda bytecode, Constant[] frame, Context context) return context.pc.next(); } - private Object execute(Codes.Loop bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Loop bytecode, Constant[] frame, Context context) { Object r; do { // Keep executing the loop body until we exit it somehow. @@ -781,7 +781,7 @@ private Object execute(Codes.Loop bytecode, Constant[] frame, Context context) { * --- Context in which bytecodes are executed * @return */ - private Object execute(Codes.Return bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Return bytecode, Constant[] frame, Context context) { int[] operands = bytecode.operands(); Constant[] returns = new Constant[operands.length]; for (int i = 0; i != operands.length; ++i) { @@ -790,7 +790,7 @@ private Object execute(Codes.Return bytecode, Constant[] frame, Context context) return returns; } - private Object execute(Codes.Switch bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Switch bytecode, Constant[] frame, Context context) { // Constant operand = frame[bytecode.operand(0)]; for (Pair branch : bytecode.branches) { @@ -802,7 +802,7 @@ private Object execute(Codes.Switch bytecode, Constant[] frame, Context context) return context.getLabel(bytecode.defaultTarget); } - private Object execute(Codes.Update bytecode, Constant[] frame, Context context) { + private Object execute(Bytecode.Update bytecode, Constant[] frame, Context context) { Constant rhs = frame[bytecode.result()]; Constant lhs = frame[bytecode.target(0)]; frame[bytecode.target(0)] = update(lhs, bytecode.iterator(), rhs, frame, context); @@ -830,14 +830,14 @@ private Object execute(Codes.Update bytecode, Constant[] frame, Context context) * * @return The left-hand side updated with the new value assigned */ - private Constant update(Constant lhs, Iterator descriptor, Constant rhs, Constant[] frame, + private Constant update(Constant lhs, Iterator descriptor, Constant rhs, Constant[] frame, Context context) { if (descriptor.hasNext()) { - Codes.LVal lval = descriptor.next(); + Bytecode.LVal lval = descriptor.next(); // Check what shape the left-hand side is - if (lval instanceof Codes.ArrayLVal) { + if (lval instanceof Bytecode.ArrayLVal) { // List - Codes.ArrayLVal lv = (Codes.ArrayLVal) lval; + Bytecode.ArrayLVal lv = (Bytecode.ArrayLVal) lval; Constant operand = frame[lv.indexOperand]; checkType(operand, context, Constant.Integer.class); checkType(lhs, context, Constant.Array.class); @@ -847,9 +847,9 @@ private Constant update(Constant lhs, Iterator descriptor, Constant rhs = update(values.get(index), descriptor, rhs, frame, context); values.set(index, rhs); return Constant.V_ARRAY(values); - } else if (lval instanceof Codes.RecordLVal) { + } else if (lval instanceof Bytecode.RecordLVal) { // Record - Codes.RecordLVal lv = (Codes.RecordLVal) lval; + Bytecode.RecordLVal lv = (Bytecode.RecordLVal) lval; checkType(lhs, context, Constant.Record.class); Constant.Record record = (Constant.Record) lhs; HashMap values = new HashMap(record.values); @@ -858,7 +858,7 @@ private Constant update(Constant lhs, Iterator descriptor, Constant return Constant.V_RECORD(values); } else { // Reference - Codes.ReferenceLVal lv = (Codes.ReferenceLVal) lval; + Bytecode.ReferenceLVal lv = (Bytecode.ReferenceLVal) lval; checkType(lhs, context, ConstantObject.class); ConstantObject object = (ConstantObject) lhs; rhs = update(object.read(), descriptor, rhs, frame, context); diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index f41ea7f21b..c3966c93d6 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -1,7 +1,6 @@ package wyil.util.interpreter; import wyil.lang.Bytecode; -import wyil.lang.Codes; import wyil.lang.Constant; import wyil.lang.Type; import wyil.util.interpreter.Interpreter.ConstantObject; @@ -244,7 +243,7 @@ public Constant apply(Constant[] operands, Context context) { private static final class RecordConstructor implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { - Codes.Operator bytecode = (Codes.Operator) context.getBytecode(); + Bytecode.Operator bytecode = (Bytecode.Operator) context.getBytecode(); Type.EffectiveRecord rType = (Type.EffectiveRecord) bytecode.type(0); HashMap values = new HashMap(); ArrayList fields = new ArrayList(rType.fields().keySet()); diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index f0ec1ef57b..8b0f150afb 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -42,6 +42,7 @@ import wyil.attributes.SourceLocation; import wyil.lang.*; import wyil.lang.Constant; +import static wyil.lang.Bytecode.*; import wyil.util.TypeExpander; import static wyil.util.ErrorMessages.internalFailure; @@ -52,9 +53,7 @@ import jasm.attributes.SourceFile; import jasm.lang.*; import jasm.lang.Bytecode; -import jasm.lang.Bytecode.Goto; import jasm.lang.Modifier; -import jasm.lang.Bytecode.Load; import jasm.util.Triple; import jasm.verifier.ClassFileVerifier; import wyrl.io.JavaIdentifierOutputStream; @@ -406,12 +405,12 @@ private void patchInvariantBlockHelper(String falseBranch, CodeForest.Block bloc CodeForest.Entry e = block.get(i); wyil.lang.Bytecode c = e.code(); - if (c instanceof Codes.Return) { + if (c instanceof Return) { // first patch point - block.set(i, Codes.Operator(Type.T_VOID, new int[0], new int[0], Codes.OperatorKind.ASSIGN)); - } else if (c instanceof Codes.Fail) { + block.set(i, Operator(Type.T_VOID, new int[0], new int[0], OperatorKind.ASSIGN)); + } else if (c instanceof Fail) { // second patch point - block.set(i, Codes.Goto(falseBranch)); + block.set(i, Goto(falseBranch)); } } } @@ -590,46 +589,46 @@ private int translate(CodeForest.Index pc, wyil.lang.Bytecode code, int freeSlot ArrayList bytecodes) { try { - if (code instanceof Codes.Operator) { - translate(pc, (Codes.Operator) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Convert) { - translate(pc, (Codes.Convert) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Const) { - translate(pc, (Codes.Const) code, freeSlot, forest,bytecodes); - } else if (code instanceof Codes.Debug) { - translate(pc, (Codes.Debug) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.AssertOrAssume) { - translate(pc, (Codes.AssertOrAssume) code, freeSlot, forest, + if (code instanceof Operator) { + translate(pc, (Operator) code, freeSlot, forest, bytecodes); + } else if (code instanceof Convert) { + translate(pc, (Convert) code, freeSlot, forest, bytecodes); + } else if (code instanceof Const) { + translate(pc, (Const) code, freeSlot, forest,bytecodes); + } else if (code instanceof Debug) { + translate(pc, (Debug) code, freeSlot, forest, bytecodes); + } else if (code instanceof AssertOrAssume) { + translate(pc, (AssertOrAssume) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Fail) { - translate(pc, (Codes.Fail) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.FieldLoad) { - translate(pc, (Codes.FieldLoad) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Quantify) { - freeSlot = translate(pc, (Codes.Quantify) code, freeSlot, + } else if (code instanceof Fail) { + translate(pc, (Fail) code, freeSlot, forest, bytecodes); + } else if (code instanceof FieldLoad) { + translate(pc, (FieldLoad) code, freeSlot, forest, bytecodes); + } else if (code instanceof Quantify) { + freeSlot = translate(pc, (Quantify) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Goto) { - translate(pc, (Codes.Goto) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.If) { - translateIfGoto(pc, (Codes.If) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.IfIs) { - translate(pc, (Codes.IfIs) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.IndirectInvoke) { - translate(pc, (Codes.IndirectInvoke) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Invoke) { - translate(pc, (Codes.Invoke) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Label) { - translate(pc, (Codes.Label) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Lambda) { - translate(pc, (Codes.Lambda) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Loop) { - translate(pc, (Codes.Loop) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Update) { - translate(pc, (Codes.Update) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Return) { - translate(pc, (Codes.Return) code, freeSlot, forest, bytecodes); - } else if (code instanceof Codes.Switch) { - translate(pc, (Codes.Switch) code, freeSlot, forest, bytecodes); + } else if (code instanceof Goto) { + translate(pc, (Goto) code, freeSlot, forest, bytecodes); + } else if (code instanceof If) { + translateIfGoto(pc, (If) code, freeSlot, forest, bytecodes); + } else if (code instanceof IfIs) { + translate(pc, (IfIs) code, freeSlot, forest, bytecodes); + } else if (code instanceof IndirectInvoke) { + translate(pc, (IndirectInvoke) code, freeSlot, forest, bytecodes); + } else if (code instanceof Invoke) { + translate(pc, (Invoke) code, freeSlot, forest, bytecodes); + } else if (code instanceof Label) { + translate(pc, (Label) code, freeSlot, forest, bytecodes); + } else if (code instanceof Lambda) { + translate(pc, (Lambda) code, freeSlot, forest, bytecodes); + } else if (code instanceof Loop) { + translate(pc, (Loop) code, freeSlot, forest, bytecodes); + } else if (code instanceof Update) { + translate(pc, (Update) code, freeSlot, forest, bytecodes); + } else if (code instanceof Return) { + translate(pc, (Return) code, freeSlot, forest, bytecodes); + } else if (code instanceof Switch) { + translate(pc, (Switch) code, freeSlot, forest, bytecodes); } else { internalFailure("unknown wyil code encountered (" + code + ")", filename, @@ -645,17 +644,17 @@ private int translate(CodeForest.Index pc, wyil.lang.Bytecode code, int freeSlot } - private void translate(CodeForest.Index index, Codes.AssertOrAssume c, + private void translate(CodeForest.Index index, AssertOrAssume c, int freeSlot, CodeForest forest, ArrayList bytecodes) { CodeForest.Index pc = new CodeForest.Index(c.block(), 0); - if(c instanceof Codes.Invariant) { + if(c instanceof Invariant) { // essentially a no-op for now } else { translate(pc, freeSlot, forest, bytecodes); } } - private void translate(CodeForest.Index index, Codes.Const c, int freeSlot, + private void translate(CodeForest.Index index, Const c, int freeSlot, CodeForest forest, ArrayList bytecodes) { Constant constant = c.constant; JvmType jt = convertUnderlyingType(constant.type()); @@ -673,14 +672,14 @@ private void translate(CodeForest.Index index, Codes.Const c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(), jt)); } - private void translate(CodeForest.Index index, Codes.Convert c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Convert c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); addCoercion(c.type(0), c.result(), freeSlot, constants, bytecodes); bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.result()))); } - private void translate(CodeForest.Index index, Codes.Update code, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Update code, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(code.target(0), convertUnderlyingType(code.type(0)))); translateUpdate(code.iterator(), code, bytecodes); @@ -711,17 +710,17 @@ private void translate(CodeForest.Index index, Codes.Update code, int freeSlot, * @param bytecodes * --- List of bytecodes to append to. */ - private void translateUpdate(Iterator iterator, - Codes.Update code, ArrayList bytecodes) { + private void translateUpdate(Iterator iterator, + Update code, ArrayList bytecodes) { // At this point, we have not yet reached the "innermost" position. // Therefore, we keep recursing down the chain of LVals. - Codes.LVal lv = iterator.next(); - if (lv instanceof Codes.ArrayLVal) { - translateUpdate((Codes.ArrayLVal) lv,iterator,code,bytecodes); - } else if (lv instanceof Codes.RecordLVal) { - translateUpdate((Codes.RecordLVal) lv,iterator,code,bytecodes); + LVal lv = iterator.next(); + if (lv instanceof ArrayLVal) { + translateUpdate((ArrayLVal) lv,iterator,code,bytecodes); + } else if (lv instanceof RecordLVal) { + translateUpdate((RecordLVal) lv,iterator,code,bytecodes); } else { - translateUpdate((Codes.ReferenceLVal) lv,iterator,code,bytecodes); + translateUpdate((ReferenceLVal) lv,iterator,code,bytecodes); } } @@ -747,7 +746,7 @@ private void translateUpdate(Iterator iterator, * @param code * @param bytecodes */ - private void translateUpdate(Codes.ArrayLVal lval, Iterator iterator, Codes.Update code, + private void translateUpdate(ArrayLVal lval, Iterator iterator, Update code, ArrayList bytecodes) { if(iterator.hasNext()) { @@ -774,7 +773,7 @@ private void translateUpdate(Codes.ArrayLVal lval, Iterator iterator bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "set", setFunType, Bytecode.InvokeMode.STATIC)); } - private void translateUpdate(Codes.RecordLVal lval, Iterator iterator, Codes.Update code, + private void translateUpdate(RecordLVal lval, Iterator iterator, Update code, ArrayList bytecodes) { Type.EffectiveRecord type = lval.rawType(); @@ -802,7 +801,7 @@ private void translateUpdate(Codes.RecordLVal lval, Iterator iterato bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "put", putFunType, Bytecode.InvokeMode.STATIC)); } - private void translateUpdate(Codes.ReferenceLVal lval, Iterator iterator, Codes.Update code, + private void translateUpdate(ReferenceLVal lval, Iterator iterator, Update code, ArrayList bytecodes) { if(iterator.hasNext()) { // This is not the innermost case, hence we read out the current @@ -824,7 +823,7 @@ private void translateUpdate(Codes.ReferenceLVal lval, Iterator iter bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "setState", setFunType, Bytecode.InvokeMode.VIRTUAL)); } - private void translate(CodeForest.Index index, Codes.Return c, int freeSlot, + private void translate(CodeForest.Index index, Return c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType jt = null; int[] operands = c.operands(); @@ -839,7 +838,7 @@ private void translate(CodeForest.Index index, Codes.Return c, int freeSlot, bytecodes.add(new Bytecode.Return(jt)); } - private void translate(CodeForest.Index index, Codes.Switch c, int freeSlot, + private void translate(CodeForest.Index index, Switch c, int freeSlot, CodeForest forest, ArrayList bytecodes) { ArrayList> cases = new ArrayList(); @@ -876,13 +875,13 @@ private void translate(CodeForest.Index index, Codes.Switch c, int freeSlot, String target = p.second(); translate(value, freeSlot, bytecodes); bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); - translateIfGoto(index, value.type(), Codes.Comparator.EQ, target, freeSlot + 1, forest, bytecodes); + translateIfGoto(index, value.type(), wyil.lang.Bytecode.Comparator.EQ, target, freeSlot + 1, forest, bytecodes); } bytecodes.add(new Bytecode.Goto(c.defaultTarget)); } } - private void translateIfGoto(CodeForest.Index index, Codes.If code, int freeSlot, CodeForest forest, + private void translateIfGoto(CodeForest.Index index, If code, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(code.type(0)); bytecodes.add(new Bytecode.Load(code.operand(0), jt)); @@ -890,7 +889,7 @@ private void translateIfGoto(CodeForest.Index index, Codes.If code, int freeSlot translateIfGoto(index, code.type(0), code.op, code.destination(), freeSlot, forest, bytecodes); } - private void translateIfGoto(CodeForest.Index index, Type c_type, Codes.Comparator cop, String target, int freeSlot, + private void translateIfGoto(CodeForest.Index index, Type c_type, wyil.lang.Bytecode.Comparator cop, String target, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType type = convertUnderlyingType(c_type); @@ -974,7 +973,7 @@ private void translateIfGoto(CodeForest.Index index, Type c_type, Codes.Comparat bytecodes.add(new Bytecode.If(op, target)); } - private void translate(CodeForest.Index index, Codes.IfIs c, int freeSlot, + private void translate(CodeForest.Index index, IfIs c, int freeSlot, CodeForest forest, ArrayList bytecodes) { // In this case, we're updating the type of a local variable. To @@ -1168,7 +1167,7 @@ private void translateInvariantTest(String falseTarget, Type type, int rootSlot, } } - private void translate(CodeForest.Index index, Codes.Loop c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Loop c, int freeSlot, CodeForest forest, ArrayList bytecodes) { // Allocate header label for loop String loopHeader = freshLabel(); @@ -1180,7 +1179,7 @@ private void translate(CodeForest.Index index, Codes.Loop c, int freeSlot, CodeF bytecodes.add(new Bytecode.Goto(loopHeader)); } - private int translate(CodeForest.Index index, Codes.Quantify c, int freeSlot, + private int translate(CodeForest.Index index, Quantify c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.startOperand(), WHILEYINT)); bytecodes.add(new Bytecode.Load(c.endOperand(), WHILEYINT)); @@ -1211,24 +1210,24 @@ private int translate(CodeForest.Index index, Codes.Quantify c, int freeSlot, return freeSlot; } - private void translate(CodeForest.Index index, Codes.Goto c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Goto c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Goto(c.destination())); } - private void translate(CodeForest.Index index, Codes.Label c, int freeSlot, + private void translate(CodeForest.Index index, Label c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Label(c.label)); } - private void translate(CodeForest.Index index, Codes.Debug c, int freeSlot, + private void translate(CodeForest.Index index, Debug c, int freeSlot, CodeForest forest, ArrayList bytecodes) { JvmType.Function ftype = new JvmType.Function(T_VOID, WHILEYARRAY); bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "print", ftype, Bytecode.InvokeMode.STATIC)); } - private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + private void translate(CodeForest.Index index, Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.LoadConst("runtime fault encountered")); @@ -1237,7 +1236,7 @@ private void translate(CodeForest.Index index, Codes.Fail c, int freeSlot, CodeF bytecodes.add(new Bytecode.Throw()); } - private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + private void translate(CodeForest.Index index, FieldLoad c, int freeSlot, CodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYRECORD)); bytecodes.add(new Bytecode.LoadConst(c.field)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYRECORD, JAVA_LANG_STRING); @@ -1246,13 +1245,13 @@ private void translate(CodeForest.Index index, Codes.FieldLoad c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.fieldType()))); } - private void translate(CodeForest.Index index, Codes.Operator c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Operator c, int freeSlot, CodeForest forest, ArrayList bytecodes) { Context context = new Context(forest, index, freeSlot, bytecodes); generators[c.opcode()].translate(c, context); } - private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Lambda c, int freeSlot, CodeForest forest, ArrayList bytecodes) { // First, build and register lambda class which calls the given function @@ -1311,7 +1310,7 @@ private void translate(CodeForest.Index index, Codes.Lambda c, int freeSlot, Cod bytecodes.add(new Bytecode.Store(c.target(0), clazz)); } - private void translate(CodeForest.Index index, Codes.Invoke c, int freeSlot, CodeForest forest, + private void translate(CodeForest.Index index, Invoke c, int freeSlot, CodeForest forest, ArrayList bytecodes) { for (int i = 0; i != c.operands().length; ++i) { @@ -1339,7 +1338,7 @@ private void translate(CodeForest.Index index, Codes.Invoke c, int freeSlot, Cod } } - private void translate(CodeForest.Index index, Codes.IndirectInvoke c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + private void translate(CodeForest.Index index, IndirectInvoke c, int freeSlot, CodeForest forest, ArrayList bytecodes) { Type.FunctionOrMethod ft = c.type(0); JvmType.Clazz owner = (JvmType.Clazz) convertUnderlyingType(ft); bytecodes.add(new Bytecode.Load(c.reference(), convertUnderlyingType(ft))); @@ -2310,6 +2309,6 @@ public JvmType toJvmType(Type type) { * */ public interface BytecodeTranslator { - void translate(Codes.Operator bytecode, Context context); + void translate(Operator bytecode, Context context); } } diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java index f0747c21ef..4812e30568 100644 --- a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -6,11 +6,9 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import jasm.lang.Bytecode; import jasm.lang.JvmType; -import wyil.lang.Codes; import wyil.lang.Type; import wyjc.Wyil2JavaBuilder.BytecodeTranslator; import wyjc.Wyil2JavaBuilder.Context; @@ -57,7 +55,7 @@ public class BytecodeTranslators { private static final class Assign implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType jt = context.toJvmType(bytecode.type(0)); int[] targets = bytecode.targets(); int[] operands = bytecode.operands(); @@ -74,7 +72,7 @@ public void translate(Codes.Operator bytecode, Context context) { private static final class Dereference implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -88,7 +86,7 @@ public void translate(Codes.Operator bytecode, Context context) { private static final class New implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { Type.Reference refType = (Type.Reference) bytecode.type(0); JvmType type = context.toJvmType(refType); JvmType elementType = context.toJvmType(refType.element()); @@ -107,7 +105,7 @@ public void translate(Codes.Operator bytecode, Context context) { private static final class Negate implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -117,7 +115,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class Add implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -128,7 +126,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class Subtract implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -139,7 +137,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class Multiply implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -150,7 +148,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class Divide implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -161,7 +159,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class Remainder implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -176,7 +174,7 @@ public void translate(Codes.Operator bytecode, Context context) { // ==================================================================================== private static final class Invert implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -186,7 +184,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class BitwiseOr implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -197,7 +195,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class BitwiseXor implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -208,7 +206,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class BitwiseAnd implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, type); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -219,7 +217,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class LeftShift implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, WHILEYINT); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -230,7 +228,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class RightShift implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(type, WHILEYINT); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -244,7 +242,7 @@ public void translate(Codes.Operator bytecode, Context context) { // ==================================================================================== private static final class ArrayLength implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); JvmType.Function ftype = new JvmType.Function(WHILEYINT); context.add(new Bytecode.Load(bytecode.operand(0), type)); @@ -254,7 +252,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class ArrayIndex implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { Type.EffectiveArray arrType = (Type.EffectiveArray) bytecode.type(0); JvmType elementType = context.toJvmType(arrType.element()); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYARRAY, WHILEYINT); @@ -267,7 +265,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class ArrayGenerator implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { Type elementType = ((Type.Array) bytecode.type(0)).element(); JvmType.Function ftype = new JvmType.Function(WHILEYARRAY, JAVA_LANG_OBJECT, WHILEYINT); context.add(new Bytecode.Load(bytecode.operand(0), context.toJvmType(elementType))); @@ -279,7 +277,7 @@ public void translate(Codes.Operator bytecode, Context context) { } private static final class ArrayConstructor implements BytecodeTranslator { @Override - public void translate(Codes.Operator bytecode, Context context) { + public void translate(Operator bytecode, Context context) { Type.Array arrType = (Type.Array) bytecode.type(0); JvmType elementType = context.toJvmType(arrType.element()); JvmType.Function initJvmType = new JvmType.Function(T_VOID, T_INT); @@ -302,7 +300,7 @@ public void translate(Codes.Operator bytecode, Context context) { private static final class RecordConstructor implements BytecodeTranslator { @Override - public void translate(Codes.Operator code, Context context) { + public void translate(Operator code, Context context) { Type.EffectiveRecord recType = (Type.EffectiveRecord) code.type(0); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); From 92af01c82e0f43f0f9baa62f3b231386124fd75a Mon Sep 17 00:00:00 2001 From: DavePearce Date: Wed, 30 Mar 2016 18:58:16 +1300 Subject: [PATCH 22/43] Removed method Interpreter.cleanse() This method served no purpose any more. --- .../wyil/util/interpreter/Interpreter.java | 41 +------------------ 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index d2fbd013b6..fc998ad4b6 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -265,8 +265,7 @@ private Object execute(Bytecode.Operator bytecode, Constant[] frame, Context con * @return */ private Object execute(Bytecode.Const bytecode, Constant[] frame, Context context) { - Constant c = cleanse(bytecode.constant, context); - frame[bytecode.target()] = c; + frame[bytecode.target()] = bytecode.constant; return context.pc.next(); } @@ -794,7 +793,7 @@ private Object execute(Bytecode.Switch bytecode, Constant[] frame, Context conte // Constant operand = frame[bytecode.operand(0)]; for (Pair branch : bytecode.branches) { - Constant caseOperand = cleanse(branch.first(), context); + Constant caseOperand = branch.first(); if (caseOperand.equals(operand)) { return context.getLabel(branch.second()); } @@ -895,42 +894,6 @@ public static T checkType(Constant operand, Context context return null; } - /** - * Cleanse all instances of Constant.Decimal from the given constant and - * replace them with Constant.Rational. - * - * @param constant - * @return - */ - private Constant cleanse(Constant constant, Context context) { - // See #494 for more on why this method exists, and whether or not we - // can get rid of it. - if (constant instanceof Constant.Null || constant instanceof Constant.Byte || constant instanceof Constant.Bool - || constant instanceof Constant.Integer || constant instanceof Constant.Lambda - || constant instanceof Constant.Type) { - return constant; - } else if (constant instanceof Constant.Array) { - Constant.Array ct = (Constant.Array) constant; - ArrayList values = new ArrayList(ct.values); - for (int i = 0; i != values.size(); ++i) { - values.set(i, cleanse(values.get(i), context)); - } - return Constant.V_ARRAY(values); - } else if (constant instanceof Constant.Record) { - Constant.Record mt = (Constant.Record) constant; - HashMap fields = mt.values; - HashMap nFields = new HashMap(); - for (Map.Entry e : fields.entrySet()) { - Constant value = cleanse(e.getValue(), context); - nFields.put(e.getKey(), value); - } - return Constant.V_RECORD(nFields); - } else { - deadCode(context); - return null; - } - } - /** * This method is provided as a generic mechanism for reporting runtime * errors within the interpreter. From 32eed8a576dcdb4f3e2629cbdce62bf37efec9c5 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Wed, 30 Mar 2016 20:30:27 +1300 Subject: [PATCH 23/43] Refactor Bytecode.If #502 This refactors Bytecode.If to accept only one argument rather than two and a comparator. Instead the comparator is implemented as a Bytecode.Operator. Perhaps surprisingly, this does offer some useful simplifications. --- .../wyc/src/wyc/builder/CodeGenerator.java | 59 +++---- .../src/wyil/builders/VcExprGenerator.java | 15 +- .../wyil/src/wyil/builders/VcGenerator.java | 44 ++--- .../wyil/checks/DefiniteAssignmentCheck.java | 2 +- modules/wyil/src/wyil/io/WyilFileReader.java | 57 +++--- modules/wyil/src/wyil/lang/Bytecode.java | 163 ++++++++---------- modules/wyil/src/wyil/lang/CodeUtils.java | 26 --- .../wyil/util/interpreter/Interpreter.java | 53 +----- .../util/interpreter/StandardFunctions.java | 66 ++++++- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 99 ++--------- modules/wyjc/src/wyjc/runtime/Util.java | 24 +++ modules/wyjc/src/wyjc/runtime/WyBool.java | 4 + .../src/wyjc/util/BytecodeTranslators.java | 72 ++++++++ 13 files changed, 322 insertions(+), 362 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index ff1de6a10e..c0b21b78aa 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1341,10 +1341,8 @@ public void generateCondition(String target, Expr condition, Environment environ // true. In some cases, we could actually do better. For // example, !(x < 5) could be rewritten into x >= 5. - int r1 = generate(condition, environment, block, forest, context); - int r2 = environment.allocate(Type.T_BOOL); - block.add(Bytecode.Const(r2, Constant.V_BOOL(true)), attributes(condition)); - block.add(Bytecode.If(Type.T_BOOL, r1, r2, Bytecode.Comparator.EQ, target), attributes(condition)); + int result = generate(condition, environment, block, forest, context); + block.add(Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); } else { syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, condition); @@ -1435,10 +1433,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm generateTypeCondition(target, v, environment, block, forest, context); } else { - - Bytecode.Comparator cop = OP2COP(bop, v, context); - - if (cop == Bytecode.Comparator.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant + if (bop == Expr.BOp.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; @@ -1447,7 +1442,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm } int slot = environment.get(lhs.var); block.add(Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); - } else if (cop == Bytecode.Comparator.NEQ && v.lhs instanceof Expr.LocalVariable + } else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. String exitLabel = CodeUtils.freshLabel(); @@ -1460,9 +1455,8 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm block.add(Bytecode.Goto(target)); block.add(Bytecode.Label(exitLabel)); } else { - int lhs = generate(v.lhs, environment, block, forest, context); - int rhs = generate(v.rhs, environment, block, forest, context); - block.add(Bytecode.If(v.srcType.raw(), lhs, rhs, cop, target), attributes(v)); + int result = generate(v, environment, block, forest, context); + block.add(Bytecode.If(v.srcType.raw(), result, target), attributes(v)); } } } @@ -1949,10 +1943,8 @@ private int generate(Expr.Cast expr, Environment environment, CodeForest.Block b private int generate(Expr.BinOp v, Environment environment, CodeForest.Block block, CodeForest forest, Context context) throws Exception { - // could probably use a range test for this somehow - if (v.op == Expr.BOp.EQ || v.op == Expr.BOp.NEQ || v.op == Expr.BOp.LT || v.op == Expr.BOp.LTEQ - || v.op == Expr.BOp.GT || v.op == Expr.BOp.GTEQ || v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { + if(v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { String trueLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); generateCondition(trueLabel, v, environment, block, forest, context); @@ -1963,7 +1955,6 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo block.add(Bytecode.Const(target, Constant.V_BOOL(true)), attributes(v)); block.add(Bytecode.Label(exitLabel)); return target; - } else { Type result = v.result().raw(); int[] targets = new int[] { environment.allocate(result) }; @@ -1972,8 +1963,7 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo generate(v.rhs, environment, block, forest, context) }; - block.add(Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context)), - attributes(v)); + block.add(Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context)), attributes(v)); return targets[0]; } @@ -2070,6 +2060,18 @@ private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Contex return Bytecode.OperatorKind.DIV; case REM: return Bytecode.OperatorKind.REM; + case EQ: + return Bytecode.OperatorKind.EQ; + case NEQ: + return Bytecode.OperatorKind.NEQ; + case LT: + return Bytecode.OperatorKind.LT; + case LTEQ: + return Bytecode.OperatorKind.LTEQ; + case GT: + return Bytecode.OperatorKind.GT; + case GTEQ: + return Bytecode.OperatorKind.GTEQ; case BITWISEAND: return Bytecode.OperatorKind.BITWISEAND; case BITWISEOR: @@ -2087,27 +2089,6 @@ private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Contex return null; } - private Bytecode.Comparator OP2COP(Expr.BOp bop, SyntacticElement elem, Context context) { - switch (bop) { - case EQ: - return Bytecode.Comparator.EQ; - case NEQ: - return Bytecode.Comparator.NEQ; - case LT: - return Bytecode.Comparator.LT; - case LTEQ: - return Bytecode.Comparator.LTEQ; - case GT: - return Bytecode.Comparator.GT; - case GTEQ: - return Bytecode.Comparator.GTEQ; - default: - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, elem); - } - // dead-code - return null; - } - @SuppressWarnings("incomplete-switch") private static Expr invert(Expr e) { if (e instanceof Expr.BinOp) { diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index 795bc0672b..c944438809 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -105,7 +105,12 @@ public void transform(Bytecode code, CodeForest forest, VcBranch branch) { Expr.Binary.Op.MUL, Expr.Binary.Op.DIV, Expr.Binary.Op.REM, - null, + Expr.Binary.Op.EQ, + Expr.Binary.Op.NEQ, + Expr.Binary.Op.LT, + Expr.Binary.Op.LTEQ, + Expr.Binary.Op.GT, + Expr.Binary.Op.GTEQ, null, // bitwise or null, // bitwise xor null, // bitwise and @@ -135,7 +140,13 @@ protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch bra case SUB: case MUL: case DIV: - case REM:{ + case REM: + case EQ: + case NEQ: + case LT: + case LTEQ: + case GT: + case GTEQ: { transformBinary(binaryOperatorMap[code.kind.ordinal()], code, branch, forest); break; } diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java index 0ef272b4c6..597afaf75b 100644 --- a/modules/wyil/src/wyil/builders/VcGenerator.java +++ b/modules/wyil/src/wyil/builders/VcGenerator.java @@ -1055,7 +1055,7 @@ protected List transform(Bytecode.If code, VcBranch branch, VcBranch trueBranch = branch.fork(); VcBranch falseBranch = branch.fork(); // Second assume the condition on each branch - Expr.Binary trueTest = buildTest(code.op, code.operand(0), code.operand(1), code.type(0), forest, trueBranch); + Expr.Binary trueTest = buildCondition(branch.read(code.operand(0)),forest,branch); trueBranch.assume(trueTest); falseBranch.assume(utils.invert(trueTest)); // Third, dispatch branches to their targets @@ -1564,40 +1564,16 @@ private Expr generateAssumptionsHelper(VcBranch b, VcBranch end) { * @param elem * @return */ - private Expr.Binary buildTest(Bytecode.Comparator cop, int leftOperand, - int rightOperand, Type type, CodeForest forest, - VcBranch branch) { - Expr lhs = branch.read(leftOperand); - Expr rhs = branch.read(rightOperand); - Expr.Binary.Op op; - switch (cop) { - case EQ: - op = Expr.Binary.Op.EQ; - break; - case NEQ: - op = Expr.Binary.Op.NEQ; - break; - case GTEQ: - op = Expr.Binary.Op.GTEQ; - break; - case GT: - op = Expr.Binary.Op.GT; - break; - case LTEQ: - op = Expr.Binary.Op.LTEQ; - break; - case LT: - op = Expr.Binary.Op.LT; - break; - default: - internalFailure("unknown comparator (" + cop + ")", filename, - forest.get(branch.pc()).attributes()); - return null; - } - return new Expr.Binary(op, lhs, rhs, - VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())); + private Expr.Binary buildCondition(Expr test, CodeForest forest, VcBranch branch) { + if (test instanceof Expr.Binary) { + return (Expr.Binary) test; + } else { + Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()); + Expr.Constant tt = new Expr.Constant(Value.Bool(true), attributes); + return new Expr.Binary(Expr.Binary.Op.EQ, test, tt, attributes); + } } - + private Type expand(Type t, Collection attributes) { try { return expander.getUnderlyingType(t); diff --git a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java index 480b1ccc2f..317eeac21a 100755 --- a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java +++ b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java @@ -121,7 +121,7 @@ public HashSet propagate(CodeForest.Index index, Bytecode code, HashSet public Pair, HashSet> propagate(CodeForest.Index index, Bytecode.If igoto, HashSet in) { - if (!in.contains(igoto.operand(0)) || !in.contains(igoto.operand(1))) { + if (!in.contains(igoto.operand(0))) { syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename, forest.get(index).attribute(SourceLocation.class)); } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 491ed91c7a..732e3e8b1a 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -962,63 +962,68 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= // Binary Operators // ========================================================================= - schemas[Bytecode.OPCODE_ifeq] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + schemas[Bytecode.OPCODE_if] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.EQ, (String) extras[0]); + return Bytecode.If(types[0], operands[0], (String) extras[0]); } }; - schemas[Bytecode.OPCODE_ifne] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + + // ========================================================================= + // Binary Assignables + // ========================================================================= + schemas[Bytecode.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.NEQ, (String) extras[0]); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ADD); } }; - schemas[Bytecode.OPCODE_iflt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + schemas[Bytecode.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.LT, (String) extras[0]); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.SUB); } }; - schemas[Bytecode.OPCODE_ifle] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + schemas[Bytecode.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.LTEQ, (String) extras[0]); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.MUL); } }; - schemas[Bytecode.OPCODE_ifgt] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + schemas[Bytecode.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.GT, (String) extras[0]); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DIV); } }; - schemas[Bytecode.OPCODE_ifge] = new Schema(Targets.ZERO, Operands.TWO, Types.ONE, Extras.TARGET){ + schemas[Bytecode.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], operands[1], Bytecode.Comparator.GTEQ, (String) extras[0]); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.REM); } }; - - // ========================================================================= - // Binary Assignables - // ========================================================================= - schemas[Bytecode.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Bytecode.OPCODE_eq] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ADD); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.EQ); } }; - schemas[Bytecode.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Bytecode.OPCODE_ne] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.SUB); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEQ); } }; - schemas[Bytecode.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Bytecode.OPCODE_lt] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.MUL); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LT); } }; - schemas[Bytecode.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Bytecode.OPCODE_le] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DIV); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LTEQ); } }; - schemas[Bytecode.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + schemas[Bytecode.OPCODE_gt] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.REM); + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.GT); + } + }; + schemas[Bytecode.OPCODE_ge] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.GTEQ); } }; schemas[Bytecode.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java index d2378da2dc..28019ffd2f 100755 --- a/modules/wyil/src/wyil/lang/Bytecode.java +++ b/modules/wyil/src/wyil/lang/Bytecode.java @@ -447,8 +447,8 @@ public static Return Return(Type[] types, int... operands) { return new Return(types, operands); } - public static If If(Type type, int leftOperand, int rightOperand, Comparator cop, String label) { - return new If(type, leftOperand, rightOperand, cop, label); + public static If If(Type type, int operand, String label) { + return new If(type, operand, label); } public static IfIs IfIs(Type type, int leftOperand, Type rightOperand, String label) { @@ -558,57 +558,87 @@ public String toString() { return "rem"; } }, - BITWISEOR(9) { + EQ(9) { + public String toString() { + return "eq"; + } + }, + NEQ(10) { + public String toString() { + return "ne"; + } + }, + LT(11) { + public String toString() { + return "lt"; + } + }, + LTEQ(12) { + public String toString() { + return "le"; + } + }, + GT(13) { + public String toString() { + return "gt"; + } + }, + GTEQ(14) { + public String toString() { + return "ge"; + } + }, + BITWISEOR(15) { public String toString() { return "or"; } }, - BITWISEXOR(10) { + BITWISEXOR(16) { public String toString() { return "xor"; } }, - BITWISEAND(11) { + BITWISEAND(17) { public String toString() { return "and"; } }, - LEFTSHIFT(12) { + LEFTSHIFT(18) { public String toString() { return "shl"; } }, - RIGHTSHIFT(13) { + RIGHTSHIFT(19) { public String toString() { return "shr"; } }, - ARRAYINDEX(14) { + ARRAYINDEX(20) { public String toString() { return "indexof"; } }, - ARRAYGENERATOR(15) { + ARRAYGENERATOR(21) { public String toString() { return "arraygen"; } }, - ARRAYCONSTRUCTOR(16) { + ARRAYCONSTRUCTOR(22) { public String toString() { return "array"; } }, - RECORDCONSTRUCTOR(17) { + RECORDCONSTRUCTOR(23) { public String toString() { return "record"; } }, - NEW(18) { + NEW(24) { public String toString() { return "new"; } }, - ASSIGN(19) { + ASSIGN(25) { public String toString() { return "assign"; } @@ -1190,11 +1220,8 @@ public String toString() { * */ public static final class If extends Branching { - public final Comparator op; - - private If(Type type, int leftOperand, int rightOperand, Comparator op, String target) { - super(target, new Type[] { type }, new int[0], leftOperand, rightOperand); - this.op = op; + private If(Type type, int operand, String target) { + super(target, new Type[] { type }, new int[0], operand); } public If relabel(Map labels) { @@ -1202,81 +1229,28 @@ public If relabel(Map labels) { if (nlabel == null) { return this; } else { - return If(types[0], operands[0], operands[1], op, nlabel); + return If(types[0], operands[0], nlabel); } } public int opcode() { - return OPCODE_ifeq + op.offset; + return OPCODE_if; } @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return If(types[0], nOperands[0], nOperands[1], op, destination()); - } - - public int hashCode() { - return super.hashCode() + op.hashCode(); + return If(types[0], nOperands[0], destination()); } public boolean equals(Object o) { - if (o instanceof If) { - If ig = (If) o; - return op == ig.op && super.equals(ig); - } - return false; + return o instanceof If && super.equals(o); } public String toString() { - return "if" + op + " %" + operands[0] + ", %" + operands[1] + " goto " + destination() + " : " + types[0]; + return "if" + " %" + operands[0] + " goto " + destination() + " : " + types[0]; } } - /** - * Represents a comparison operator (e.g. '==','!=',etc) that is provided to - * a IfGoto bytecode. - * - * @author David J. Pearce - * - */ - public enum Comparator { - EQ(0) { - public String toString() { - return "eq"; - } - }, - NEQ(1) { - public String toString() { - return "ne"; - } - }, - LT(2) { - public String toString() { - return "lt"; - } - }, - LTEQ(3) { - public String toString() { - return "le"; - } - }, - GT(4) { - public String toString() { - return "gt"; - } - }, - GTEQ(5) { - public String toString() { - return "ge"; - } - }; - public int offset; - - private Comparator(int offset) { - this.offset = offset; - } - }; - /** * Branches conditionally to the given label based on the result of a * runtime type test against a value from the operand register. More @@ -2254,12 +2228,7 @@ private static int[] append(int operand, int[] operands) { // Binary Operators public static final int BINARY_OPERATOR = UNARY_ASSIGNABLE+11; - public static final int OPCODE_ifeq = BINARY_OPERATOR+0; - public static final int OPCODE_ifne = BINARY_OPERATOR+1; - public static final int OPCODE_iflt = BINARY_OPERATOR+2; - public static final int OPCODE_ifle = BINARY_OPERATOR+3; - public static final int OPCODE_ifgt = BINARY_OPERATOR+4; - public static final int OPCODE_ifge = BINARY_OPERATOR+5; + public static final int OPCODE_if = BINARY_OPERATOR+0; // Binary Assignables public static final int BINARY_ASSIGNABLE = BINARY_OPERATOR+6; @@ -2273,20 +2242,26 @@ private static int[] append(int operand, int[] operands) { public static final int OPCODE_mul = BINARY_ASSIGNABLE+6; public static final int OPCODE_div = BINARY_ASSIGNABLE+7; public static final int OPCODE_rem = BINARY_ASSIGNABLE+8; - public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+9; - public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+10; - public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+11; - public static final int OPCODE_lshr = BINARY_ASSIGNABLE+12; - public static final int OPCODE_rshr = BINARY_ASSIGNABLE+13; - public static final int OPCODE_arrayindex = BINARY_ASSIGNABLE+14; - public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+15; - public static final int OPCODE_array = BINARY_ASSIGNABLE+16; - public static final int OPCODE_record = BINARY_ASSIGNABLE+17; - public static final int OPCODE_newobject = BINARY_ASSIGNABLE+18; - public static final int OPCODE_assign = BINARY_ASSIGNABLE+19; + public static final int OPCODE_eq = BINARY_ASSIGNABLE+9; + public static final int OPCODE_ne = BINARY_ASSIGNABLE+10; + public static final int OPCODE_lt = BINARY_ASSIGNABLE+11; + public static final int OPCODE_le = BINARY_ASSIGNABLE+12; + public static final int OPCODE_gt = BINARY_ASSIGNABLE+13; + public static final int OPCODE_ge = BINARY_ASSIGNABLE+14; + public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+15; + public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+16; + public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+17; + public static final int OPCODE_lshr = BINARY_ASSIGNABLE+18; + public static final int OPCODE_rshr = BINARY_ASSIGNABLE+19; + public static final int OPCODE_arrayindex = BINARY_ASSIGNABLE+20; + public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+21; + public static final int OPCODE_array = BINARY_ASSIGNABLE+22; + public static final int OPCODE_record = BINARY_ASSIGNABLE+23; + public static final int OPCODE_newobject = BINARY_ASSIGNABLE+24; + public static final int OPCODE_assign = BINARY_ASSIGNABLE+25; // Nary Assignables - public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+20; + public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+26; public static final int OPCODE_invoke = NARY_ASSIGNABLE+2; public static final int OPCODE_indirectinvoke = NARY_ASSIGNABLE+3; diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java index bcabacbd88..287e8639e9 100644 --- a/modules/wyil/src/wyil/lang/CodeUtils.java +++ b/modules/wyil/src/wyil/lang/CodeUtils.java @@ -5,8 +5,6 @@ import java.util.HashMap; import java.util.Map; -import wyil.lang.Bytecode.Comparator; - public class CodeUtils { private static int _idx=0; @@ -48,30 +46,6 @@ public static int[] remapOperands(Map binding, int[] operands) } return nOperands; } - - /** - * Determine the inverse comparator, or null if no inverse exists. - * - * @param cop - * @return - */ - public static Bytecode.Comparator invert(Bytecode.Comparator cop) { - switch (cop) { - case EQ: - return Bytecode.Comparator.NEQ; - case NEQ: - return Bytecode.Comparator.EQ; - case LT: - return Bytecode.Comparator.GTEQ; - case LTEQ: - return Bytecode.Comparator.GT; - case GT: - return Bytecode.Comparator.LTEQ; - case GTEQ: - return Bytecode.Comparator.LT; - } - return null; - } /** * Construct a mapping from labels to their block indices within a root diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index fc998ad4b6..11c7dc9c66 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -474,35 +474,9 @@ private Object execute(Bytecode.Goto bytecode, Constant[] frame, Context context } private Object execute(Bytecode.If bytecode, Constant[] frame, Context context) { - Constant op1 = frame[bytecode.operand(0)]; - Constant op2 = frame[bytecode.operand(1)]; - boolean result; - switch (bytecode.op) { - // Handle cases which apply to all values - case EQ: - result = op1.equals(op2); - break; - case NEQ: - result = !op1.equals(op2); - break; - // Handle arithmetic cases - case LT: - result = lessThan(op1, op2, true, context); - break; - case LTEQ: - result = lessThan(op1, op2, false, context); - break; - case GT: - result = lessThan(op2, op1, true, context); - break; - case GTEQ: - result = lessThan(op2, op1, false, context); - break; - default: - return deadCode(context); - } - - if (result) { + Constant.Bool operand = checkType(frame[bytecode.operand(0)],context,Constant.Bool.class); + + if (operand.value) { // branch taken, so jump to destination label return context.getLabel(bytecode.destination()); } else { @@ -511,27 +485,6 @@ private Object execute(Bytecode.If bytecode, Constant[] frame, Context context) } } - private boolean elementOf(Constant lhs, Constant rhs, Context context) { - checkType(rhs, context, Constant.Array.class); - Constant.Array list = (Constant.Array) rhs; - return list.values.contains(lhs); - } - - private boolean lessThan(Constant lhs, Constant rhs, boolean isStrict, Context context) { - checkType(lhs, context, Constant.Integer.class); - checkType(rhs, context, Constant.Integer.class); - Constant.Integer lhs_i = (Constant.Integer) lhs; - Constant.Integer rhs_i = (Constant.Integer) rhs; - int result = lhs_i.compareTo(rhs_i); - // In the strict case, the lhs must be strictly below the rhs. In the - // non-strict case, they can be equal. - if (isStrict) { - return result < 0; - } else { - return result <= 0; - } - } - private Object execute(Bytecode.IfIs bytecode, Constant[] frame, Context context) { Type typeOperand = bytecode.type(1); Constant op = frame[bytecode.operand(0)]; diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index c3966c93d6..520f5f2634 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -30,6 +30,14 @@ public class StandardFunctions { standardFunctions[Bytecode.OPCODE_mul ] = new Multiply(); standardFunctions[Bytecode.OPCODE_div ] = new Divide(); standardFunctions[Bytecode.OPCODE_rem ] = new Remainder(); + + standardFunctions[Bytecode.OPCODE_eq ] = new Equal(); + standardFunctions[Bytecode.OPCODE_ne ] = new NotEqual(); + standardFunctions[Bytecode.OPCODE_lt ] = new LessThan(); + standardFunctions[Bytecode.OPCODE_le ] = new LessThanEqual(); + standardFunctions[Bytecode.OPCODE_gt ] = new GreaterThan(); + standardFunctions[Bytecode.OPCODE_ge ] = new GreaterThanEqual(); + standardFunctions[Bytecode.OPCODE_bitwiseor] = new BitwiseOr(); standardFunctions[Bytecode.OPCODE_bitwisexor] = new BitwiseXor(); standardFunctions[Bytecode.OPCODE_bitwiseand] = new BitwiseAnd(); @@ -128,7 +136,42 @@ public Constant apply(Constant[] operands, Context context) { return lhs.remainder(rhs); } } - + private static final class Equal implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return Constant.V_BOOL(operands[0].equals(operands[1])); + } + } + private static final class NotEqual implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return Constant.V_BOOL(!operands[0].equals(operands[1])); + } + } + private static final class LessThan implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return lessThan(operands[0],operands[1],true,context); + } + } + private static final class LessThanEqual implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return lessThan(operands[0],operands[1],false,context); + } + } + private static final class GreaterThan implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return lessThan(operands[1],operands[0],true,context); + } + } + private static final class GreaterThanEqual implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + return lessThan(operands[1],operands[0],false,context); + } + } // ==================================================================================== // Bytes // ==================================================================================== @@ -254,4 +297,25 @@ public Constant apply(Constant[] operands, Context context) { return Constant.V_RECORD(values); } } + + + // ==================================================================================== + // Helpers + // ==================================================================================== + private static Constant.Bool lessThan(Constant lhs, Constant rhs, boolean isStrict, Context context) { + checkType(lhs, context, Constant.Integer.class); + checkType(rhs, context, Constant.Integer.class); + Constant.Integer lhs_i = (Constant.Integer) lhs; + Constant.Integer rhs_i = (Constant.Integer) rhs; + int result = lhs_i.compareTo(rhs_i); + // In the strict case, the lhs must be strictly below the rhs. In the + // non-strict case, they can be equal. + if (isStrict) { + return Constant.V_BOOL(result < 0); + } else { + return Constant.V_BOOL(result <= 0); + } + + } + } diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 8b0f150afb..89354ea73a 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -45,6 +45,7 @@ import static wyil.lang.Bytecode.*; import wyil.util.TypeExpander; import static wyil.util.ErrorMessages.internalFailure; +import static wyjc.Wyil2JavaBuilder.WHILEYUTIL; import wyjc.util.BytecodeTranslators; import wyjc.util.WyjcBuildTask; @@ -864,8 +865,7 @@ private void translate(CodeForest.Index index, Switch c, int freeSlot, if (canUseSwitchBytecode) { JvmType.Function ftype = new JvmType.Function(T_INT); bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); - bytecodes.add(new Bytecode.Invoke(WHILEYINT, "intValue", ftype, - Bytecode.InvokeMode.VIRTUAL)); + bytecodes.add(new Bytecode.Invoke(WHILEYINT, "intValue", ftype, Bytecode.InvokeMode.VIRTUAL)); bytecodes.add(new Bytecode.Switch(c.defaultTarget, cases)); } else { // ok, in this case we have to fall back to series of the if @@ -875,7 +875,10 @@ private void translate(CodeForest.Index index, Switch c, int freeSlot, String target = p.second(); translate(value, freeSlot, bytecodes); bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); - translateIfGoto(index, value.type(), wyil.lang.Bytecode.Comparator.EQ, target, freeSlot + 1, forest, bytecodes); + JvmType.Function ftype = new JvmType.Function(T_BOOL, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); + bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "equals", ftype, Bytecode.InvokeMode.STATIC)); + bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, target)); + } bytecodes.add(new Bytecode.Goto(c.defaultTarget)); } @@ -885,94 +888,12 @@ private void translateIfGoto(CodeForest.Index index, If code, int freeSlot, Code ArrayList bytecodes) { JvmType jt = convertUnderlyingType(code.type(0)); bytecodes.add(new Bytecode.Load(code.operand(0), jt)); - bytecodes.add(new Bytecode.Load(code.operand(1), jt)); - translateIfGoto(index, code.type(0), code.op, code.destination(), freeSlot, forest, bytecodes); - } - - private void translateIfGoto(CodeForest.Index index, Type c_type, wyil.lang.Bytecode.Comparator cop, String target, int freeSlot, - CodeForest forest, ArrayList bytecodes) { - - JvmType type = convertUnderlyingType(c_type); - // Just use the Object.equals() method, followed - // by "if" bytecode. - Bytecode.IfMode op; - switch (cop) { - case EQ: { - if (Type.isSubtype(c_type, Type.T_NULL)) { - // this indicates an interesting special case. The left - // handside of this equality can be null. Therefore, we - // cannot directly call "equals()" on this method, since - // this would cause a null pointer exception! - JvmType.Function ftype = new JvmType.Function(T_BOOL, - JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "equals", ftype, - Bytecode.InvokeMode.STATIC)); - } else { - JvmType.Function ftype = new JvmType.Function(T_BOOL, - JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "equals", ftype, Bytecode.InvokeMode.VIRTUAL)); - } - op = Bytecode.IfMode.NE; - break; - } - case NEQ: { - if (Type.isSubtype(c_type, Type.T_NULL)) { - // this indicates an interesting special case. The left - // handside of this equality can be null. Therefore, we - // cannot directly call "equals()" on this method, since - // this would cause a null pointer exception! - JvmType.Function ftype = new JvmType.Function(T_BOOL, - JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "equals", ftype, - Bytecode.InvokeMode.STATIC)); - } else { - JvmType.Function ftype = new JvmType.Function(T_BOOL, - JAVA_LANG_OBJECT); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "equals", ftype, Bytecode.InvokeMode.VIRTUAL)); - } - op = Bytecode.IfMode.EQ; - break; - } - case LT: { - JvmType.Function ftype = new JvmType.Function(T_INT, type); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL)); - op = Bytecode.IfMode.LT; - break; - } - case LTEQ: { - JvmType.Function ftype = new JvmType.Function(T_INT, type); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL)); - op = Bytecode.IfMode.LE; - break; - } - case GT: { - JvmType.Function ftype = new JvmType.Function(T_INT, type); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL)); - op = Bytecode.IfMode.GT; - break; - } - case GTEQ: { - JvmType.Function ftype = new JvmType.Function(T_INT, type); - bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, - "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL)); - op = Bytecode.IfMode.GE; - break; - } - default: - internalFailure("unknown if condition encountered", filename, - forest.get(index).attribute(SourceLocation.class)); - return; - } - - // do the jump - bytecodes.add(new Bytecode.If(op, target)); + JvmType.Function ftype = new JvmType.Function(T_BOOL); + bytecodes.add(new Bytecode.Invoke(WHILEYBOOL, "value", ftype, Bytecode.InvokeMode.VIRTUAL)); + bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, code.destination())); } + private void translate(CodeForest.Index index, IfIs c, int freeSlot, CodeForest forest, ArrayList bytecodes) { diff --git a/modules/wyjc/src/wyjc/runtime/Util.java b/modules/wyjc/src/wyjc/runtime/Util.java index 6f02d9f549..35109f4b3f 100755 --- a/modules/wyjc/src/wyjc/runtime/Util.java +++ b/modules/wyjc/src/wyjc/runtime/Util.java @@ -84,6 +84,30 @@ public static boolean equals(Object o1, Object o2) { return (o1 != null && o1.equals(o2)) || (o1 == o2); } + public static WyBool equal(Object o1, Object o2) { + return WyBool.valueOf(equals(o1,o2)); + } + + public static WyBool notEqual(Object o1, Object o2) { + return WyBool.valueOf(!equals(o1,o2)); + } + + public static WyBool lessThan(BigInteger i1, BigInteger i2) { + return WyBool.valueOf(i1.compareTo(i2) < 0); + } + + public static WyBool lessThanEqual(BigInteger i1, BigInteger i2) { + return WyBool.valueOf(i1.compareTo(i2) <= 0); + } + + public static WyBool greaterThan(BigInteger i1, BigInteger i2) { + return WyBool.valueOf(i1.compareTo(i2) > 0); + } + + public static WyBool greaterThanEqual(BigInteger i1, BigInteger i2) { + return WyBool.valueOf(i1.compareTo(i2) >= 0); + } + /** * The instanceOf method implements a runtime type test. */ diff --git a/modules/wyjc/src/wyjc/runtime/WyBool.java b/modules/wyjc/src/wyjc/runtime/WyBool.java index 2695ffa27d..54302f067e 100644 --- a/modules/wyjc/src/wyjc/runtime/WyBool.java +++ b/modules/wyjc/src/wyjc/runtime/WyBool.java @@ -7,6 +7,10 @@ private WyBool(boolean value) { this.value = value; } + public boolean value() { + return value; + } + public boolean equals(Object o) { if (o instanceof WyBool) { WyBool b = (WyBool) o; diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java index 4812e30568..9e2f469a8f 100644 --- a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -36,6 +36,12 @@ public class BytecodeTranslators { standardFunctions[OPCODE_mul ] = new Multiply(); standardFunctions[OPCODE_div ] = new Divide(); standardFunctions[OPCODE_rem ] = new Remainder(); + standardFunctions[OPCODE_eq ] = new Equal(); + standardFunctions[OPCODE_ne ] = new NotEqual(); + standardFunctions[OPCODE_lt ] = new LessThan(); + standardFunctions[OPCODE_le ] = new LessThanEqual(); + standardFunctions[OPCODE_gt ] = new GreaterThan(); + standardFunctions[OPCODE_ge ] = new GreaterThanEqual(); standardFunctions[OPCODE_bitwiseor] = new BitwiseOr(); standardFunctions[OPCODE_bitwisexor] = new BitwiseXor(); standardFunctions[OPCODE_bitwiseand] = new BitwiseAnd(); @@ -169,6 +175,72 @@ public void translate(Operator bytecode, Context context) { } } + private static final class Equal implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + JvmType.Function ftype = new JvmType.Function(WHILEYBOOL, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); + context.add(new Bytecode.Invoke(WHILEYUTIL, "equal", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class NotEqual implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + JvmType.Function ftype = new JvmType.Function(WHILEYBOOL, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); + context.add(new Bytecode.Invoke(WHILEYUTIL, "notEqual", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class LessThan implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + JvmType.Function ftype = new JvmType.Function(WHILEYBOOL, WHILEYINT, WHILEYINT); + context.add(new Bytecode.Invoke(WHILEYUTIL, "lessThan", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class LessThanEqual implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + JvmType.Function ftype = new JvmType.Function(WHILEYBOOL, WHILEYINT, WHILEYINT); + context.add(new Bytecode.Invoke(WHILEYUTIL, "lessThanEqual", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class GreaterThan implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + JvmType.Function ftype = new JvmType.Function(WHILEYBOOL, WHILEYINT, WHILEYINT); + context.add(new Bytecode.Invoke(WHILEYUTIL, "greaterThan", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + private static final class GreaterThanEqual implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Load(bytecode.operand(1), type)); + JvmType.Function ftype = new JvmType.Function(WHILEYBOOL, WHILEYINT, WHILEYINT); + context.add(new Bytecode.Invoke(WHILEYUTIL, "greaterThanEqual", ftype, Bytecode.InvokeMode.STATIC)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } // ==================================================================================== // Bytes // ==================================================================================== From 0b7aae7d9c9a12cfdeeb02306144ea43d57aa053 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Sat, 16 Apr 2016 22:01:29 +1200 Subject: [PATCH 24/43] Remove Bytecode "Constructors" #502 This removes the so-called bytecode constructors, which were static methods that actually instantiated Bytecode objects. Whilst these were actually quite nice in some ways, they were just accounting for yet more code. Removing makes little or no difference. --- .../wyc/src/wyc/builder/CodeGenerator.java | 182 ++++++------ modules/wyil/src/wyil/io/WyilFileReader.java | 92 +++--- modules/wyil/src/wyil/lang/Bytecode.java | 276 +++--------------- .../src/wyil/transforms/LoopVariants.java | 4 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 4 +- 5 files changed, 185 insertions(+), 373 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index c0b21b78aa..b2454fd630 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -259,7 +259,7 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw // The following is sneaky. It guarantees that every method ends in a // return. For methods that actually need a value, this is either // removed as dead-code or remains and will cause an error. - body.add(Bytecode.Return(), attributes(fd)); + body.add(new Bytecode.Return(), attributes(fd)); WyilFile.FunctionOrMethod declaration; @@ -294,9 +294,9 @@ private int generateInvariantBlock(Expr invariant, Environment environment, Code int index = forest.add(precondition); String endLab = CodeUtils.freshLabel(); generateCondition(endLab, invariant, environment, precondition, forest, context); - precondition.add(Bytecode.Fail(), attributes(invariant)); - precondition.add(Bytecode.Label(endLab)); - precondition.add(Bytecode.Return()); + precondition.add(new Bytecode.Fail(), attributes(invariant)); + precondition.add(new Bytecode.Label(endLab)); + precondition.add(new Bytecode.Return()); return index; } @@ -451,7 +451,7 @@ private void generate(VariableDeclaration s, Environment environment, CodeForest // Second, translate initialiser expression if it exists. if (s.expr != null) { int[] operands = { generate(s.expr, environment, block, forest, context) }; - block.add(Bytecode.Operator(s.expr.result().raw(), targets, operands, Bytecode.OperatorKind.ASSIGN), + block.add(new Bytecode.Operator(s.expr.result().raw(), targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(s)); } } @@ -545,7 +545,7 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme // for variable on the left-hand side. int[] targets = new int[] { environment.get(v.var) }; int[] operands = new int[] { operand }; - block.add(Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); + block.add(new Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); } else if (lval instanceof Expr.IndexOf || lval instanceof Expr.FieldAccess || lval instanceof Expr.Dereference) { // This is the more complicated case, since the left-hand side @@ -558,8 +558,8 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme ArrayList operands = new ArrayList(); Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, environment, block, forest, context); int target = environment.get(lhs.var); - block.add(Bytecode.Update(lhs.type.raw(), target, operands, operand, lhs.afterType.raw(), fields), - attributes(lval)); + block.add(new Bytecode.Update(lhs.type.raw(), target, toIntArray(operands), operand, lhs.afterType.raw(), + fields), attributes(lval)); } else { WhileyFile.syntaxError("invalid assignment", context, lval); } @@ -641,10 +641,10 @@ private void generate(Stmt.Assert s, Environment environment, CodeForest.Block b int body = forest.add(subblock); String endLab = CodeUtils.freshLabel(); generateCondition(endLab, s.expr, environment, subblock, forest, context); - subblock.add(Bytecode.Fail(), attributes(s.expr)); - subblock.add(Bytecode.Label(endLab)); + subblock.add(new Bytecode.Fail(), attributes(s.expr)); + subblock.add(new Bytecode.Label(endLab)); // Second, create assert bytecode - block.add(Bytecode.Assert(body), attributes(s)); + block.add(new Bytecode.Assert(body), attributes(s)); } @@ -670,10 +670,10 @@ private void generate(Stmt.Assume s, Environment environment, CodeForest.Block b int body = forest.add(subblock); String endLab = CodeUtils.freshLabel(); generateCondition(endLab, s.expr, environment, subblock, forest, context); - subblock.add(Bytecode.Fail(), attributes(s.expr)); - subblock.add(Bytecode.Label(endLab)); + subblock.add(new Bytecode.Fail(), attributes(s.expr)); + subblock.add(new Bytecode.Label(endLab)); // Second, create assert bytecode - block.add(Bytecode.Assume(body), attributes(s)); + block.add(new Bytecode.Assume(body), attributes(s)); } /** @@ -732,7 +732,7 @@ private void generate(Stmt.Return s, Environment environment, CodeForest.Block b operands[index++] = generate(e, environment, block, forest, context); } } - block.add(Bytecode.Return(types, operands), attributes(s)); + block.add(new Bytecode.Return(types, operands), attributes(s)); } /** @@ -791,7 +791,7 @@ private void generate(Stmt.Skip s, Environment environment, CodeForest.Block blo private void generate(Stmt.Debug s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { int operand = generate(s.expr, environment, block, forest, context); - block.add(Bytecode.Debug(operand), attributes(s)); + block.add(new Bytecode.Debug(operand), attributes(s)); } /** @@ -821,7 +821,7 @@ private void generate(Stmt.Debug s, Environment environment, CodeForest.Block bl */ private void generate(Stmt.Fail s, Environment environment, CodeForest.Block block, CodeForest forest, Context context) { - block.add(Bytecode.Fail(), attributes(s)); + block.add(new Bytecode.Fail(), attributes(s)); } /** @@ -881,14 +881,14 @@ private void generate(Stmt.IfElse s, Environment environment, CodeForest.Block b generate(st, environment, block, forest, context); } if (!s.falseBranch.isEmpty()) { - block.add(Bytecode.Goto(exitLab)); - block.add(Bytecode.Label(falseLab)); + block.add(new Bytecode.Goto(exitLab)); + block.add(new Bytecode.Label(falseLab)); for (Stmt st : s.falseBranch) { generate(st, environment, block, forest, context); } } - block.add(Bytecode.Label(exitLab)); + block.add(new Bytecode.Label(exitLab)); } /** @@ -942,7 +942,7 @@ private void generate(Stmt.Break s, Environment environment, CodeForest.Block bl if (scope == null) { WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context, s); } - block.add(Bytecode.Goto(scope.breakLabel)); + block.add(new Bytecode.Goto(scope.breakLabel)); } /** @@ -999,7 +999,7 @@ private void generate(Stmt.Continue s, Environment environment, CodeForest.Block if (scope == null) { WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context, s); } - block.add(Bytecode.Goto(scope.continueLabel)); + block.add(new Bytecode.Goto(scope.continueLabel)); } /** @@ -1074,16 +1074,16 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context, c); } else { defaultTarget = CodeUtils.freshLabel(); - block.add(Bytecode.Label(defaultTarget), attributes(c)); + block.add(new Bytecode.Label(defaultTarget), attributes(c)); for (Stmt st : c.stmts) { generate(st, environment, block, forest, context); } - block.add(Bytecode.Goto(exitLab), attributes(c)); + block.add(new Bytecode.Goto(exitLab), attributes(c)); } } else if (defaultTarget == exitLab) { String target = CodeUtils.freshLabel(); - block.add(Bytecode.Label(target), attributes(c)); + block.add(new Bytecode.Label(target), attributes(c)); // Case statements in Whiley may have multiple matching constant // values. Therefore, we iterate each matching value and @@ -1103,7 +1103,7 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b for (Stmt st : c.stmts) { generate(st, environment, block, forest, context); } - block.add(Bytecode.Goto(exitLab), attributes(c)); + block.add(new Bytecode.Goto(exitLab), attributes(c)); } else { // This represents the case where we have another non-default @@ -1113,8 +1113,8 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b } } - block.add(start, Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); - block.add(Bytecode.Label(exitLab), attributes(s)); + block.add(start, new Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); + block.add(new Bytecode.Label(exitLab), attributes(s)); } /** @@ -1173,7 +1173,7 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl for (Expr condition : s.invariants) { int invariant = generateInvariantBlock(condition, environment, forest, context); - bodyBlock.add(Bytecode.Invariant(invariant), attributes(condition)); + bodyBlock.add(new Bytecode.Invariant(invariant), attributes(condition)); } generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); @@ -1184,9 +1184,9 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl } scopes.pop(); // break - bodyBlock.add(Bytecode.Label(continueLab), attributes(s)); - block.add(Bytecode.Loop(new int[] {}, body), attributes(s)); - block.add(Bytecode.Label(exitLab), attributes(s)); + bodyBlock.add(new Bytecode.Label(continueLab), attributes(s)); + block.add(new Bytecode.Loop(new int[] {}, body), attributes(s)); + block.add(new Bytecode.Label(exitLab), attributes(s)); } /** @@ -1252,14 +1252,14 @@ private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block for (Expr condition : s.invariants) { int invariant = generateInvariantBlock(condition, environment, forest, context); - bodyBlock.add(Bytecode.Invariant(invariant), attributes(condition)); + bodyBlock.add(new Bytecode.Invariant(invariant), attributes(condition)); } - bodyBlock.add(Bytecode.Label(continueLab), attributes(s)); + bodyBlock.add(new Bytecode.Label(continueLab), attributes(s)); generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); - block.add(Bytecode.Loop(new int[] {}, body), attributes(s)); - block.add(Bytecode.Label(exitLab), attributes(s)); + block.add(new Bytecode.Loop(new int[] {}, body), attributes(s)); + block.add(new Bytecode.Label(exitLab), attributes(s)); } // ========================================================================= @@ -1342,7 +1342,7 @@ public void generateCondition(String target, Expr condition, Environment environ // example, !(x < 5) could be rewritten into x >= 5. int result = generate(condition, environment, block, forest, context); - block.add(Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); + block.add(new Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); } else { syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, condition); @@ -1388,7 +1388,7 @@ private void generateCondition(String target, Expr.Constant c, Environment envir CodeForest forest, Context context) { Constant.Bool b = (Constant.Bool) c.value; if (b.value) { - block.add(Bytecode.Goto(target)); + block.add(new Bytecode.Goto(target)); } else { // do nout } @@ -1427,7 +1427,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm String exitLabel = CodeUtils.freshLabel(); generateCondition(exitLabel, invert(v.lhs), environment, block, forest, context); generateCondition(target, v.rhs, environment, block, forest, context); - block.add(Bytecode.Label(exitLabel)); + block.add(new Bytecode.Label(exitLabel)); } else if (bop == Expr.BOp.IS) { generateTypeCondition(target, v, environment, block, forest, context); @@ -1441,7 +1441,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); } int slot = environment.get(lhs.var); - block.add(Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); + block.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); } else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) { // this is a simple rewrite to enable type inference. @@ -1451,12 +1451,12 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); } int slot = environment.get(lhs.var); - block.add(Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); - block.add(Bytecode.Goto(target)); - block.add(Bytecode.Label(exitLabel)); + block.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); + block.add(new Bytecode.Goto(target)); + block.add(new Bytecode.Label(exitLabel)); } else { int result = generate(v, environment, block, forest, context); - block.add(Bytecode.If(v.srcType.raw(), result, target), attributes(v)); + block.add(new Bytecode.If(v.srcType.raw(), result, target), attributes(v)); } } } @@ -1511,7 +1511,7 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Environm // following cast is always safe. Expr.TypeVal rhs = (Expr.TypeVal) condition.rhs; - block.add(Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); + block.add(new Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); } /** @@ -1548,8 +1548,8 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme String label = CodeUtils.freshLabel(); generateCondition(label, v.mhs, environment, block, forest, context); - block.add(Bytecode.Goto(target)); - block.add(Bytecode.Label(label)); + block.add(new Bytecode.Goto(target)); + block.add(new Bytecode.Label(label)); return; default: // Nothing else is a valud boolean condition here. @@ -1586,14 +1586,14 @@ private void generateCondition(String target, Expr.Quantifier e, Environment env switch (e.cop) { case NONE: - block.add(Bytecode.Goto(target)); - block.add(Bytecode.Label(exit)); + block.add(new Bytecode.Goto(target)); + block.add(new Bytecode.Label(exit)); break; case SOME: break; case ALL: - block.add(Bytecode.Goto(target)); - block.add(Bytecode.Label(exit)); + block.add(new Bytecode.Goto(target)); + block.add(new Bytecode.Label(exit)); break; } } @@ -1615,7 +1615,7 @@ private void generate(Iterator> srcIterator, String t int body = forest.add(bodyBlock); generate(srcIterator, trueLabel, falseLabel, e, environment, bodyBlock, forest, context); // Finally, create the forall loop bytecode - block.add(Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], body), attributes(e)); + block.add(new Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], body), attributes(e)); } else { // This is the base case (i.e. the innermost loop) switch (e.cop) { @@ -1669,7 +1669,7 @@ public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment CodeForest forest, Context context, int... targets) throws ResolveError { // int[] operands = generate(expr.arguments, environment, block, forest, context); - block.add(Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); + block.add(new Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); } public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, CodeForest.Block block, @@ -1677,7 +1677,7 @@ public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment env // int operand = generate(expr.src, environment, block, forest, context); int[] operands = generate(expr.arguments, environment, block, forest, context); - block.add(Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); + block.add(new Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); } // ========================================================================= @@ -1773,7 +1773,7 @@ private int generate(Expr.Constant expr, Environment environment, CodeForest.Blo Context context) { Constant val = expr.value; int target = environment.allocate(val.type()); - block.add(Bytecode.Const(target, expr.value), attributes(expr)); + block.add(new Bytecode.Const(target, expr.value), attributes(expr)); return target; } @@ -1782,7 +1782,7 @@ private int generate(Expr.FunctionOrMethod expr, Environment environment, CodeFo Type.FunctionOrMethod rawType = expr.type.raw(); Type.FunctionOrMethod nominalType = expr.type.nominal(); int target = environment.allocate(rawType); - block.add(Bytecode.Lambda(nominalType, target, Collections.EMPTY_LIST, expr.nid), attributes(expr)); + block.add(new Bytecode.Lambda(nominalType, target, new int[0], expr.nid), attributes(expr)); return target; } @@ -1819,10 +1819,10 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block CodeForest.Block bodyBlock = new CodeForest.Block(); bodyForest.addAsRoot(bodyBlock); if (tfm.returns().isEmpty()) { - bodyBlock.add(Bytecode.Return(), attributes(expr)); + bodyBlock.add(new Bytecode.Return(), attributes(expr)); } else { int target = generate(expr.body, benv, bodyBlock, bodyForest, context); - bodyBlock.add(Bytecode.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), + bodyBlock.add(new Bytecode.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), attributes(expr)); } @@ -1854,7 +1854,7 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block // Finally, create the lambda int target = environment.allocate(tfm); - block.add(Bytecode.Lambda(cfm, target, operands, nid), attributes(expr)); + block.add(new Bytecode.Lambda(cfm, target, toIntArray(operands), nid), attributes(expr)); return target; } @@ -1862,7 +1862,7 @@ private int generate(Expr.ConstantAccess expr, Environment environment, CodeFore Context context) throws ResolveError { Constant val = expr.value; int target = environment.allocate(val.type()); - block.add(Bytecode.Const(target, val), attributes(expr)); + block.add(new Bytecode.Const(target, val), attributes(expr)); return target; } @@ -1885,25 +1885,25 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b int[] targets = new int[] { environment.allocate(expr.result().raw()) }; switch (expr.op) { case NEG: - block.add(Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), + block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), attributes(expr)); break; case INVERT: - block.add(Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), + block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), attributes(expr)); break; case NOT: String falseLabel = CodeUtils.freshLabel(); String exitLabel = CodeUtils.freshLabel(); generateCondition(falseLabel, expr.mhs, environment, block, forest, context); - block.add(Bytecode.Const(targets[0], Constant.V_BOOL(true)), attributes(expr)); - block.add(Bytecode.Goto(exitLabel)); - block.add(Bytecode.Label(falseLabel)); - block.add(Bytecode.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); - block.add(Bytecode.Label(exitLabel)); + block.add(new Bytecode.Const(targets[0], Constant.V_BOOL(true)), attributes(expr)); + block.add(new Bytecode.Goto(exitLabel)); + block.add(new Bytecode.Label(falseLabel)); + block.add(new Bytecode.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); + block.add(new Bytecode.Label(exitLabel)); break; case ARRAYLENGTH: - block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); + block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); break; default: // should be dead-code @@ -1917,7 +1917,7 @@ private int generate(Expr.Dereference expr, Environment environment, CodeForest. Context context) { int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), + block.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), attributes(expr)); return targets[0]; } @@ -1927,7 +1927,7 @@ private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Bloc int[] operands = { generate(expr.src, environment, block, forest, context), generate(expr.index, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); + block.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); return targets[0]; } @@ -1937,7 +1937,7 @@ private int generate(Expr.Cast expr, Environment environment, CodeForest.Block b Type from = expr.expr.result().raw(); Type to = expr.result().raw(); int target = environment.allocate(to); - block.add(Bytecode.Convert(from, target, operand, to), attributes(expr)); + block.add(new Bytecode.Convert(from, target, operand, to), attributes(expr)); return target; } @@ -1949,11 +1949,11 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo String exitLabel = CodeUtils.freshLabel(); generateCondition(trueLabel, v, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - block.add(Bytecode.Const(target, Constant.V_BOOL(false)), attributes(v)); - block.add(Bytecode.Goto(exitLabel)); - block.add(Bytecode.Label(trueLabel)); - block.add(Bytecode.Const(target, Constant.V_BOOL(true)), attributes(v)); - block.add(Bytecode.Label(exitLabel)); + block.add(new Bytecode.Const(target, Constant.V_BOOL(false)), attributes(v)); + block.add(new Bytecode.Goto(exitLabel)); + block.add(new Bytecode.Label(trueLabel)); + block.add(new Bytecode.Const(target, Constant.V_BOOL(true)), attributes(v)); + block.add(new Bytecode.Label(exitLabel)); return target; } else { Type result = v.result().raw(); @@ -1963,7 +1963,7 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo generate(v.rhs, environment, block, forest, context) }; - block.add(Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context)), attributes(v)); + block.add(new Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context)), attributes(v)); return targets[0]; } @@ -1973,7 +1973,7 @@ private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeFo Context context) { int[] operands = generate(expr.arguments, environment, block, forest, context); int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), + block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), attributes(expr)); return targets[0]; } @@ -1983,7 +1983,7 @@ private int generate(Expr.ArrayGenerator expr, Environment environment, CodeFore int[] operands = new int[] { generate(expr.element, environment, block, forest, context), generate(expr.count, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); + block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); return targets[0]; } @@ -1993,11 +1993,11 @@ private int generate(Expr.Quantifier e, Environment environment, CodeForest.Bloc String exitLabel = CodeUtils.freshLabel(); generateCondition(trueLabel, e, environment, block, forest, context); int target = environment.allocate(Type.T_BOOL); - block.add(Bytecode.Const(target, Constant.V_BOOL(false)), attributes(e)); - block.add(Bytecode.Goto(exitLabel)); - block.add(Bytecode.Label(trueLabel)); - block.add(Bytecode.Const(target, Constant.V_BOOL(true)), attributes(e)); - block.add(Bytecode.Label(exitLabel)); + block.add(new Bytecode.Const(target, Constant.V_BOOL(false)), attributes(e)); + block.add(new Bytecode.Goto(exitLabel)); + block.add(new Bytecode.Label(trueLabel)); + block.add(new Bytecode.Const(target, Constant.V_BOOL(true)), attributes(e)); + block.add(new Bytecode.Label(exitLabel)); return target; } @@ -2012,7 +2012,7 @@ private int generate(Expr.Record expr, Environment environment, CodeForest.Block operands[i] = generate(arg, environment, block, forest, context); } int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), + block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), attributes(expr)); return targets[0]; } @@ -2021,7 +2021,7 @@ private int generate(Expr.FieldAccess expr, Environment environment, CodeForest. Context context) { int operand = generate(expr.src, environment, block, forest, context); int target = environment.allocate(expr.result().raw()); - block.add(Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), + block.add(new Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), attributes(expr)); return target; } @@ -2030,7 +2030,7 @@ private int generate(Expr.New expr, Environment environment, CodeForest.Block bl Context context) throws ResolveError { int[] operands = new int[] { generate(expr.expr, environment, block, forest, context) }; int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); + block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); return targets[0]; } @@ -2232,6 +2232,14 @@ public List toIntegerList(int... items) { return list; } + public int[] toIntArray(List items) { + int[] arr = new int[items.size()]; + for(int i=0;i!=arr.length;++i) { + arr[i] = items.get(i); + } + return arr; + } + /** * Maintains a mapping from Variable names to their allocated register slot, * and their declared types. diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 732e3e8b1a..5a401e54fe 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -842,7 +842,7 @@ private Type[] readTypes(int nTypes) throws IOException { private static Bytecode.Label findLabel(int target, HashMap labels) { Bytecode.Label label = labels.get(target); if (label == null) { - label = Bytecode.Label("label" + labelCount++); + label = new Bytecode.Label("label" + labelCount++); labels.put(target, label); } return label; @@ -860,27 +860,27 @@ private static Bytecode.Label findLabel(int target, HashMap[] cases = (Pair[]) extras[1]; - return Bytecode.Switch(types[0], operands[0], defaultTarget, Arrays.asList(cases)); + return new Bytecode.Switch(types[0], operands[0], defaultTarget, Arrays.asList(cases)); } }; schemas[Bytecode.OPCODE_ifis] = new Schema(Targets.ZERO, Operands.ONE, Types.TWO, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.IfIs(types[0], operands[0], types[1], (String)extras[0]); + return new Bytecode.IfIs(types[0], operands[0], types[1], (String)extras[0]); } }; @@ -915,47 +915,47 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_assign] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ASSIGN); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ASSIGN); } }; schemas[Bytecode.OPCODE_newobject] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0],targets,operands,Bytecode.OperatorKind.NEW); + return new Bytecode.Operator(types[0],targets,operands,Bytecode.OperatorKind.NEW); } }; schemas[Bytecode.OPCODE_dereference] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DEREFERENCE); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DEREFERENCE); } }; schemas[Bytecode.OPCODE_arrayinvert] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEINVERT); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEINVERT); } }; schemas[Bytecode.OPCODE_arraylength] = new Schema(Targets.ONE, Operands.ONE, Types.ONE) { public Bytecode construct(int opcode, int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ARRAYLENGTH); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ARRAYLENGTH); } }; schemas[Bytecode.OPCODE_neg] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEG); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEG); } }; schemas[Bytecode.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); + return new Bytecode.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); } }; schemas[Bytecode.OPCODE_convert] = new Schema(Targets.ONE, Operands.ONE, Types.TWO){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Convert(types[0], targets[0], operands[0], types[1]); + return new Bytecode.Convert(types[0], targets[0], operands[0], types[1]); } }; schemas[Bytecode.OPCODE_const] = new Schema(Targets.ONE, Operands.ZERO, Types.ZERO, Extras.CONSTANT){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Const(targets[0], (Constant) extras[0]); + return new Bytecode.Const(targets[0], (Constant) extras[0]); } }; @@ -964,7 +964,7 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_if] = new Schema(Targets.ZERO, Operands.ONE, Types.ONE, Extras.TARGET){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.If(types[0], operands[0], (String) extras[0]); + return new Bytecode.If(types[0], operands[0], (String) extras[0]); } }; @@ -973,98 +973,98 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_add] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ADD); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ADD); } }; schemas[Bytecode.OPCODE_sub] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.SUB); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.SUB); } }; schemas[Bytecode.OPCODE_mul] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.MUL); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.MUL); } }; schemas[Bytecode.OPCODE_div] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DIV); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.DIV); } }; schemas[Bytecode.OPCODE_rem] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.REM); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.REM); } }; schemas[Bytecode.OPCODE_eq] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.EQ); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.EQ); } }; schemas[Bytecode.OPCODE_ne] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEQ); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEQ); } }; schemas[Bytecode.OPCODE_lt] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LT); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LT); } }; schemas[Bytecode.OPCODE_le] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LTEQ); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LTEQ); } }; schemas[Bytecode.OPCODE_gt] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.GT); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.GT); } }; schemas[Bytecode.OPCODE_ge] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.GTEQ); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.GTEQ); } }; schemas[Bytecode.OPCODE_bitwiseor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEOR); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEOR); } }; schemas[Bytecode.OPCODE_bitwisexor] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEXOR); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEXOR); } }; schemas[Bytecode.OPCODE_bitwiseand] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEAND); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.BITWISEAND); } }; schemas[Bytecode.OPCODE_lshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LEFTSHIFT); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.LEFTSHIFT); } }; schemas[Bytecode.OPCODE_rshr] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.RIGHTSHIFT); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.RIGHTSHIFT); } }; schemas[Bytecode.OPCODE_arrayindex] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0],targets,operands,Bytecode.OperatorKind.ARRAYINDEX); + return new Bytecode.Operator(types[0],targets,operands,Bytecode.OperatorKind.ARRAYINDEX); } }; schemas[Bytecode.OPCODE_arrygen] = new Schema(Targets.ONE, Operands.TWO, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands,Bytecode.OperatorKind.ARRAYGENERATOR); + return new Bytecode.Operator(types[0], targets, operands,Bytecode.OperatorKind.ARRAYGENERATOR); } }; schemas[Bytecode.OPCODE_array] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR); } }; @@ -1073,34 +1073,34 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types // ========================================================================= schemas[Bytecode.OPCODE_record] = new Schema(Targets.ONE, Operands.MANY, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR); + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR); } }; schemas[Bytecode.OPCODE_invoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Invoke((Type.FunctionOrMethod) types[0], targets, operands, (NameID) extras[0]); + return new Bytecode.Invoke((Type.FunctionOrMethod) types[0], targets, operands, (NameID) extras[0]); } }; schemas[Bytecode.OPCODE_indirectinvoke] = new Schema(Targets.MANY, Operands.MANY, Types.ONE){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { int src = operands[0]; operands = Arrays.copyOfRange(operands,1,operands.length); - return Bytecode.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, src, operands); + return new Bytecode.IndirectInvoke((Type.FunctionOrMethod) types[0], targets, src, operands); } }; schemas[Bytecode.OPCODE_lambda] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.NAME){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, (NameID) extras[0]); + return new Bytecode.Lambda((Type.FunctionOrMethod) types[0], targets[0], operands, (NameID) extras[0]); } }; schemas[Bytecode.OPCODE_loop] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Loop(targets, (Integer) extras[0]); + return new Bytecode.Loop(targets, (Integer) extras[0]); } }; schemas[Bytecode.OPCODE_quantify] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.BLOCK){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { - return Bytecode.Quantify(operands[0],operands[1],operands[2], targets, (Integer) extras[0]); + return new Bytecode.Quantify(operands[0],operands[1],operands[2], targets, (Integer) extras[0]); } }; schemas[Bytecode.OPCODE_update] = new Schema(Targets.MANY, Operands.MANY, Types.ONE, Extras.STRING_ARRAY){ @@ -1110,7 +1110,7 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types for(int i=0;i!=strings.length;++i) { fields.add(strings[i]); } int operand = operands[operands.length-1]; operands = Arrays.copyOf(operands, operands.length-1); - return Bytecode.Update(types[0], targets[0], operands, operand, types[1], fields); + return new Bytecode.Update(types[0], targets[0], operands, operand, types[1], fields); } }; } diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java index 28019ffd2f..75e1c2c22f 100755 --- a/modules/wyil/src/wyil/lang/Bytecode.java +++ b/modules/wyil/src/wyil/lang/Bytecode.java @@ -298,206 +298,6 @@ public boolean equals(Object o) { } } - // =============================================================== - // Bytecode Constructors - // =============================================================== - - /** - * Construct an assert bytecode which represents a user-defined - * assertion check. - * - * @param message - * --- message to report upon failure. - * @return - */ - public static Assert Assert(int block) { - return new Assert(block); - } - - /** - * Construct an assume bytecode which represents a user-defined - * assumption. - * - * @param message - * --- message to report upon failure. - * @return - */ - public static Assume Assume(int block) { - return new Assume(block); - } - - public static Operator Operator(Type type, int[] targets, int[] operands, OperatorKind op) { - return new Operator(type, targets, operands, op); - } - - /** - * Construct a const bytecode which loads a given constant onto - * the stack. - * - * @param afterType - * --- record type. - * @param field - * --- field to write. - * @return - */ - public static Const Const(int target, Constant constant) { - return new Const(target, constant); - } - - public static Convert Convert(Type from, int target, int operand, Type to) { - return new Convert(from, target, operand, to); - } - - public static final Debug Debug(int operand) { - return new Debug(operand); - } - - /** - * Construct a fail bytecode which halts execution by raising a - * fault. - * - * @param string - * --- Message to give on error. - * @return - */ - public static Fail Fail() { - return new Fail(); - } - - /** - * Construct a fieldload bytecode which reads a given field - * from a record of a given type. - * - * @param type - * --- record type. - * @param field - * --- field to load. - * @return - */ - public static FieldLoad FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { - return new FieldLoad(type, target, operand, field); - } - - /** - * Construct a goto bytecode which branches unconditionally to - * a given label. - * - * @param label - * --- destination label. - * @return - */ - public static Goto Goto(String label) { - return new Goto(label); - } - - public static Invoke Invoke(Type.FunctionOrMethod fun, Collection targets, Collection operands, - NameID name) { - return new Invoke(fun, CodeUtils.toIntArray(targets), CodeUtils.toIntArray(operands), name); - } - - public static Invoke Invoke(Type.FunctionOrMethod fun, int[] targets, int[] operands, NameID name) { - return new Invoke(fun, targets, operands, name); - } - - /** - * Construct an invariant bytecode which represents a - * user-defined loop invariant. - * - * @param message - * --- message to report upon failure. - * @return - */ - public static Invariant Invariant(int block) { - return new Invariant(block); - } - - public static Lambda Lambda(Type.FunctionOrMethod fun, int target, Collection operands, NameID name) { - return new Lambda(fun, target, CodeUtils.toIntArray(operands), name); - } - - public static Lambda Lambda(Type.FunctionOrMethod fun, int target, int[] operands, NameID name) { - return new Lambda(fun, target, operands, name); - } - - public static Loop Loop(int[] modifiedOperands, int block) { - return new Loop(modifiedOperands, block); - } - - /** - * Construct a return bytecode which does return a value and, hence, its - * type automatically defaults to void. - * - * @return - */ - public static Return Return() { - return new Return(new Type[0]); - } - - /** - * Construct a return bytecode which reads a value from the operand register - * and returns it. - * - * @param type - * --- type of the value to be returned (cannot be void). - * @param operand - * --- register to read return value from. - * @return - */ - public static Return Return(Type[] types, int... operands) { - return new Return(types, operands); - } - - public static If If(Type type, int operand, String label) { - return new If(type, operand, label); - } - - public static IfIs IfIs(Type type, int leftOperand, Type rightOperand, String label) { - return new IfIs(type, leftOperand, rightOperand, label); - } - - public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, int[] targets, int operand, - Collection operands) { - return new IndirectInvoke(fun, targets, operand, CodeUtils.toIntArray(operands)); - } - - public static IndirectInvoke IndirectInvoke(Type.FunctionOrMethod fun, int[] targets, int operand, int[] operands) { - return new IndirectInvoke(fun, targets, operand, operands); - } - - public static Label Label(String label) { - return new Label(label); - } - - /** - * Construct a switch bytecode which pops a value off the - * stack, and switches to a given label based on it. - * - * @param type - * --- value type to switch on. - * @param defaultLabel - * --- target for the default case. - * @param cases - * --- map from values to destination labels. - * @return - */ - public static Switch Switch(Type type, int operand, String defaultLabel, Collection> cases) { - return new Switch(type, operand, defaultLabel, cases); - } - - public static Quantify Quantify(int startOperand, int endOperand, int indexOperand, int[] modifiedOperands, - int block) { - return new Quantify(startOperand, endOperand, indexOperand, modifiedOperands, block); - } - - public static Update Update(Type beforeType, int target, Collection operands, int operand, Type afterType, - Collection fields) { - return new Update(beforeType, target, CodeUtils.toIntArray(operands), operand, afterType, fields); - } - - public static Update Update(Type beforeType, int target, int[] operands, int operand, Type afterType, - Collection fields) { - return new Update(beforeType, target, operands, operand, afterType, fields); - } // =============================================================== // Bytecode Implementations @@ -695,7 +495,7 @@ private OperatorKind(int offset) { public static final class Operator extends Bytecode { public final OperatorKind kind; - private Operator(Type type, int[] targets, int[] operands, OperatorKind bop) { + public Operator(Type type, int[] targets, int[] operands, OperatorKind bop) { super(new Type[] { type }, targets, operands); if (bop == null) { throw new IllegalArgumentException("Operator kind cannot be null"); @@ -710,7 +510,7 @@ public int opcode() { @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return Operator(type(0), nTargets, nOperands, kind); + return new Operator(type(0), nTargets, nOperands, kind); } public int hashCode() { @@ -772,12 +572,12 @@ public String toString() { */ public static final class Convert extends Bytecode { - private Convert(Type from, int target, int operand, Type result) { + public Convert(Type from, int target, int operand, Type result) { super(new Type[] { from, result }, new int[] { target }, operand); } public Bytecode clone(int[] nTargets, int[] nOperands) { - return Convert(type(0), nTargets[0], nOperands[0], type(1)); + return new Convert(type(0), nTargets[0], nOperands[0], type(1)); } public Type result() { @@ -836,7 +636,7 @@ public String toString() { public static final class Const extends Bytecode { public final Constant constant; - private Const(int target, Constant constant) { + public Const(int target, Constant constant) { super(new Type[0], new int[] { target }, new int[0]); this.constant = constant; } @@ -903,7 +703,7 @@ protected Bytecode clone(int[] nTargets, int[] nOperands) { */ public static final class Debug extends Bytecode { - private Debug(int operand) { + public Debug(int operand) { super(new Type[] { Type.Array(Type.T_INT, false) }, new int[0], operand); } @@ -913,7 +713,7 @@ public int opcode() { @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return Debug(nOperands[0]); + return new Debug(nOperands[0]); } public boolean equals(Object o) { @@ -946,7 +746,7 @@ private AssertOrAssume(int block) { */ public static class Assert extends AssertOrAssume { - private Assert(int block) { + public Assert(int block) { super(block); } @@ -976,7 +776,7 @@ protected Bytecode clone(int[] nTargets, int[] nOperands) { */ public static final class Assume extends AssertOrAssume { - private Assume(int block) { + public Assume(int block) { super(block); } @@ -1007,7 +807,7 @@ protected Bytecode clone(int[] nTargets, int[] nOperands) { * */ public static final class Fail extends Bytecode { - private Fail() { + public Fail() { super(new Type[0], new int[0]); } @@ -1056,7 +856,7 @@ public String toString() { public static final class FieldLoad extends Bytecode { public final String field; - private FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { + public FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) { super((Type) type, target, operand); if (field == null) { throw new IllegalArgumentException("FieldLoad field argument cannot be null"); @@ -1066,7 +866,7 @@ private FieldLoad(Type.EffectiveRecord type, int target, int operand, String fie @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return FieldLoad((Type.EffectiveRecord) type(0), nTargets[0], nOperands[0], field); + return new FieldLoad((Type.EffectiveRecord) type(0), nTargets[0], nOperands[0], field); } public int opcode() { @@ -1137,7 +937,7 @@ public String toString() { * */ public static final class Goto extends Branching { - private Goto(String target) { + public Goto(String target) { super(target, new Type[0], new int[0]); } @@ -1150,7 +950,7 @@ public Goto relabel(Map labels) { if (nlabel == null) { return this; } else { - return Goto(nlabel); + return new Goto(nlabel); } } @@ -1220,7 +1020,7 @@ public String toString() { * */ public static final class If extends Branching { - private If(Type type, int operand, String target) { + public If(Type type, int operand, String target) { super(target, new Type[] { type }, new int[0], operand); } @@ -1229,7 +1029,7 @@ public If relabel(Map labels) { if (nlabel == null) { return this; } else { - return If(types[0], operands[0], nlabel); + return new If(types[0], operands[0], nlabel); } } @@ -1239,7 +1039,7 @@ public int opcode() { @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return If(types[0], nOperands[0], destination()); + return new If(types[0], nOperands[0], destination()); } public boolean equals(Object o) { @@ -1294,7 +1094,7 @@ public String toString() { * */ public static final class IfIs extends Branching { - private IfIs(Type type, int leftOperand, Type rightOperand, String target) { + public IfIs(Type type, int leftOperand, Type rightOperand, String target) { super(target, new Type[] { type, rightOperand }, new int[0], leftOperand); } @@ -1311,13 +1111,13 @@ public IfIs relabel(Map labels) { if (nlabel == null) { return this; } else { - return IfIs(types[0], operands[0], types[1], nlabel); + return new IfIs(types[0], operands[0], types[1], nlabel); } } @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return IfIs(types[0], nOperands[0], types[1], destination()); + return new IfIs(types[0], nOperands[0], types[1], destination()); } public boolean equals(Object o) { @@ -1361,7 +1161,7 @@ public static final class IndirectInvoke extends Bytecode { * @param operands * Registers holding parameters for the invoked function */ - private IndirectInvoke(Type.FunctionOrMethod type, int[] targets, int operand, int[] operands) { + public IndirectInvoke(Type.FunctionOrMethod type, int[] targets, int operand, int[] operands) { super(new Type.FunctionOrMethod[] { type }, targets, append(operand, operands)); } @@ -1405,7 +1205,7 @@ public Type.FunctionOrMethod type(int i) { @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return IndirectInvoke(type(0), nTargets, nOperands[0], Arrays.copyOfRange(nOperands, 1, nOperands.length)); + return new IndirectInvoke(type(0), nTargets, nOperands[0], Arrays.copyOfRange(nOperands, 1, nOperands.length)); } public boolean equals(Object o) { @@ -1426,7 +1226,7 @@ public String toString() { */ public static class Invariant extends Assert { - private Invariant(int block) { + public Invariant(int block) { super(block); } @@ -1499,7 +1299,7 @@ public Invariant clone() { public static final class Invoke extends Bytecode { public final NameID name; - private Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, NameID name) { + public Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, NameID name) { super(new Type.FunctionOrMethod[] { type }, targets, operands); this.name = name; } @@ -1519,7 +1319,7 @@ public Type.FunctionOrMethod type(int i) { @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return Invoke(type(0), nTargets, nOperands, name); + return new Invoke(type(0), nTargets, nOperands, name); } public boolean equals(Object o) { @@ -1539,7 +1339,7 @@ public String toString() { public static final class Lambda extends Bytecode { public final NameID name; - private Lambda(Type.FunctionOrMethod type, int target, int[] operands, NameID name) { + public Lambda(Type.FunctionOrMethod type, int target, int[] operands, NameID name) { super(type, target, operands); this.name = name; } @@ -1559,7 +1359,7 @@ public Type.FunctionOrMethod type(int i) { @Override public Bytecode clone(int[] nTargets, int[] nOperands) { - return Lambda((Type.FunctionOrMethod) type(0), nTargets[0], nOperands, name); + return new Lambda((Type.FunctionOrMethod) type(0), nTargets[0], nOperands, name); } public boolean equals(Object o) { @@ -1584,7 +1384,7 @@ public String toString() { public static class Label extends Bytecode { public final String label; - private Label(String label) { + public Label(String label) { super(new Type[0], new int[0], new int[0]); this.label = label; } @@ -1598,7 +1398,7 @@ public Label relabel(Map labels) { if (nlabel == null) { return this; } else { - return Label(nlabel); + return new Label(nlabel); } } @@ -1676,7 +1476,7 @@ protected Bytecode clone(int[] nTargets, int[] nOperands) { */ public static class Loop extends Compound { - private Loop(int[] targets, int block, int... operands) { + public Loop(int[] targets, int block, int... operands) { super(block, new Type[0], targets, operands); } @@ -1708,7 +1508,7 @@ public String toString() { public static final class Quantify extends Loop { - private Quantify(int startOperand, int endOperand, int indexOperand, int[] targets, int block) { + public Quantify(int startOperand, int endOperand, int indexOperand, int[] targets, int block) { super(targets, block, startOperand, endOperand, indexOperand); } @@ -1890,7 +1690,7 @@ public static final class Update extends Bytecode implements Iterable { * @param fields * Fields for record updates */ - private Update(Type beforeType, int target, int[] operands, int operand, Type afterType, + public Update(Type beforeType, int target, int[] operands, int operand, Type afterType, Collection fields) { super(new Type[] { beforeType, afterType }, new int[] { target }, append(operands, operand)); if (fields == null) { @@ -2044,7 +1844,11 @@ public String toString() { */ public static final class Return extends Bytecode { - private Return(Type[] types, int... operands) { + public Return() { + super(new Type[0], new int[0], new int[0]); + } + + public Return(Type[] types, int... operands) { super(types, new int[0], operands); } @@ -2124,7 +1928,7 @@ public static final class Switch extends Bytecode { public final ArrayList> branches; public final String defaultTarget; - Switch(Type type, int operand, String defaultTarget, Collection> branches) { + public Switch(Type type, int operand, String defaultTarget, Collection> branches) { super(new Type[] { type }, new int[0], operand); this.branches = new ArrayList>(branches); this.defaultTarget = defaultTarget; @@ -2148,9 +1952,9 @@ public Switch relabel(Map labels) { String nlabel = labels.get(defaultTarget); if (nlabel == null) { - return Switch(types[0], operands[0], defaultTarget, nbranches); + return new Switch(types[0], operands[0], defaultTarget, nbranches); } else { - return Switch(types[0], operands[0], nlabel, nbranches); + return new Switch(types[0], operands[0], nlabel, nbranches); } } diff --git a/modules/wyil/src/wyil/transforms/LoopVariants.java b/modules/wyil/src/wyil/transforms/LoopVariants.java index 93fb16f0f5..b8f9474374 100644 --- a/modules/wyil/src/wyil/transforms/LoopVariants.java +++ b/modules/wyil/src/wyil/transforms/LoopVariants.java @@ -124,12 +124,12 @@ protected BitSet infer(int blockID, CodeForest forest) { // already implied that this is modified. Bytecode.Quantify qc = (Bytecode.Quantify) code; loopModified.clear(qc.indexOperand()); - code = Bytecode.Quantify(qc.startOperand(), qc.endOperand(), qc.indexOperand(), toArray(loopModified), + code = new Bytecode.Quantify(qc.startOperand(), qc.endOperand(), qc.indexOperand(), toArray(loopModified), qc.block()); block.set(i, code, e.attributes()); } else if (code instanceof Bytecode.Loop) { Bytecode.Loop loop = (Bytecode.Loop) code; - code = Bytecode.Loop(toArray(loopModified), loop.block()); + code = new Bytecode.Loop(toArray(loopModified), loop.block()); block.set(i, code, e.attributes()); } diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 89354ea73a..438bf77c91 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -408,10 +408,10 @@ private void patchInvariantBlockHelper(String falseBranch, CodeForest.Block bloc if (c instanceof Return) { // first patch point - block.set(i, Operator(Type.T_VOID, new int[0], new int[0], OperatorKind.ASSIGN)); + block.set(i, new Operator(Type.T_VOID, new int[0], new int[0], OperatorKind.ASSIGN)); } else if (c instanceof Fail) { // second patch point - block.set(i, Goto(falseBranch)); + block.set(i, new Goto(falseBranch)); } } } From 9b9190d9cded76c4e7c1ed8383673a839001ca3f Mon Sep 17 00:00:00 2001 From: DavePearce Date: Sat, 16 Apr 2016 22:40:30 +1200 Subject: [PATCH 25/43] Add explicit Not operator #502 This adds an explicit Operator kind for logical not expressions. This can simplify code generation in some situations. --- .../wyc/src/wyc/builder/CodeGenerator.java | 11 +- .../src/wyil/builders/VcExprGenerator.java | 2 + modules/wyil/src/wyil/io/WyilFileReader.java | 5 + modules/wyil/src/wyil/lang/Bytecode.java | 106 +++++++++--------- .../util/interpreter/StandardFunctions.java | 15 ++- modules/wyjc/src/wyjc/runtime/WyBool.java | 4 + .../src/wyjc/util/BytecodeTranslators.java | 15 +++ 7 files changed, 98 insertions(+), 60 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index b2454fd630..d3bd60e250 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -165,7 +165,6 @@ public WyilFile generate(WhileyFile wf) { * indicate an invalid constant declaration was encountered. */ private WyilFile.Constant generate(WhileyFile.Constant cd) { - // TODO: this the point where were should run an evaluator ? return new WyilFile.Constant(cd.modifiers(), cd.name(), cd.resolvedValue); } @@ -1893,14 +1892,8 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b attributes(expr)); break; case NOT: - String falseLabel = CodeUtils.freshLabel(); - String exitLabel = CodeUtils.freshLabel(); - generateCondition(falseLabel, expr.mhs, environment, block, forest, context); - block.add(new Bytecode.Const(targets[0], Constant.V_BOOL(true)), attributes(expr)); - block.add(new Bytecode.Goto(exitLabel)); - block.add(new Bytecode.Label(falseLabel)); - block.add(new Bytecode.Const(targets[0], Constant.V_BOOL(false)), attributes(expr)); - block.add(new Bytecode.Label(exitLabel)); + block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NOT), + attributes(expr)); break; case ARRAYLENGTH: block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index c944438809..1ddd924a85 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -87,6 +87,7 @@ public void transform(Bytecode code, CodeForest forest, VcBranch branch) { */ private static Expr.Unary.Op[] unaryOperatorMap = { Expr.Unary.Op.NEG, // neg + Expr.Unary.Op.NOT, // not null, // invert null, // deref Expr.Unary.Op.LENGTHOF @@ -97,6 +98,7 @@ public void transform(Bytecode code, CodeForest forest, VcBranch branch) { */ private static Expr.Binary.Op[] binaryOperatorMap = { null, // neg + null, // not null, // invert null, // deref null, // lengthof diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 5a401e54fe..6a507f7361 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -943,6 +943,11 @@ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NEG); } }; + schemas[Bytecode.OPCODE_not] = new Schema(Targets.ONE, Operands.ONE, Types.ONE){ + public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { + return new Bytecode.Operator(types[0], targets, operands, Bytecode.OperatorKind.NOT); + } + }; schemas[Bytecode.OPCODE_fieldload] = new Schema(Targets.ONE, Operands.ONE, Types.ONE, Extras.STRING){ public Bytecode construct(int opcode,int[] targets, int[] operands, Type[] types, Object[] extras) { return new Bytecode.FieldLoad((Type.EffectiveRecord) types[0], targets[0], operands[0], (String) extras[0]); diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java index 75e1c2c22f..9dbf3ecf3a 100755 --- a/modules/wyil/src/wyil/lang/Bytecode.java +++ b/modules/wyil/src/wyil/lang/Bytecode.java @@ -317,128 +317,133 @@ public String toString() { return "neg"; } }, - BITWISEINVERT(1) { + NOT(1) { + public String toString() { + return "not"; + } + }, + BITWISEINVERT(2) { public String toString() { return "invert"; } }, - DEREFERENCE(2) { + DEREFERENCE(3) { public String toString() { return "deref"; } }, - ARRAYLENGTH(3) { + ARRAYLENGTH(4) { public String toString() { return "length"; } }, // Binary - ADD(4) { + ADD(5) { public String toString() { return "add"; } }, - SUB(5) { + SUB(6) { public String toString() { return "sub"; } }, - MUL(6) { + MUL(7) { public String toString() { return "mul"; } }, - DIV(7) { + DIV(8) { public String toString() { return "div"; } }, - REM(8) { + REM(9) { public String toString() { return "rem"; } }, - EQ(9) { + EQ(10) { public String toString() { return "eq"; } }, - NEQ(10) { + NEQ(11) { public String toString() { return "ne"; } }, - LT(11) { + LT(12) { public String toString() { return "lt"; } }, - LTEQ(12) { + LTEQ(13) { public String toString() { return "le"; } }, - GT(13) { + GT(14) { public String toString() { return "gt"; } }, - GTEQ(14) { + GTEQ(15) { public String toString() { return "ge"; } }, - BITWISEOR(15) { + BITWISEOR(16) { public String toString() { return "or"; } }, - BITWISEXOR(16) { + BITWISEXOR(17) { public String toString() { return "xor"; } }, - BITWISEAND(17) { + BITWISEAND(18) { public String toString() { return "and"; } }, - LEFTSHIFT(18) { + LEFTSHIFT(19) { public String toString() { return "shl"; } }, - RIGHTSHIFT(19) { + RIGHTSHIFT(20) { public String toString() { return "shr"; } }, - ARRAYINDEX(20) { + ARRAYINDEX(21) { public String toString() { return "indexof"; } }, - ARRAYGENERATOR(21) { + ARRAYGENERATOR(22) { public String toString() { return "arraygen"; } }, - ARRAYCONSTRUCTOR(22) { + ARRAYCONSTRUCTOR(23) { public String toString() { return "array"; } }, - RECORDCONSTRUCTOR(23) { + RECORDCONSTRUCTOR(24) { public String toString() { return "record"; } }, - NEW(24) { + NEW(25) { public String toString() { return "new"; } }, - ASSIGN(25) { + ASSIGN(26) { public String toString() { return "assign"; } @@ -2038,31 +2043,32 @@ private static int[] append(int operand, int[] operands) { public static final int BINARY_ASSIGNABLE = BINARY_OPERATOR+6; public static final int OPCODE_neg = BINARY_ASSIGNABLE+0; - public static final int OPCODE_arrayinvert = BINARY_ASSIGNABLE+1; - public static final int OPCODE_dereference = BINARY_ASSIGNABLE+2; - public static final int OPCODE_arraylength = BINARY_ASSIGNABLE+3; - public static final int OPCODE_add = BINARY_ASSIGNABLE+4; - public static final int OPCODE_sub = BINARY_ASSIGNABLE+5; - public static final int OPCODE_mul = BINARY_ASSIGNABLE+6; - public static final int OPCODE_div = BINARY_ASSIGNABLE+7; - public static final int OPCODE_rem = BINARY_ASSIGNABLE+8; - public static final int OPCODE_eq = BINARY_ASSIGNABLE+9; - public static final int OPCODE_ne = BINARY_ASSIGNABLE+10; - public static final int OPCODE_lt = BINARY_ASSIGNABLE+11; - public static final int OPCODE_le = BINARY_ASSIGNABLE+12; - public static final int OPCODE_gt = BINARY_ASSIGNABLE+13; - public static final int OPCODE_ge = BINARY_ASSIGNABLE+14; - public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+15; - public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+16; - public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+17; - public static final int OPCODE_lshr = BINARY_ASSIGNABLE+18; - public static final int OPCODE_rshr = BINARY_ASSIGNABLE+19; - public static final int OPCODE_arrayindex = BINARY_ASSIGNABLE+20; - public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+21; - public static final int OPCODE_array = BINARY_ASSIGNABLE+22; - public static final int OPCODE_record = BINARY_ASSIGNABLE+23; - public static final int OPCODE_newobject = BINARY_ASSIGNABLE+24; - public static final int OPCODE_assign = BINARY_ASSIGNABLE+25; + public static final int OPCODE_not = BINARY_ASSIGNABLE+1; + public static final int OPCODE_arrayinvert = BINARY_ASSIGNABLE+2; + public static final int OPCODE_dereference = BINARY_ASSIGNABLE+3; + public static final int OPCODE_arraylength = BINARY_ASSIGNABLE+4; + public static final int OPCODE_add = BINARY_ASSIGNABLE+5; + public static final int OPCODE_sub = BINARY_ASSIGNABLE+6; + public static final int OPCODE_mul = BINARY_ASSIGNABLE+7; + public static final int OPCODE_div = BINARY_ASSIGNABLE+8; + public static final int OPCODE_rem = BINARY_ASSIGNABLE+9; + public static final int OPCODE_eq = BINARY_ASSIGNABLE+10; + public static final int OPCODE_ne = BINARY_ASSIGNABLE+11; + public static final int OPCODE_lt = BINARY_ASSIGNABLE+12; + public static final int OPCODE_le = BINARY_ASSIGNABLE+13; + public static final int OPCODE_gt = BINARY_ASSIGNABLE+14; + public static final int OPCODE_ge = BINARY_ASSIGNABLE+15; + public static final int OPCODE_bitwiseor = BINARY_ASSIGNABLE+16; + public static final int OPCODE_bitwisexor = BINARY_ASSIGNABLE+17; + public static final int OPCODE_bitwiseand = BINARY_ASSIGNABLE+18; + public static final int OPCODE_lshr = BINARY_ASSIGNABLE+19; + public static final int OPCODE_rshr = BINARY_ASSIGNABLE+20; + public static final int OPCODE_arrayindex = BINARY_ASSIGNABLE+21; + public static final int OPCODE_arrygen = BINARY_ASSIGNABLE+22; + public static final int OPCODE_array = BINARY_ASSIGNABLE+23; + public static final int OPCODE_record = BINARY_ASSIGNABLE+24; + public static final int OPCODE_newobject = BINARY_ASSIGNABLE+25; + public static final int OPCODE_assign = BINARY_ASSIGNABLE+26; // Nary Assignables public static final int NARY_ASSIGNABLE = BINARY_ASSIGNABLE+26; diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index 520f5f2634..1ddf360dfe 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -22,6 +22,7 @@ public class StandardFunctions { static { standardFunctions[Bytecode.OPCODE_assign] = new Assign(); standardFunctions[Bytecode.OPCODE_neg] = new Negate(); + standardFunctions[Bytecode.OPCODE_not] = new Not(); standardFunctions[Bytecode.OPCODE_arrayinvert] = new BitwiseInvert(); standardFunctions[Bytecode.OPCODE_dereference] = new Dereference(); standardFunctions[Bytecode.OPCODE_arraylength] = new ArrayLength(); @@ -81,6 +82,18 @@ public Constant apply(Constant[] operands, Context context) { } + // ==================================================================================== + // Boolean + // ==================================================================================== + + private static final class Not implements InternalFunction { + @Override + public Constant apply(Constant[] operands, Context context) { + Constant.Bool i = checkType(operands[0], context, Constant.Bool.class); + return Constant.V_BOOL(!i.value); + } + } + // ==================================================================================== // Arithmetic // ==================================================================================== @@ -92,7 +105,7 @@ public Constant apply(Constant[] operands, Context context) { return i.negate(); } } - + private static final class Add implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { diff --git a/modules/wyjc/src/wyjc/runtime/WyBool.java b/modules/wyjc/src/wyjc/runtime/WyBool.java index 54302f067e..b541f33ba1 100644 --- a/modules/wyjc/src/wyjc/runtime/WyBool.java +++ b/modules/wyjc/src/wyjc/runtime/WyBool.java @@ -11,6 +11,10 @@ public boolean value() { return value; } + public WyBool not() { + return new WyBool(!value); + } + public boolean equals(Object o) { if (o instanceof WyBool) { WyBool b = (WyBool) o; diff --git a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java index 9e2f469a8f..fa6ce8c3a8 100644 --- a/modules/wyjc/src/wyjc/util/BytecodeTranslators.java +++ b/modules/wyjc/src/wyjc/util/BytecodeTranslators.java @@ -28,6 +28,7 @@ public class BytecodeTranslators { static { standardFunctions[OPCODE_assign] = new Assign(); standardFunctions[OPCODE_neg] = new Negate(); + standardFunctions[OPCODE_not] = new Not(); standardFunctions[OPCODE_arrayinvert] = new Invert(); standardFunctions[OPCODE_dereference] = new Dereference(); standardFunctions[OPCODE_arraylength] = new ArrayLength(); @@ -106,6 +107,20 @@ public void translate(Operator bytecode, Context context) { } } // ==================================================================================== + // Boolean + // ==================================================================================== + + private static final class Not implements BytecodeTranslator { + @Override + public void translate(Operator bytecode, Context context) { + JvmType.Clazz type = (JvmType.Clazz) context.toJvmType(bytecode.type(0)); + JvmType.Function ftype = new JvmType.Function(type); + context.add(new Bytecode.Load(bytecode.operand(0), type)); + context.add(new Bytecode.Invoke(type, "not", ftype, Bytecode.InvokeMode.VIRTUAL)); + context.add(new Bytecode.Store(bytecode.target(0), type)); + } + } + // ==================================================================================== // Arithmetic // ==================================================================================== From f3063e57d1ffe62f167f87c65299158fffa1d149 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Mon, 18 Apr 2016 18:09:16 +1200 Subject: [PATCH 26/43] WyIL: Encapsulate fields & remove remap/registers Several publis fields in the Bytecode subclasses have been encapsulated to try and make things more consistent. Also, removed the remap method as this is no longer used, and likewise for the registers() method --- .../src/wyil/builders/VcExprGenerator.java | 20 +- modules/wyil/src/wyil/builders/VcUtils.java | 6 +- modules/wyil/src/wyil/io/WyilFileReader.java | 4 +- modules/wyil/src/wyil/io/WyilFileWriter.java | 18 +- modules/wyil/src/wyil/lang/Bytecode.java | 198 ++++-------------- modules/wyil/src/wyil/lang/CodeUtils.java | 2 +- .../wyil/util/dfa/BackwardFlowAnalysis.java | 2 +- .../wyil/util/dfa/ForwardFlowAnalysis.java | 2 +- .../wyil/util/interpreter/Interpreter.java | 8 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 12 +- 10 files changed, 74 insertions(+), 198 deletions(-) diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index 1ddd924a85..4226904c0d 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -121,7 +121,7 @@ public void transform(Bytecode code, CodeForest forest, VcBranch branch) { }; protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch branch) { - switch(code.kind) { + switch(code.kind()) { case ASSIGN: for (int i = 0; i != code.operands().length; ++i) { branch.write(code.target(i), branch.read(code.operand(i))); @@ -130,7 +130,7 @@ protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch bra case NEG: case ARRAYLENGTH: { Bytecode.Operator bc = (Bytecode.Operator) code; - transformUnary(unaryOperatorMap[code.kind.ordinal()], bc, branch, forest); + transformUnary(unaryOperatorMap[code.kind().ordinal()], bc, branch, forest); break; } case BITWISEINVERT: @@ -149,7 +149,7 @@ protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch bra case LTEQ: case GT: case GTEQ: { - transformBinary(binaryOperatorMap[code.kind.ordinal()], code, branch, forest); + transformBinary(binaryOperatorMap[code.kind().ordinal()], code, branch, forest); break; } case BITWISEAND: @@ -190,7 +190,7 @@ protected void transform(Bytecode.Convert code, CodeForest forest, VcBranch bran } protected void transform(Bytecode.Const code, CodeForest forest, VcBranch branch) { - Value val = utils.convert(code.constant, forest, branch); + Value val = utils.convert(code.constant(), forest, branch); branch.write(code.target(), new Expr.Constant(val, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes()))); } @@ -204,7 +204,7 @@ protected void transform(Bytecode.FieldLoad code, CodeForest forest, VcBranch br ArrayList fields = new ArrayList(er.fields().keySet()); Collections.sort(fields); Expr src = branch.read(code.operand(0)); - Expr index = new Expr.Constant(Value.Integer(BigInteger.valueOf(fields.indexOf(code.field)))); + Expr index = new Expr.Constant(Value.Integer(BigInteger.valueOf(fields.indexOf(code.fieldName())))); Expr result = new Expr.IndexOf(src, index, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())); branch.write(code.target(0), result); } @@ -231,8 +231,8 @@ protected void transform(Bytecode.Invoke code, CodeForest forest, } Expr argument = operands.length == 1 ? operands[0] : new Expr.Nary( Expr.Nary.Op.TUPLE, operands,wyccAttributes); - branch.write(code.targets()[0], new Expr.Invoke(code.name.name(), - code.name.module(), Collections.EMPTY_LIST, argument, + branch.write(code.targets()[0], new Expr.Invoke(code.name().name(), + code.name().module(), Collections.EMPTY_LIST, argument, wyccAttributes)); // This is a potential fix for #488, although it doesn't work @@ -247,7 +247,7 @@ protected void transform(Bytecode.Invoke code, CodeForest forest, // Here, we must find the name of the corresponding postcondition so // that we can assume it. - int numPostconditions = countPostconditions(code.name, code.type(0), forest, branch); + int numPostconditions = countPostconditions(code.name(), code.type(0), forest, branch); if (numPostconditions > 0) { // To assume the post-condition holds after the method, we @@ -257,10 +257,10 @@ protected void transform(Bytecode.Invoke code, CodeForest forest, for(int i=0;i!=targets.length;++i) { arguments[operands.length+i] = branch.read(targets[i]); } - String prefix = code.name.name() + "_ensures_"; + String prefix = code.name().name() + "_ensures_"; for (int i = 0; i != numPostconditions; ++i) { Expr.Invoke macro = new Expr.Invoke(prefix + i, - code.name.module(), Collections.EMPTY_LIST, + code.name().module(), Collections.EMPTY_LIST, new Expr.Nary(Expr.Nary.Op.TUPLE, arguments)); branch.assume(macro); } diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java index 176d218af0..ba091688d4 100644 --- a/modules/wyil/src/wyil/builders/VcUtils.java +++ b/modules/wyil/src/wyil/builders/VcUtils.java @@ -493,7 +493,7 @@ public Pair[] preconditionCheck(Bytecode.Invoke code, VcBranch bran } } // - int numPreconditions = countPreconditions(code.name, code.type(0), forest, branch); + int numPreconditions = countPreconditions(code.name(), code.type(0), forest, branch); // if (numPreconditions > 0) { // First, read out the operands from the branch @@ -503,11 +503,11 @@ public Pair[] preconditionCheck(Bytecode.Invoke code, VcBranch bran } // To check the pre-condition holds after the method, we // simply called the corresponding pre-condition macros. - String prefix = code.name.name() + "_requires_"; + String prefix = code.name().name() + "_requires_"; Expr argument = operands.length == 1 ? operands[0] : new Expr.Nary(Expr.Nary.Op.TUPLE, operands); for (int i = 0; i < numPreconditions; ++i) { - Expr precondition = new Expr.Invoke(prefix + i, code.name.module(), Collections.EMPTY_LIST, argument); + Expr precondition = new Expr.Invoke(prefix + i, code.name().module(), Collections.EMPTY_LIST, argument); preconditions.add(new Pair("precondition not satisfied", precondition)); } diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java index 6a507f7361..529af9dbb0 100644 --- a/modules/wyil/src/wyil/io/WyilFileReader.java +++ b/modules/wyil/src/wyil/io/WyilFileReader.java @@ -788,7 +788,7 @@ private Object[] readExtras(Bytecode.Schema schema, HashMap(constantPool[constIdx],label); } results[i] = pairs; diff --git a/modules/wyil/src/wyil/io/WyilFileWriter.java b/modules/wyil/src/wyil/io/WyilFileWriter.java index c8448a4325..5eb2e61fb8 100644 --- a/modules/wyil/src/wyil/io/WyilFileWriter.java +++ b/modules/wyil/src/wyil/io/WyilFileWriter.java @@ -597,7 +597,7 @@ private HashMap buildLabelsMap(CodeForest forest) { Bytecode code = block.get(j).code(); if (code instanceof Bytecode.Label) { Bytecode.Label l = (Bytecode.Label) code; - labels.put(l.label, offset-1); + labels.put(l.label(), offset-1); } else { offset = offset + 1; } @@ -811,16 +811,16 @@ private void writeRest(Bytecode code, int offset, HashMap label output.write_uv(destination); } else if (code instanceof Bytecode.Const) { Bytecode.Const c = (Bytecode.Const) code; - output.write_uv(constantCache.get(c.constant)); + output.write_uv(constantCache.get(c.constant())); } else if (code instanceof Bytecode.FieldLoad) { Bytecode.FieldLoad c = (Bytecode.FieldLoad) code; - output.write_uv(stringCache.get(c.field)); + output.write_uv(stringCache.get(c.fieldName())); } else if (code instanceof Bytecode.Invoke) { Bytecode.Invoke c = (Bytecode.Invoke) code; - output.write_uv(nameCache.get(c.name)); + output.write_uv(nameCache.get(c.name())); } else if (code instanceof Bytecode.Lambda) { Bytecode.Lambda c = (Bytecode.Lambda) code; - output.write_uv(nameCache.get(c.name)); + output.write_uv(nameCache.get(c.name())); } else if (code instanceof Bytecode.Update) { Bytecode.Update c = (Bytecode.Update) code; List fields = c.fields; @@ -956,16 +956,16 @@ private void buildPools(Bytecode code) { // First, deal with special cases if (code instanceof Bytecode.Const) { Bytecode.Const c = (Bytecode.Const) code; - addConstantItem(c.constant); + addConstantItem(c.constant()); } else if (code instanceof Bytecode.FieldLoad) { Bytecode.FieldLoad c = (Bytecode.FieldLoad) code; - addStringItem(c.field); + addStringItem(c.fieldName()); }else if (code instanceof Bytecode.Invoke) { Bytecode.Invoke c = (Bytecode.Invoke) code; - addNameItem(c.name); + addNameItem(c.name()); } else if (code instanceof Bytecode.Lambda) { Bytecode.Lambda c = (Bytecode.Lambda) code; - addNameItem(c.name); + addNameItem(c.name()); } else if (code instanceof Bytecode.Update) { Bytecode.Update c = (Bytecode.Update) code; for (Bytecode.LVal l : c) { diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java index 9dbf3ecf3a..c5ce406368 100755 --- a/modules/wyil/src/wyil/lang/Bytecode.java +++ b/modules/wyil/src/wyil/lang/Bytecode.java @@ -47,7 +47,7 @@ * tests. The following illustrates: * *

- * function sum([int] data) -> int:
+ * function sum(int[] data) -> int:
  *    int r = 0
  *    for item in data:
  *       r = r + item
@@ -57,7 +57,7 @@
  * This function is compiled into the following WyIL bytecode:
  *
  * 
- * function sum([int] data) -> int:
+ * function sum(int[] data) -> int:
  * body:
  *   const %1 = 0          : int
  *   assign %2 = %0        : [int]
@@ -128,43 +128,6 @@ public Bytecode(Type[] types, int[] targets, int... operands) {
 		this.operands = operands;
 	}
 
-	/**
-	 * Determine which registers are used in this bytecode. This can be used,
-	 * for example, to determine the size of the register file required for a
-	 * given method.
-	 *
-	 * @param register
-	 */
-	public void registers(java.util.Set registers) {
-		for (int i = 0; i != targets().length; ++i) {
-			registers.add(targets()[i]);
-		}
-		for (int i = 0; i != operands().length; ++i) {
-			registers.add(operands[i]);
-		}
-	}
-
-
-	/**
-	 * Remaps all registers according to a given binding. Registers not
-	 * mentioned in the binding retain their original value. Note, if the
-	 * returned bytecode is unchanged then it must be this.
-	 *
-	 * @param binding
-	 *            --- map from (existing) registers to (new) registers.
-	 * @return
-	 */
-	public Bytecode remap(Map binding) {
-		int[] nTargets = remapOperands(binding, targets());
-		int[] nOperands = remapOperands(binding, operands());
-		if (nTargets != targets() || nOperands != operands()) {
-			return clone(nTargets, nOperands);
-		}
-		return this;
-	}
-
-	protected abstract Bytecode clone(int[] nTargets, int[] nOperands);
-	
 	@Override
 	public int hashCode() {
 		return Arrays.hashCode(types) + Arrays.hashCode(targets()) + Arrays.hashCode(operands());
@@ -498,7 +461,7 @@ private OperatorKind(int offset) {
 	 *
 	 */
 	public static final class Operator extends Bytecode {
-		public final OperatorKind kind;
+		private final OperatorKind kind;
 
 		public Operator(Type type, int[] targets, int[] operands, OperatorKind bop) {
 			super(new Type[] { type }, targets, operands);
@@ -510,12 +473,7 @@ public Operator(Type type, int[] targets, int[] operands, OperatorKind bop) {
 
 		@Override
 		public int opcode() {
-			return OPCODE_neg + kind.offset;
-		}
-
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Operator(type(0), nTargets, nOperands, kind);
+			return OPCODE_neg + kind().offset;
 		}
 
 		public int hashCode() {
@@ -531,7 +489,11 @@ public boolean equals(Object o) {
 		}
 
 		public String toString() {
-			return kind + " %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0);
+			return kind() + " %" + target(0) + " = " + arrayToString(operands()) + " : " + type(0);
+		}
+
+		public OperatorKind kind() {
+			return kind;
 		}
 	}
 
@@ -581,10 +543,6 @@ public Convert(Type from, int target, int operand, Type result) {
 			super(new Type[] { from, result }, new int[] { target }, operand);
 		}
 
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Convert(type(0), nTargets[0], nOperands[0], type(1));
-		}
-
 		public Type result() {
 			return type(1);
 		}
@@ -639,7 +597,7 @@ public String toString() {
 	 *
 	 */
 	public static final class Const extends Bytecode {
-		public final Constant constant;
+		private final Constant constant;
 
 		public Const(int target, Constant constant) {
 			super(new Type[0], new int[] { target }, new int[0]);
@@ -653,6 +611,10 @@ public int opcode() {
 		public int target() {
 			return targets()[0];
 		}
+		
+		public Constant constant() {
+			return constant;
+		}
 
 		public int hashCode() {
 			return constant.hashCode() + targets()[0];
@@ -669,11 +631,6 @@ public boolean equals(Object o) {
 		public String toString() {
 			return "const %" + targets()[0] + " = " + constant + " : " + constant.type();
 		}
-
-		@Override
-		protected Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Const(nTargets[0], constant);
-		}
 	}
 
 	/**
@@ -716,11 +673,6 @@ public int opcode() {
 			return OPCODE_debug;
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Debug(nOperands[0]);
-		}
-
 		public boolean equals(Object o) {
 			return o instanceof Debug && super.equals(o);
 		}
@@ -766,11 +718,6 @@ public String toString() {
 		public boolean equals(Object o) {
 			return o instanceof Assume && super.equals(o);
 		}
-
-		@Override
-		protected Bytecode clone(int[] nTargets, int[] nOperands) {
-			return this;
-		}
 	}
 
 	/**
@@ -796,11 +743,6 @@ public String toString() {
 		public boolean equals(Object o) {
 			return o instanceof Assume && super.equals(o);
 		}
-
-		@Override
-		protected Bytecode clone(int[] nTargets, int[] nOperands) {
-			return this;
-		}
 	}
 
 	/**
@@ -821,15 +763,9 @@ public int opcode() {
 			return OPCODE_fail;
 		}
 
-		@Override
-		protected Bytecode clone(int[] nTargets, int[] nOperands) {
-			return this;
-		}
-
 		public String toString() {
 			return "fail";
 		}
-
 	}
 
 	/**
@@ -859,7 +795,7 @@ public String toString() {
 	 *
 	 */
 	public static final class FieldLoad extends Bytecode {
-		public final String field;
+		private final String field;
 
 		public FieldLoad(Type.EffectiveRecord type, int target, int operand, String field) {
 			super((Type) type, target, operand);
@@ -869,11 +805,6 @@ public FieldLoad(Type.EffectiveRecord type, int target, int operand, String fiel
 			this.field = field;
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new FieldLoad((Type.EffectiveRecord) type(0), nTargets[0], nOperands[0], field);
-		}
-
 		public int opcode() {
 			return OPCODE_fieldload;
 		}
@@ -887,6 +818,10 @@ public Type fieldType() {
 			return er.fields().get(field);
 		}
 
+		public String fieldName() {
+			return field;
+		}
+		
 		public boolean equals(Object o) {
 			if (o instanceof FieldLoad) {
 				FieldLoad i = (FieldLoad) o;
@@ -963,11 +898,6 @@ public boolean equals(Object o) {
 			return o instanceof Goto && super.equals(o);
 		}
 
-		@Override
-		protected Bytecode clone(int[] nTargets, int[] nOperands) {
-			return this;
-		}
-
 		public String toString() {
 			return "goto " + destination();
 		}
@@ -1042,11 +972,6 @@ public int opcode() {
 			return OPCODE_if;
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new If(types[0], nOperands[0], destination());
-		}
-
 		public boolean equals(Object o) {
 			return o instanceof If && super.equals(o);			
 		}
@@ -1120,11 +1045,6 @@ public IfIs relabel(Map labels) {
 			}
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new IfIs(types[0], nOperands[0], types[1], destination());
-		}
-
 		public boolean equals(Object o) {
 			return o instanceof IfIs && super.equals(o);
 		}
@@ -1208,11 +1128,6 @@ public Type.FunctionOrMethod type(int i) {
 			return (Type.FunctionOrMethod) super.type(i);
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new IndirectInvoke(type(0), nTargets, nOperands[0], Arrays.copyOfRange(nOperands, 1, nOperands.length));
-		}
-
 		public boolean equals(Object o) {
 			return o instanceof IndirectInvoke && super.equals(o);
 		}
@@ -1254,11 +1169,6 @@ public boolean equals(Object o) {
 			}
 			return false;
 		}
-
-		@Override
-		public Invariant clone() {
-			return this;
-		}
 	}
 
 	/**
@@ -1302,7 +1212,7 @@ public Invariant clone() {
 	 *
 	 */
 	public static final class Invoke extends Bytecode {
-		public final NameID name;
+		private final NameID name;
 
 		public Invoke(Type.FunctionOrMethod type, int[] targets, int[] operands, NameID name) {
 			super(new Type.FunctionOrMethod[] { type }, targets, operands);
@@ -1313,6 +1223,10 @@ public int opcode() {
 			return OPCODE_invoke;
 		}
 
+		public NameID name() {
+			return name;
+		}
+		
 		public int hashCode() {
 			return name.hashCode() + super.hashCode();
 		}
@@ -1322,15 +1236,10 @@ public Type.FunctionOrMethod type(int i) {
 			return (Type.FunctionOrMethod) super.type(i);
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Invoke(type(0), nTargets, nOperands, name);
-		}
-
 		public boolean equals(Object o) {
 			if (o instanceof Invoke) {
 				Invoke i = (Invoke) o;
-				return name.equals(i.name) && super.equals(i);
+				return name().equals(i.name) && super.equals(i);
 			}
 			return false;
 		}
@@ -1342,7 +1251,7 @@ public String toString() {
 	}
 
 	public static final class Lambda extends Bytecode {
-		public final NameID name;
+		private final NameID name;
 
 		public Lambda(Type.FunctionOrMethod type, int target, int[] operands, NameID name) {
 			super(type, target, operands);
@@ -1353,6 +1262,10 @@ public int opcode() {
 			return OPCODE_lambda;
 		}
 
+		public NameID name() {
+			return name;
+		}
+		
 		public int hashCode() {
 			return name.hashCode() + super.hashCode();
 		}
@@ -1362,15 +1275,10 @@ public Type.FunctionOrMethod type(int i) {
 			return (Type.FunctionOrMethod) super.type(i);
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Lambda((Type.FunctionOrMethod) type(0), nTargets[0], nOperands, name);
-		}
-
 		public boolean equals(Object o) {
 			if (o instanceof Lambda) {
 				Lambda i = (Lambda) o;
-				return name.equals(i.name) && super.equals(i);
+				return name().equals(i.name()) && super.equals(i);
 			}
 			return false;
 		}
@@ -1387,7 +1295,7 @@ public String toString() {
 	 *
 	 */
 	public static class Label extends Bytecode {
-		public final String label;
+		private final String label;
 
 		public Label(String label) {
 			super(new Type[0], new int[0], new int[0]);
@@ -1398,6 +1306,10 @@ public int opcode() {
 			return -1;
 		}
 
+		public String label() {
+			return label;
+		}
+		
 		public Label relabel(Map labels) {
 			String nlabel = labels.get(label);
 			if (nlabel == null) {
@@ -1407,13 +1319,8 @@ public Label relabel(Map labels) {
 			}
 		}
 
-		@Override
-		public Bytecode remap(Map binding) {
-			return this;
-		}
-
 		public int hashCode() {
-			return label.hashCode();
+			return label().hashCode();
 		}
 
 		public boolean equals(Object o) {
@@ -1426,16 +1333,6 @@ public boolean equals(Object o) {
 		public String toString() {
 			return "." + label;
 		}
-
-		@Override
-		public void registers(Set register) {
-			// TODO Auto-generated method stub
-		}
-
-		@Override
-		protected Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Label(label);
-		}
 	}
 
 	/**
@@ -1501,11 +1398,6 @@ public int[] modifiedOperands() {
 			return targets();
 		}
 
-		@Override
-		public Loop clone(int[] nTargets, int[] nOperands) {
-			return new Loop(nTargets, block, nOperands);
-		}
-
 		public String toString() {
 			return "loop " + arrayToString(targets()) + " = " + block;
 		}
@@ -1790,11 +1682,6 @@ public Type rhs() {
 			return iter;
 		}
 
-		@Override
-		public final Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Update(types, nTargets, nOperands, fields);
-		}
-
 		public boolean equals(Object o) {
 			if (o instanceof Update) {
 				Update i = (Update) o;
@@ -1862,11 +1749,6 @@ public int opcode() {
 			return OPCODE_return;
 		}
 
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Return(Arrays.copyOf(types, types.length), nOperands);
-		}
-
 		public boolean equals(Object o) {
 			if (o instanceof Return) {
 				return super.equals(o);
@@ -1985,12 +1867,6 @@ public String toString() {
 			table += ", *->" + defaultTarget;
 			return "switch %" + operands[0] + " " + table;
 		}
-
-		@Override
-		public Bytecode clone(int[] nTargets, int[] nOperands) {
-			return new Switch(types[0], nOperands[0], defaultTarget, branches);
-		}
-
 	}
 
 	// =============================================================
diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java
index 287e8639e9..5fc72e2ce3 100644
--- a/modules/wyil/src/wyil/lang/CodeUtils.java
+++ b/modules/wyil/src/wyil/lang/CodeUtils.java
@@ -64,7 +64,7 @@ public static Map buildLabelMap(CodeForest forest) {
 				if (code instanceof Bytecode.Label) {
 					// Found a label, so register it in the labels map
 					Bytecode.Label label = (Bytecode.Label) code;
-					labels.put(label.label, new CodeForest.Index(i, j));
+					labels.put(label.label(), new CodeForest.Index(i, j));
 				}
 			}
 		}
diff --git a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java
index 5d6c99b6d8..61d4e27909 100755
--- a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java
+++ b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java
@@ -133,7 +133,7 @@ protected T propagate(int blockID, T store, List> handlers) {
 					continue;
 				} else if (code instanceof Bytecode.Label) {
 					Bytecode.Label l = (Bytecode.Label) code;
-					stores.put(l.label,store);
+					stores.put(l.label(),store);
 				} else if (code instanceof Bytecode.If) {
 					Bytecode.If ifgoto = (Bytecode.If) code;
 					T trueStore = stores.get(ifgoto.destination());
diff --git a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java
index b622bdd99d..110439fcb2 100755
--- a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java
+++ b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java
@@ -128,7 +128,7 @@ protected T propagate(int blockID, T store) {
 				// First, check for a label which may have incoming information.
 				if (code instanceof Bytecode.Label) {
 					Bytecode.Label l = (Bytecode.Label) code;
-					T tmp = stores.get(l.label);
+					T tmp = stores.get(l.label());
 					if (tmp != null && store != null) {
 						store = join(store, tmp);
 					} else if (tmp != null) {
diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java
index 11c7dc9c66..471279373e 100644
--- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java
+++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java
@@ -265,7 +265,7 @@ private Object execute(Bytecode.Operator bytecode, Constant[] frame, Context con
 	 * @return
 	 */
 	private Object execute(Bytecode.Const bytecode, Constant[] frame, Context context) {
-		frame[bytecode.target()] = bytecode.constant;
+		frame[bytecode.target()] = bytecode.constant();
 		return context.pc.next();
 	}
 
@@ -440,7 +440,7 @@ private Object execute(Bytecode.Fail bytecode, Constant[] frame, Context context
 
 	private Object execute(Bytecode.FieldLoad bytecode, Constant[] frame, Context context) {
 		Constant.Record rec = (Constant.Record) frame[bytecode.operand(0)];
-		frame[bytecode.target(0)] = rec.values.get(bytecode.field);
+		frame[bytecode.target(0)] = rec.values.get(bytecode.fieldName());
 		return context.pc.next();
 	}
 
@@ -685,7 +685,7 @@ private Object execute(Bytecode.Invoke bytecode, Constant[] frame, Context conte
 		for (int i = 0; i != arguments.length; ++i) {
 			arguments[i] = frame[operands[i]];
 		}
-		Constant[] results = execute(bytecode.name, bytecode.type(0), arguments);
+		Constant[] results = execute(bytecode.name(), bytecode.type(0), arguments);
 		int[] targets = bytecode.targets();
 		for (int i = 0; i != targets.length; ++i) {
 			frame[targets[i]] = results[i];
@@ -703,7 +703,7 @@ private Object execute(Bytecode.Lambda bytecode, Constant[] frame, Context conte
 			arguments[i] = frame[reg];
 		}
 		// FIXME: need to do something with the operands here.
-		frame[bytecode.target(0)] = Constant.V_LAMBDA(bytecode.name, bytecode.type(0), arguments);
+		frame[bytecode.target(0)] = Constant.V_LAMBDA(bytecode.name(), bytecode.type(0), arguments);
 		//
 		return context.pc.next();
 	}
diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java
index 438bf77c91..5d7f9ef6c1 100755
--- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java
+++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java
@@ -657,7 +657,7 @@ private void translate(CodeForest.Index index, AssertOrAssume c,
 
 	private void translate(CodeForest.Index index, Const c, int freeSlot,
 			CodeForest forest, ArrayList bytecodes) {
-		Constant constant = c.constant;
+		Constant constant = c.constant();
 		JvmType jt = convertUnderlyingType(constant.type());
 
 		if (constant instanceof Constant.Bool
@@ -1138,7 +1138,7 @@ private void translate(CodeForest.Index index, Goto c, int freeSlot, CodeForest
 
 	private void translate(CodeForest.Index index, Label c, int freeSlot,
 			CodeForest forest, ArrayList bytecodes) {
-		bytecodes.add(new Bytecode.Label(c.label));
+		bytecodes.add(new Bytecode.Label(c.label()));
 	}
 
 	private void translate(CodeForest.Index index, Debug c, int freeSlot,
@@ -1159,7 +1159,7 @@ private void translate(CodeForest.Index index, Fail c, int freeSlot, CodeForest
 
 	private void translate(CodeForest.Index index, FieldLoad c, int freeSlot, CodeForest forest, ArrayList bytecodes) {
 		bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYRECORD));
-		bytecodes.add(new Bytecode.LoadConst(c.field));
+		bytecodes.add(new Bytecode.LoadConst(c.fieldName()));
 		JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYRECORD, JAVA_LANG_STRING);
 		bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "get", ftype, Bytecode.InvokeMode.STATIC));
 		addReadConversion(c.fieldType(), bytecodes);
@@ -1179,7 +1179,7 @@ private void translate(CodeForest.Index index, Lambda c, int freeSlot, CodeFores
 		// or method. This class will extend class wyjc.runtime.WyLambda.
 		Type.FunctionOrMethod lamType = (Type.FunctionOrMethod) c.type(0); 
 		int lambda_id = lambdas.size();
-		lambdas.add(buildLambda(c.name, lamType, lambda_id));
+		lambdas.add(buildLambda(c.name(), lamType, lambda_id));
 
 		// Second, create and duplicate new lambda object. This will then stay
 		// on the stack (whilst the parameters are constructed) until the
@@ -1240,8 +1240,8 @@ private void translate(CodeForest.Index index, Invoke c, int freeSlot, CodeFores
 			bytecodes.add(new Bytecode.Load(register, parameterType));
 		}
 
-		Path.ID mid = c.name.module();
-		String mangled = nameMangle(c.name.name(), c.type(0));
+		Path.ID mid = c.name().module();
+		String mangled = nameMangle(c.name().name(), c.type(0));
 		JvmType.Clazz owner = new JvmType.Clazz(mid.parent().toString().replace('/', '.'), mid.last());
 		JvmType.Function type = convertFunType(c.type(0));
 		bytecodes.add(new Bytecode.Invoke(owner, mangled, type, Bytecode.InvokeMode.STATIC));

From 9da6c2fff76a482d18e4d4a3fc3d741360f2f26d Mon Sep 17 00:00:00 2001
From: DavePearce 
Date: Mon, 18 Apr 2016 18:26:05 +1200
Subject: [PATCH 27/43] WyIL: Remove CodeUtils

Some of the stuff in CodeUtils was deadcode.  A few other bits are
related to labels and will become deadcode.  For now I've those things
into specific classes that use them and will remove when labels are
gone.
---
 .../wyc/src/wyc/builder/CodeGenerator.java    | 48 ++++++------
 .../wyil/src/wyil/builders/VcGenerator.java   |  2 +-
 modules/wyil/src/wyil/lang/Bytecode.java      | 39 +++++++++-
 modules/wyil/src/wyil/lang/CodeUtils.java     | 73 -------------------
 .../wyil/util/interpreter/Interpreter.java    |  2 +-
 5 files changed, 66 insertions(+), 98 deletions(-)
 delete mode 100644 modules/wyil/src/wyil/lang/CodeUtils.java

diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java
index d3bd60e250..e9a72b61c7 100644
--- a/modules/wyc/src/wyc/builder/CodeGenerator.java
+++ b/modules/wyc/src/wyc/builder/CodeGenerator.java
@@ -291,7 +291,7 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw
 	private int generateInvariantBlock(Expr invariant, Environment environment, CodeForest forest, Context context) {
 		CodeForest.Block precondition = new CodeForest.Block();
 		int index = forest.add(precondition);
-		String endLab = CodeUtils.freshLabel();
+		String endLab = freshLabel();
 		generateCondition(endLab, invariant, environment, precondition, forest, context);
 		precondition.add(new Bytecode.Fail(), attributes(invariant));
 		precondition.add(new Bytecode.Label(endLab));
@@ -638,7 +638,7 @@ private void generate(Stmt.Assert s, Environment environment, CodeForest.Block b
 		// First, create assert block body
 		CodeForest.Block subblock = new CodeForest.Block();
 		int body = forest.add(subblock);
-		String endLab = CodeUtils.freshLabel();
+		String endLab = freshLabel();
 		generateCondition(endLab, s.expr, environment, subblock, forest, context);
 		subblock.add(new Bytecode.Fail(), attributes(s.expr));
 		subblock.add(new Bytecode.Label(endLab));
@@ -667,7 +667,7 @@ private void generate(Stmt.Assume s, Environment environment, CodeForest.Block b
 		// First, create assume block body
 		CodeForest.Block subblock = new CodeForest.Block();
 		int body = forest.add(subblock);
-		String endLab = CodeUtils.freshLabel();
+		String endLab = freshLabel();
 		generateCondition(endLab, s.expr, environment, subblock, forest, context);
 		subblock.add(new Bytecode.Fail(), attributes(s.expr));
 		subblock.add(new Bytecode.Label(endLab));
@@ -871,8 +871,8 @@ private void generate(Stmt.Fail s, Environment environment, CodeForest.Block blo
 	private void generate(Stmt.IfElse s, Environment environment, CodeForest.Block block, CodeForest forest,
 			Context context) {
 
-		String falseLab = CodeUtils.freshLabel();
-		String exitLab = s.falseBranch.isEmpty() ? falseLab : CodeUtils.freshLabel();
+		String falseLab = freshLabel();
+		String exitLab = s.falseBranch.isEmpty() ? falseLab : freshLabel();
 
 		generateCondition(falseLab, invert(s.condition), environment, block, forest, context);
 
@@ -1057,7 +1057,7 @@ private void generate(Stmt.Continue s, Environment environment, CodeForest.Block
 	 */
 	private void generate(Stmt.Switch s, Environment environment, CodeForest.Block block, CodeForest forest,
 			Context context) throws Exception {
-		String exitLab = CodeUtils.freshLabel();
+		String exitLab = freshLabel();
 		int operand = generate(s.expr, environment, block, forest, context);
 		String defaultTarget = exitLab;
 		HashSet values = new HashSet<>();
@@ -1072,7 +1072,7 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b
 				if (defaultTarget != exitLab) {
 					WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context, c);
 				} else {
-					defaultTarget = CodeUtils.freshLabel();
+					defaultTarget = freshLabel();
 					block.add(new Bytecode.Label(defaultTarget), attributes(c));
 					for (Stmt st : c.stmts) {
 						generate(st, environment, block, forest, context);
@@ -1081,7 +1081,7 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b
 				}
 
 			} else if (defaultTarget == exitLab) {
-				String target = CodeUtils.freshLabel();
+				String target = freshLabel();
 				block.add(new Bytecode.Label(target), attributes(c));
 
 				// Case statements in Whiley may have multiple matching constant
@@ -1162,10 +1162,10 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl
 		// A label marking where execution continues after the while
 		// loop finishes. Used when the loop condition evaluates to false
 		// or when a break statement is encountered.
-		String exitLab = CodeUtils.freshLabel();
+		String exitLab = freshLabel();
 		// A label marking the end of the current loop iteration. Used
 		// by the continue statement.
-		String continueLab = CodeUtils.freshLabel();
+		String continueLab = freshLabel();
 
 		CodeForest.Block bodyBlock = new CodeForest.Block();
 		int body = forest.add(bodyBlock);
@@ -1235,10 +1235,10 @@ private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block
 		// A label marking where execution continues after the do-while
 		// loop finishes. Used when the loop condition evaluates to false
 		// or when a break statement is encountered.
-		String exitLab = CodeUtils.freshLabel();
+		String exitLab = freshLabel();
 		// A label marking the end of the current loop iteration. Used
 		// by the continue statement.
-		String continueLab = CodeUtils.freshLabel();
+		String continueLab = freshLabel();
 
 		CodeForest.Block bodyBlock = new CodeForest.Block();
 		int body = forest.add(bodyBlock);
@@ -1423,7 +1423,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm
 			generateCondition(target, v.rhs, environment, block, forest, context);
 
 		} else if (bop == Expr.BOp.AND) {
-			String exitLabel = CodeUtils.freshLabel();
+			String exitLabel = freshLabel();
 			generateCondition(exitLabel, invert(v.lhs), environment, block, forest, context);
 			generateCondition(target, v.rhs, environment, block, forest, context);
 			block.add(new Bytecode.Label(exitLabel));
@@ -1444,7 +1444,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm
 			} else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable
 					&& v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) {
 				// this is a simple rewrite to enable type inference.
-				String exitLabel = CodeUtils.freshLabel();
+				String exitLabel = freshLabel();
 				Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs;
 				if (environment.get(lhs.var) == null) {
 					syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs);
@@ -1545,7 +1545,7 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme
 			// its true destination to a temporary label. Then, for the fall
 			// through case we branch to our true destination.
 
-			String label = CodeUtils.freshLabel();
+			String label = freshLabel();
 			generateCondition(label, v.mhs, environment, block, forest, context);
 			block.add(new Bytecode.Goto(target));
 			block.add(new Bytecode.Label(label));
@@ -1580,7 +1580,7 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme
 	private void generateCondition(String target, Expr.Quantifier e, Environment environment, CodeForest.Block block,
 			CodeForest forest, Context context) {
 
-		String exit = CodeUtils.freshLabel();
+		String exit = freshLabel();
 		generate(e.sources.iterator(), target, exit, e, environment, block, forest, context);
 
 		switch (e.cop) {
@@ -1938,8 +1938,8 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo
 			Context context) throws Exception {
 		// could probably use a range test for this somehow
 		if(v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) {
-			String trueLabel = CodeUtils.freshLabel();
-			String exitLabel = CodeUtils.freshLabel();
+			String trueLabel = freshLabel();
+			String exitLabel = freshLabel();
 			generateCondition(trueLabel, v, environment, block, forest, context);
 			int target = environment.allocate(Type.T_BOOL);
 			block.add(new Bytecode.Const(target, Constant.V_BOOL(false)), attributes(v));
@@ -1982,8 +1982,8 @@ private int generate(Expr.ArrayGenerator expr, Environment environment, CodeFore
 
 	private int generate(Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest,
 			Context context) {
-		String trueLabel = CodeUtils.freshLabel();
-		String exitLabel = CodeUtils.freshLabel();
+		String trueLabel = freshLabel();
+		String exitLabel = freshLabel();
 		generateCondition(trueLabel, e, environment, block, forest, context);
 		int target = environment.allocate(Type.T_BOOL);
 		block.add(new Bytecode.Const(target, Constant.V_BOOL(false)), attributes(e));
@@ -2225,7 +2225,7 @@ public List toIntegerList(int... items) {
 		return list;
 	}
 
-	public int[] toIntArray(List items) {
+	private int[] toIntArray(List items) {
 		int[] arr = new int[items.size()];
 		for(int i=0;i!=arr.length;++i) {
 			arr[i] = items.get(i);
@@ -2233,6 +2233,12 @@ public int[] toIntArray(List items) {
 		return arr;
 	}
 	
+	private static int _idx=0;
+	public static String freshLabel() {
+		return "blklab" + _idx++;
+	}
+
+	
 	/**
 	 * Maintains a mapping from Variable names to their allocated register slot,
 	 * and their declared types.
diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java
index 597afaf75b..9db7210f63 100644
--- a/modules/wyil/src/wyil/builders/VcGenerator.java
+++ b/modules/wyil/src/wyil/builders/VcGenerator.java
@@ -322,7 +322,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) {
 	public List transform(VcBranch branch, CodeForest.Index root, boolean isInvariant, Type[] environment,
 			CodeForest forest) {
 		// Construct the label map which is needed for conditional branches
-		Map labels = CodeUtils.buildLabelMap(forest);
+		Map labels = Bytecode.buildLabelMap(forest);
 		Pair> p = transform(root.block(), 0, null, branch, false, isInvariant, environment,
 				labels, forest);
 		// Ok, return list of exit branches
diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java
index c5ce406368..5c4629365b 100755
--- a/modules/wyil/src/wyil/lang/Bytecode.java
+++ b/modules/wyil/src/wyil/lang/Bytecode.java
@@ -33,8 +33,6 @@
 import wyil.lang.Bytecode.Branching;
 import wyil.lang.Bytecode.Compound;
 
-import static wyil.lang.CodeUtils.*;
-
 /**
  * Represents a WyIL bytecode. The Whiley Intermediate Language (WyIL) employs
  * register-based bytecodes (as opposed to e.g. the Java Virtual Machine, which
@@ -1872,6 +1870,43 @@ public String toString() {
 	// =============================================================
 	// Helpers
 	// =============================================================
+
+
+	/**
+	 * Construct a mapping from labels to their block indices within a root
+	 * block. This is useful so they can easily be resolved during the
+	 * subsequent traversal of the block.
+	 * 
+	 * @param block
+	 * @return
+	 */
+	public static Map buildLabelMap(CodeForest forest) {
+		HashMap labels = new HashMap();
+		for (int i = 0; i != forest.numBlocks(); ++i) {
+			CodeForest.Block block = forest.get(i);
+			for (int j = 0; j != block.size(); ++j) {
+				Bytecode code = block.get(j).code();
+				if (code instanceof Bytecode.Label) {
+					// Found a label, so register it in the labels map
+					Bytecode.Label label = (Bytecode.Label) code;
+					labels.put(label.label(), new CodeForest.Index(i, j));
+				}
+			}
+		}
+		return labels;
+	}
+	
+	private static String arrayToString(int... operands) {
+		String r = "(";
+		for (int i = 0; i != operands.length; ++i) {
+			if (i != 0) {
+				r = r + ", ";
+			}
+			r = r + "%" + operands[i];			
+		}
+		return r + ")";
+	}
+
 	private static int[] append(int[] operands, int operand) {
 		int[] noperands = Arrays.copyOf(operands, operands.length + 1);
 		noperands[operands.length] = operand;
diff --git a/modules/wyil/src/wyil/lang/CodeUtils.java b/modules/wyil/src/wyil/lang/CodeUtils.java
deleted file mode 100644
index 5fc72e2ce3..0000000000
--- a/modules/wyil/src/wyil/lang/CodeUtils.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package wyil.lang;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-public class CodeUtils {
-
-	private static int _idx=0;
-	public static String freshLabel() {
-		return "blklab" + _idx++;
-	}
-
-	public static String arrayToString(int... operands) {
-		String r = "(";
-		for (int i = 0; i != operands.length; ++i) {
-			if (i != 0) {
-				r = r + ", ";
-			}
-			r = r + "%" + operands[i];			
-		}
-		return r + ")";
-	}
-
-	public static int[] toIntArray(Collection operands) {
-		int[] ops = new int[operands.size()];
-		int i = 0;
-		for (Integer o : operands) {
-			ops[i++] = o;
-		}
-		return ops;
-	}
-
-	public static int[] remapOperands(Map binding, int[] operands) {
-		int[] nOperands = operands;
-		for (int i = 0; i != nOperands.length; ++i) {
-			int o = operands[i];
-			Integer nOperand = binding.get(o);
-			if (nOperand != null) {
-				if (nOperands == operands) {
-					nOperands = Arrays.copyOf(operands, operands.length);
-				}
-				nOperands[i] = nOperand;
-			}
-		}
-		return nOperands;
-	}
-	
-	/**
-	 * Construct a mapping from labels to their block indices within a root
-	 * block. This is useful so they can easily be resolved during the
-	 * subsequent traversal of the block.
-	 * 
-	 * @param block
-	 * @return
-	 */
-	public static Map buildLabelMap(CodeForest forest) {
-		HashMap labels = new HashMap();
-		for (int i = 0; i != forest.numBlocks(); ++i) {
-			CodeForest.Block block = forest.get(i);
-			for (int j = 0; j != block.size(); ++j) {
-				Bytecode code = block.get(j).code();
-				if (code instanceof Bytecode.Label) {
-					// Found a label, so register it in the labels map
-					Bytecode.Label label = (Bytecode.Label) code;
-					labels.put(label.label(), new CodeForest.Index(i, j));
-				}
-			}
-		}
-		return labels;
-	}
-}
diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java
index 471279373e..6bfa690918 100644
--- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java
+++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java
@@ -893,7 +893,7 @@ public Context(CodeForest.Index pc, CodeForest block) {
 
 		public CodeForest.Index getLabel(String label) {
 			if (labels == null) {
-				labels = CodeUtils.buildLabelMap(forest);
+				labels = Bytecode.buildLabelMap(forest);
 			}
 			return labels.get(label);
 		}

From 3180879ce4d5257573e5f58271fbc9c3179be36f Mon Sep 17 00:00:00 2001
From: DavePearce 
Date: Mon, 18 Apr 2016 20:06:44 +1200
Subject: [PATCH 28/43] WyIL: Encapsulate Constant fields

This encapsulates a bunch if previously public fields in
wyil.lang.Constant.  Also renames CodeForest => BytecodeForest.
---
 .../wyc/src/wyc/builder/CodeGenerator.java    | 186 +++++++++---------
 .../wyc/src/wyc/builder/FlowTypeChecker.java  |  36 ++--
 modules/wyc/src/wyc/io/WhileyFileParser.java  |  18 +-
 modules/wyil/src/wyil/builders/VcBranch.java  |   8 +-
 .../src/wyil/builders/VcExprGenerator.java    |  34 ++--
 .../wyil/src/wyil/builders/VcGenerator.java   | 106 +++++-----
 modules/wyil/src/wyil/builders/VcUtils.java   |  28 +--
 .../wyil/src/wyil/checks/CoercionCheck.java   |   8 +-
 .../wyil/checks/DefiniteAssignmentCheck.java  |  16 +-
 modules/wyil/src/wyil/checks/ModuleCheck.java |   8 +-
 modules/wyil/src/wyil/io/WyilFilePrinter.java |  10 +-
 modules/wyil/src/wyil/io/WyilFileReader.java  |  44 ++---
 modules/wyil/src/wyil/io/WyilFileWriter.java  |  54 ++---
 modules/wyil/src/wyil/lang/Bytecode.java      |  39 ++--
 .../{CodeForest.java => BytecodeForest.java}  |  10 +-
 modules/wyil/src/wyil/lang/Constant.java      | 152 +++++++-------
 modules/wyil/src/wyil/lang/WyilFile.java      |  16 +-
 .../src/wyil/transforms/LoopVariants.java     |  12 +-
 .../wyil/util/dfa/BackwardFlowAnalysis.java   |  22 +--
 .../wyil/util/dfa/ForwardFlowAnalysis.java    |  22 +--
 .../wyil/util/interpreter/Interpreter.java    |  88 ++++-----
 .../util/interpreter/StandardFunctions.java   |  66 ++++---
 modules/wyjc/src/wyjc/Wyil2JavaBuilder.java   | 120 +++++------
 23 files changed, 548 insertions(+), 555 deletions(-)
 rename modules/wyil/src/wyil/lang/{CodeForest.java => BytecodeForest.java} (96%)

diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java
index e9a72b61c7..f8aa92d7ee 100644
--- a/modules/wyc/src/wyc/builder/CodeGenerator.java
+++ b/modules/wyc/src/wyc/builder/CodeGenerator.java
@@ -182,7 +182,7 @@ private WyilFile.Constant generate(WhileyFile.Constant cd) {
 	 * @throws Exception
 	 */
 	private WyilFile.Type generate(WhileyFile.Type td) throws Exception {
-		CodeForest forest = new CodeForest();
+		BytecodeForest forest = new BytecodeForest();
 		Environment environment = new Environment();
 		// Allocate declared parameter
 		environment.allocate(td.resolvedType.raw(), td.parameter.name());
@@ -209,9 +209,9 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw
 		// Construct environments
 		// ==================================================================
 
-		CodeForest forest = new CodeForest();
+		BytecodeForest forest = new BytecodeForest();
 		Environment environment = new Environment();
-		ArrayList declarations = new ArrayList();
+		ArrayList declarations = new ArrayList();
 		addDeclaredParameters(fd.parameters, fd.resolvedType().params(), environment, declarations);
 		addDeclaredParameters(fd.returns, fd.resolvedType().returns(), environment, declarations);
 		// Allocate all declared variables now. This ensures that all declared
@@ -249,7 +249,7 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw
 		// fail because they have the same type).
 		environment = resetEnvironment(environment, declarations);
 
-		CodeForest.Block body = new CodeForest.Block();
+		BytecodeForest.Block body = new BytecodeForest.Block();
 		forest.addAsRoot(body);
 		for (Stmt s : fd.statements) {
 			generate(s, environment, body, forest, fd);
@@ -288,8 +288,8 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw
 	 * @param forest
 	 * @param context
 	 */
-	private int generateInvariantBlock(Expr invariant, Environment environment, CodeForest forest, Context context) {
-		CodeForest.Block precondition = new CodeForest.Block();
+	private int generateInvariantBlock(Expr invariant, Environment environment, BytecodeForest forest, Context context) {
+		BytecodeForest.Block precondition = new BytecodeForest.Block();
 		int index = forest.add(precondition);
 		String endLab = freshLabel();
 		generateCondition(endLab, invariant, environment, precondition, forest, context);
@@ -306,14 +306,14 @@ private int generateInvariantBlock(Expr invariant, Environment environment, Code
 	 * compile and run a Whiley program. However, it is very useful for
 	 * debugging and performing verification.
 	 */
-	private List createVariableDeclarations(Environment environment,
-			List declarations) {
+	private List createVariableDeclarations(Environment environment,
+			List declarations) {
 		// FIXME: this is a hack. In essence, we're trying to get the types of
 		// all intermediate registers used in code generation. To do this, we're
 		// looking at their type having typed the entire function.
 		for (int i = declarations.size(); i < environment.size(); i = i + 1) {
 			Type t = environment.type(i);
-			declarations.add(new CodeForest.Register(t, null));
+			declarations.add(new BytecodeForest.Register(t, null));
 		}
 		return declarations;
 	}
@@ -329,11 +329,11 @@ private List createVariableDeclarations(Environment environ
 	 *            --- List of declarations being constructed
 	 */
 	private void addDeclaredParameters(List parameters, List types,
-			Environment environment, List declarations) {
+			Environment environment, List declarations) {
 		for (int i = 0; i != parameters.size(); ++i) {
 			WhileyFile.Parameter parameter = parameters.get(i);
 			// allocate parameter to register in the current block
-			declarations.add(new CodeForest.Register(types.get(i).nominal(), parameter.name));
+			declarations.add(new BytecodeForest.Register(types.get(i).nominal(), parameter.name));
 			// allocate parameter to register in the current block
 			environment.allocate(types.get(i).raw(), parameter.name);
 		}
@@ -361,7 +361,7 @@ private void addDeclaredParameters(List parameters, List fields, ArrayList operands,
-			Environment environment, CodeForest.Block block, CodeForest forest, Context context) {
+			Environment environment, BytecodeForest.Block block, BytecodeForest forest, Context context) {
 
 		if (e instanceof Expr.AssignedVariable) {
 			Expr.AssignedVariable v = (Expr.AssignedVariable) e;
@@ -633,10 +633,10 @@ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, Arra
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Assert s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Assert s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		// First, create assert block body
-		CodeForest.Block subblock = new CodeForest.Block();
+		BytecodeForest.Block subblock = new BytecodeForest.Block();
 		int body = forest.add(subblock);
 		String endLab = freshLabel();
 		generateCondition(endLab, s.expr, environment, subblock, forest, context);
@@ -662,10 +662,10 @@ private void generate(Stmt.Assert s, Environment environment, CodeForest.Block b
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Assume s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Assume s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		// First, create assume block body
-		CodeForest.Block subblock = new CodeForest.Block();
+		BytecodeForest.Block subblock = new BytecodeForest.Block();
 		int body = forest.add(subblock);
 		String endLab = freshLabel();
 		generateCondition(endLab, s.expr, environment, subblock, forest, context);
@@ -708,7 +708,7 @@ private void generate(Stmt.Assume s, Environment environment, CodeForest.Block b
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Return s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Return s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		List returns = s.returns;
 		// Here, we don't put the type propagated for the return expression.
@@ -749,7 +749,7 @@ private void generate(Stmt.Return s, Environment environment, CodeForest.Block b
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Skip s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Skip s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		// TODO: should actually generate a NOP bytecode. This is an assignment
 		// from zero operands to zero targets. At the moment, I cannot encode
@@ -787,7 +787,7 @@ private void generate(Stmt.Skip s, Environment environment, CodeForest.Block blo
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Debug s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Debug s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int operand = generate(s.expr, environment, block, forest, context);
 		block.add(new Bytecode.Debug(operand), attributes(s));
@@ -818,7 +818,7 @@ private void generate(Stmt.Debug s, Environment environment, CodeForest.Block bl
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Fail s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Fail s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		block.add(new Bytecode.Fail(), attributes(s));
 	}
@@ -868,7 +868,7 @@ private void generate(Stmt.Fail s, Environment environment, CodeForest.Block blo
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.IfElse s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.IfElse s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 
 		String falseLab = freshLabel();
@@ -935,7 +935,7 @@ private void generate(Stmt.IfElse s, Environment environment, CodeForest.Block b
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Break s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Break s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		LoopScope scope = findEnclosingScope(LoopScope.class);
 		if (scope == null) {
@@ -992,7 +992,7 @@ private void generate(Stmt.Break s, Environment environment, CodeForest.Block bl
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Continue s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Continue s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		LoopScope scope = findEnclosingScope(LoopScope.class);
 		if (scope == null) {
@@ -1055,7 +1055,7 @@ private void generate(Stmt.Continue s, Environment environment, CodeForest.Block
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.Switch s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.Switch s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) throws Exception {
 		String exitLab = freshLabel();
 		int operand = generate(s.expr, environment, block, forest, context);
@@ -1157,7 +1157,7 @@ private void generate(Stmt.Switch s, Environment environment, CodeForest.Block b
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.While s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.While s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		// A label marking where execution continues after the while
 		// loop finishes. Used when the loop condition evaluates to false
@@ -1167,7 +1167,7 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl
 		// by the continue statement.
 		String continueLab = freshLabel();
 
-		CodeForest.Block bodyBlock = new CodeForest.Block();
+		BytecodeForest.Block bodyBlock = new BytecodeForest.Block();
 		int body = forest.add(bodyBlock);
 
 		for (Expr condition : s.invariants) {
@@ -1230,7 +1230,7 @@ private void generate(Stmt.While s, Environment environment, CodeForest.Block bl
 	 *            with error reporting as it determines the enclosing file.
 	 * @return
 	 */
-	private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block block, CodeForest forest,
+	private void generate(Stmt.DoWhile s, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		// A label marking where execution continues after the do-while
 		// loop finishes. Used when the loop condition evaluates to false
@@ -1240,7 +1240,7 @@ private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block
 		// by the continue statement.
 		String continueLab = freshLabel();
 
-		CodeForest.Block bodyBlock = new CodeForest.Block();
+		BytecodeForest.Block bodyBlock = new BytecodeForest.Block();
 		int body = forest.add(bodyBlock);
 
 		scopes.push(new LoopScope(continueLab, exitLab));
@@ -1316,8 +1316,8 @@ private void generate(Stmt.DoWhile s, Environment environment, CodeForest.Block
 	 *            appended.
 	 * @return
 	 */
-	public void generateCondition(String target, Expr condition, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) {
+	public void generateCondition(String target, Expr condition, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) {
 		try {
 
 			// First, we see whether or not we can employ a special handler for
@@ -1383,10 +1383,10 @@ public void generateCondition(String target, Expr condition, Environment environ
 	 *            appended.
 	 * @return
 	 */
-	private void generateCondition(String target, Expr.Constant c, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) {
+	private void generateCondition(String target, Expr.Constant c, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) {
 		Constant.Bool b = (Constant.Bool) c.value;
-		if (b.value) {
+		if (b.value()) {
 			block.add(new Bytecode.Goto(target));
 		} else {
 			// do nout
@@ -1413,8 +1413,8 @@ private void generateCondition(String target, Expr.Constant c, Environment envir
 	 *            appended.
 	 * @return
 	 */
-	private void generateCondition(String target, Expr.BinOp v, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) throws Exception {
+	private void generateCondition(String target, Expr.BinOp v, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) throws Exception {
 
 		Expr.BOp bop = v.op;
 
@@ -1433,7 +1433,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm
 
 		} else {
 			if (bop == Expr.BOp.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant
-					&& ((Expr.Constant) v.rhs).value == Constant.V_NULL) {
+					&& ((Expr.Constant) v.rhs).value == Constant.Null) {
 				// this is a simple rewrite to enable type inference.
 				Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs;
 				if (environment.get(lhs.var) == null) {
@@ -1442,7 +1442,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm
 				int slot = environment.get(lhs.var);
 				block.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v));
 			} else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable
-					&& v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.V_NULL) {
+					&& v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.Null) {
 				// this is a simple rewrite to enable type inference.
 				String exitLabel = freshLabel();
 				Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs;
@@ -1485,7 +1485,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm
 	 * @return
 	 */
 	private void generateTypeCondition(String target, Expr.BinOp condition, Environment environment,
-			CodeForest.Block block, CodeForest forest, Context context) throws Exception {
+			BytecodeForest.Block block, BytecodeForest forest, Context context) throws Exception {
 		int leftOperand;
 
 		if (condition.lhs instanceof Expr.LocalVariable) {
@@ -1536,8 +1536,8 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Environm
 	 *            appended.
 	 * @return
 	 */
-	private void generateCondition(String target, Expr.UnOp v, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) {
+	private void generateCondition(String target, Expr.UnOp v, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) {
 		Expr.UOp uop = v.op;
 		switch (uop) {
 		case NOT:
@@ -1577,8 +1577,8 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme
 	 *            appended.
 	 * @return
 	 */
-	private void generateCondition(String target, Expr.Quantifier e, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) {
+	private void generateCondition(String target, Expr.Quantifier e, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) {
 
 		String exit = freshLabel();
 		generate(e.sources.iterator(), target, exit, e, environment, block, forest, context);
@@ -1598,7 +1598,7 @@ private void generateCondition(String target, Expr.Quantifier e, Environment env
 	}
 
 	private void generate(Iterator> srcIterator, String trueLabel, String falseLabel,
-			Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest, Context context) {
+			Expr.Quantifier e, Environment environment, BytecodeForest.Block block, BytecodeForest forest, Context context) {
 
 		if (srcIterator.hasNext()) {
 			// This is the inductive case (i.e. an outer loop)
@@ -1610,7 +1610,7 @@ private void generate(Iterator> srcIterator, String t
 			int endSlot = generate(src.third(), environment, block, forest, context);
 
 			// Second, recursively generate remaining parts
-			CodeForest.Block bodyBlock = new CodeForest.Block();
+			BytecodeForest.Block bodyBlock = new BytecodeForest.Block();
 			int body = forest.add(bodyBlock);
 			generate(srcIterator, trueLabel, falseLabel, e, environment, bodyBlock, forest, context);
 			// Finally, create the forall loop bytecode
@@ -1635,7 +1635,7 @@ private void generate(Iterator> srcIterator, String t
 	// Multi-Expressions
 	// =========================================================================
 
-	public int[] generate(Expr.Multi expression, Environment environment, CodeForest.Block block, CodeForest forest,
+	public int[] generate(Expr.Multi expression, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		List returns = expression.returns();
 		int[] targets = new int[returns.size()];
@@ -1664,15 +1664,15 @@ public int[] generate(Expr.Multi expression, Environment environment, CodeForest
 		return targets;
 	}
 
-	public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context, int... targets) throws ResolveError {
+	public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context, int... targets) throws ResolveError {
 		//
 		int[] operands = generate(expr.arguments, environment, block, forest, context);
 		block.add(new Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr));
 	}
 
-	public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context, int... targets) throws ResolveError {
+	public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context, int... targets) throws ResolveError {
 		//
 		int operand = generate(expr.src, environment, block, forest, context);
 		int[] operands = generate(expr.arguments, environment, block, forest, context);
@@ -1698,7 +1698,7 @@ public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment env
 	 *
 	 * @return --- the register
 	 */
-	public int generate(Expr expression, Environment environment, CodeForest.Block block, CodeForest forest,
+	public int generate(Expr expression, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		try {
 			if (expression instanceof Expr.Constant) {
@@ -1754,21 +1754,21 @@ public int generate(Expr expression, Environment environment, CodeForest.Block b
 		return -1; // deadcode
 	}
 
-	public int generate(Expr.FunctionOrMethodCall expr, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) throws ResolveError {
+	public int generate(Expr.FunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) throws ResolveError {
 		int target = environment.allocate(expr.result().raw());
 		generateStmt(expr, environment, block, forest, context, target);
 		return target;
 	}
 
-	public int generate(Expr.IndirectFunctionOrMethodCall expr, Environment environment, CodeForest.Block block,
-			CodeForest forest, Context context) throws ResolveError {
+	public int generate(Expr.IndirectFunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block,
+			BytecodeForest forest, Context context) throws ResolveError {
 		int target = environment.allocate(expr.result().raw());
 		generateStmt(expr, environment, block, forest, context, target);
 		return target;
 	}
 
-	private int generate(Expr.Constant expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.Constant expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		Constant val = expr.value;
 		int target = environment.allocate(val.type());
@@ -1776,7 +1776,7 @@ private int generate(Expr.Constant expr, Environment environment, CodeForest.Blo
 		return target;
 	}
 
-	private int generate(Expr.FunctionOrMethod expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.FunctionOrMethod expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		Type.FunctionOrMethod rawType = expr.type.raw();
 		Type.FunctionOrMethod nominalType = expr.type.nominal();
@@ -1785,7 +1785,7 @@ private int generate(Expr.FunctionOrMethod expr, Environment environment, CodeFo
 		return target;
 	}
 
-	private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.Lambda expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		Type.FunctionOrMethod tfm = expr.type.raw();
 		List tfm_params = tfm.params();
@@ -1794,15 +1794,15 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block
 		// Create environment for the lambda body.
 		ArrayList operands = new ArrayList();
 		ArrayList paramTypes = new ArrayList();
-		CodeForest bodyForest = new CodeForest();
-		List declarations = bodyForest.registers();
+		BytecodeForest bodyForest = new BytecodeForest();
+		List declarations = bodyForest.registers();
 		Environment benv = new Environment();
 		for (int i = 0; i != tfm_params.size(); ++i) {
 			Type type = tfm_params.get(i);
 			String name = expr_params.get(i).name;
 			benv.allocate(type, name);
 			paramTypes.add(type);
-			declarations.add(new CodeForest.Register(type, name));
+			declarations.add(new BytecodeForest.Register(type, name));
 		}
 		for (Pair v : Exprs.uses(expr.body, context)) {
 			if (benv.get(v.second()) == null) {
@@ -1810,12 +1810,12 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block
 				benv.allocate(type, v.second());
 				paramTypes.add(type);
 				operands.add(environment.get(v.second()));
-				declarations.add(new CodeForest.Register(type, v.second()));
+				declarations.add(new BytecodeForest.Register(type, v.second()));
 			}
 		}
 		// Generate body based on current environment
 
-		CodeForest.Block bodyBlock = new CodeForest.Block();
+		BytecodeForest.Block bodyBlock = new BytecodeForest.Block();
 		bodyForest.addAsRoot(bodyBlock);
 		if (tfm.returns().isEmpty()) {
 			bodyBlock.add(new Bytecode.Return(), attributes(expr));
@@ -1830,7 +1830,7 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block
 		// about declared variables.
 		for (int i = declarations.size(); i != benv.size(); i = i + 1) {
 			Type t = benv.type(i);
-			declarations.add(new CodeForest.Register(t, null));
+			declarations.add(new BytecodeForest.Register(t, null));
 		}
 		// Create concrete type for private lambda function
 		Type.FunctionOrMethod cfm;
@@ -1857,7 +1857,7 @@ private int generate(Expr.Lambda expr, Environment environment, CodeForest.Block
 		return target;
 	}
 
-	private int generate(Expr.ConstantAccess expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.ConstantAccess expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) throws ResolveError {
 		Constant val = expr.value;
 		int target = environment.allocate(val.type());
@@ -1865,7 +1865,7 @@ private int generate(Expr.ConstantAccess expr, Environment environment, CodeFore
 		return target;
 	}
 
-	private int generate(Expr.LocalVariable expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.LocalVariable expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) throws ResolveError {
 
 		if (environment.get(expr.var) != null) {
@@ -1878,7 +1878,7 @@ private int generate(Expr.LocalVariable expr, Environment environment, CodeFores
 		}
 	}
 
-	private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.UnOp expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int[] operands = new int[] { generate(expr.mhs, environment, block, forest, context) };
 		int[] targets = new int[] { environment.allocate(expr.result().raw()) };
@@ -1906,7 +1906,7 @@ private int generate(Expr.UnOp expr, Environment environment, CodeForest.Block b
 		return targets[0];
 	}
 
-	private int generate(Expr.Dereference expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.Dereference expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int[] operands = new int[] { generate(expr.src, environment, block, forest, context) };
 		int[] targets = new int[] { environment.allocate(expr.result().raw()) };
@@ -1915,7 +1915,7 @@ private int generate(Expr.Dereference expr, Environment environment, CodeForest.
 		return targets[0];
 	}
 
-	private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.IndexOf expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int[] operands = { generate(expr.src, environment, block, forest, context),
 				generate(expr.index, environment, block, forest, context) };
@@ -1924,7 +1924,7 @@ private int generate(Expr.IndexOf expr, Environment environment, CodeForest.Bloc
 		return targets[0];
 	}
 
-	private int generate(Expr.Cast expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.Cast expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int operand = generate(expr.expr, environment, block, forest, context);
 		Type from = expr.expr.result().raw();
@@ -1934,7 +1934,7 @@ private int generate(Expr.Cast expr, Environment environment, CodeForest.Block b
 		return target;
 	}
 
-	private int generate(Expr.BinOp v, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.BinOp v, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) throws Exception {
 		// could probably use a range test for this somehow
 		if(v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) {
@@ -1942,10 +1942,10 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo
 			String exitLabel = freshLabel();
 			generateCondition(trueLabel, v, environment, block, forest, context);
 			int target = environment.allocate(Type.T_BOOL);
-			block.add(new Bytecode.Const(target, Constant.V_BOOL(false)), attributes(v));
+			block.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(v));
 			block.add(new Bytecode.Goto(exitLabel));
 			block.add(new Bytecode.Label(trueLabel));
-			block.add(new Bytecode.Const(target, Constant.V_BOOL(true)), attributes(v));
+			block.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(v));
 			block.add(new Bytecode.Label(exitLabel));
 			return target;
 		} else {
@@ -1962,7 +1962,7 @@ private int generate(Expr.BinOp v, Environment environment, CodeForest.Block blo
 		}
 	}
 
-	private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.ArrayInitialiser expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int[] operands = generate(expr.arguments, environment, block, forest, context);
 		int[] targets = new int[] { environment.allocate(expr.result().raw()) };
@@ -1971,7 +1971,7 @@ private int generate(Expr.ArrayInitialiser expr, Environment environment, CodeFo
 		return targets[0];
 	}
 
-	private int generate(Expr.ArrayGenerator expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.ArrayGenerator expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int[] operands = new int[] { generate(expr.element, environment, block, forest, context),
 				generate(expr.count, environment, block, forest, context) };
@@ -1980,21 +1980,21 @@ private int generate(Expr.ArrayGenerator expr, Environment environment, CodeFore
 		return targets[0];
 	}
 
-	private int generate(Expr.Quantifier e, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.Quantifier e, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		String trueLabel = freshLabel();
 		String exitLabel = freshLabel();
 		generateCondition(trueLabel, e, environment, block, forest, context);
 		int target = environment.allocate(Type.T_BOOL);
-		block.add(new Bytecode.Const(target, Constant.V_BOOL(false)), attributes(e));
+		block.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(e));
 		block.add(new Bytecode.Goto(exitLabel));
 		block.add(new Bytecode.Label(trueLabel));
-		block.add(new Bytecode.Const(target, Constant.V_BOOL(true)), attributes(e));
+		block.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(e));
 		block.add(new Bytecode.Label(exitLabel));
 		return target;
 	}
 
-	private int generate(Expr.Record expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.Record expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		ArrayList keys = new ArrayList(expr.fields.keySet());
 		Collections.sort(keys);
@@ -2010,7 +2010,7 @@ private int generate(Expr.Record expr, Environment environment, CodeForest.Block
 		return targets[0];
 	}
 
-	private int generate(Expr.FieldAccess expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.FieldAccess expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int operand = generate(expr.src, environment, block, forest, context);
 		int target = environment.allocate(expr.result().raw());
@@ -2019,7 +2019,7 @@ private int generate(Expr.FieldAccess expr, Environment environment, CodeForest.
 		return target;
 	}
 
-	private int generate(Expr.New expr, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int generate(Expr.New expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) throws ResolveError {
 		int[] operands = new int[] { generate(expr.expr, environment, block, forest, context) };
 		int[] targets = new int[] { environment.allocate(expr.result().raw()) };
@@ -2027,7 +2027,7 @@ private int generate(Expr.New expr, Environment environment, CodeForest.Block bl
 		return targets[0];
 	}
 
-	private int[] generate(List arguments, Environment environment, CodeForest.Block block, CodeForest forest,
+	private int[] generate(List arguments, Environment environment, BytecodeForest.Block block, BytecodeForest forest,
 			Context context) {
 		int[] operands = new int[arguments.size()];
 		for (int i = 0; i != operands.length; ++i) {
@@ -2140,10 +2140,10 @@ private static Expr invert(Expr e) {
 	 * @param declarations
 	 * @return
 	 */
-	public Environment resetEnvironment(Environment environment, List declarations) {
+	public Environment resetEnvironment(Environment environment, List declarations) {
 		Environment nenv = new Environment();
 		for(int i=0;i!=declarations.size();++i) {
-			CodeForest.Register reg = declarations.get(i);
+			BytecodeForest.Register reg = declarations.get(i);
 			nenv.allocate(reg.type(),reg.name());
 		}
 		for(int i=declarations.size();i block, List declarations,
+	public void buildVariableDeclarations(List block, List declarations,
 			Environment environment, WhileyFile.Context context) {
 		//
 		for (int i = 0; i != block.size(); ++i) {
@@ -2166,7 +2166,7 @@ public void buildVariableDeclarations(List block, List declarations, Environment environment,
+	public void buildVariableDeclarations(Stmt stmt, List declarations, Environment environment,
 			WhileyFile.Context context) {
 		if (stmt instanceof Assign || stmt instanceof Assert || stmt instanceof Assume || stmt instanceof Return
 				|| stmt instanceof Debug || stmt instanceof Fail || stmt instanceof Break || stmt instanceof Continue
@@ -2177,7 +2177,7 @@ public void buildVariableDeclarations(Stmt stmt, List decla
 			return;
 		} else if (stmt instanceof VariableDeclaration) {
 			VariableDeclaration d = (VariableDeclaration) stmt;
-			declarations.add(new CodeForest.Register(d.type.nominal(), d.parameter.name));
+			declarations.add(new BytecodeForest.Register(d.type.nominal(), d.parameter.name));
 			environment.allocate(d.type.raw(), d.parameter.name);
 		} else if (stmt instanceof IfElse) {
 			IfElse s = (IfElse) stmt;
@@ -2298,11 +2298,11 @@ public void put(int idx, String v) {
 			var2idx.put(v, idx);
 		}
 
-		public ArrayList asRegisters() {
-			ArrayList registers = new ArrayList();
+		public ArrayList asRegisters() {
+			ArrayList registers = new ArrayList();
 			for (int i = 0; i != idx2type.size(); ++i) {
 				Type t = idx2type.get(i);
-				registers.add(new CodeForest.Register(t, get(i)));
+				registers.add(new BytecodeForest.Register(t, get(i)));
 			}
 			return registers;
 		}
diff --git a/modules/wyc/src/wyc/builder/FlowTypeChecker.java b/modules/wyc/src/wyc/builder/FlowTypeChecker.java
index e7d75bf0cf..d7f420c644 100644
--- a/modules/wyc/src/wyc/builder/FlowTypeChecker.java
+++ b/modules/wyc/src/wyc/builder/FlowTypeChecker.java
@@ -1301,7 +1301,7 @@ private Pair resolveLeafCondition(Expr.BinOp bop, boolean sig
 			// treated the same as !(x is null)
 
 			if (lhs instanceof Expr.LocalVariable && rhs instanceof Expr.Constant
-					&& ((Expr.Constant) rhs).value == Constant.V_NULL) {
+					&& ((Expr.Constant) rhs).value == Constant.Null) {
 				// bingo, special case
 				Expr.LocalVariable lv = (Expr.LocalVariable) lhs;
 				Nominal newType;
@@ -2890,14 +2890,14 @@ private Pair resolveAsConstant(Expr expr, Context context, Ha
 					values.add(e.first());
 					element = Nominal.Union(element, e.second());
 				}
-				return new Pair(Constant.V_ARRAY(values),
+				return new Pair(new Constant.Array(values),
 						Nominal.Array(element, !nop.arguments.isEmpty()));
 			} else if (expr instanceof Expr.ArrayGenerator) {
 				Expr.ArrayGenerator lg = (Expr.ArrayGenerator) expr;
 				Pair element = resolveAsConstant(lg.element, context, visited);
 				Pair count = resolveAsConstant(lg.count, context, visited);
 				Constant.Array l = evaluate(lg, element.first(), count.first(), context);
-				return new Pair(l, Nominal.Array(element.second(), !l.values.isEmpty()));
+				return new Pair(l, Nominal.Array(element.second(), !l.values().isEmpty()));
 			} else if (expr instanceof Expr.Record) {
 				Expr.Record rg = (Expr.Record) expr;
 				HashMap values = new HashMap();
@@ -2910,11 +2910,11 @@ private Pair resolveAsConstant(Expr expr, Context context, Ha
 					values.put(e.getKey(), v.first());
 					types.put(e.getKey(), v.second());
 				}
-				return new Pair(Constant.V_RECORD(values), Nominal.Record(false, types));
+				return new Pair(new Constant.Record(values), Nominal.Record(false, types));
 			} else if (expr instanceof Expr.FunctionOrMethod) {
 				// TODO: add support for proper lambdas
 				Expr.FunctionOrMethod f = (Expr.FunctionOrMethod) expr;
-				return new Pair(Constant.V_LAMBDA(f.nid, f.type.nominal()), f.type);
+				return new Pair(new Constant.Lambda(f.nid, f.type.nominal()), f.type);
 			}
 		} catch (SyntaxError.InternalFailure e) {
 			throw e;
@@ -3036,21 +3036,21 @@ private Constant evaluate(Expr.UnOp operator, Constant operand, Context context)
 		case NOT:
 			if (operand instanceof Constant.Bool) {
 				Constant.Bool b = (Constant.Bool) operand;
-				return Constant.V_BOOL(!b.value);
+				return Constant.Bool(!b.value());
 			}
 			syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, operator);
 			break;
 		case NEG:
 			if (operand instanceof Constant.Integer) {
 				Constant.Integer b = (Constant.Integer) operand;
-				return Constant.V_INTEGER(b.value.negate());
+				return new Constant.Integer(b.value().negate());
 			} 
 			syntaxError(errorMessage(INVALID_NUMERIC_EXPRESSION), context, operator);
 			break;
 		case INVERT:
 			if (operand instanceof Constant.Byte) {
 				Constant.Byte b = (Constant.Byte) operand;
-				return Constant.V_BYTE((byte) ~b.value);
+				return new Constant.Byte((byte) ~b.value());
 			}
 			break;
 		}
@@ -3079,11 +3079,11 @@ private Constant evaluate(Expr.BinOp bop, Constant v1, Constant v2, Context cont
 	private Constant evaluateBoolean(Expr.BinOp bop, Constant.Bool v1, Constant.Bool v2, Context context) {
 		switch (bop.op) {
 		case AND:
-			return Constant.V_BOOL(v1.value & v2.value);
+			return Constant.Bool(v1.value() & v2.value());
 		case OR:
-			return Constant.V_BOOL(v1.value | v2.value);
+			return Constant.Bool(v1.value() | v2.value());
 		case XOR:
-			return Constant.V_BOOL(v1.value ^ v2.value);
+			return Constant.Bool(v1.value() ^ v2.value());
 		}
 		syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, bop);
 		return null;
@@ -3092,15 +3092,15 @@ private Constant evaluateBoolean(Expr.BinOp bop, Constant.Bool v1, Constant.Bool
 	private Constant evaluate(Expr.BinOp bop, Constant.Integer v1, Constant.Integer v2, Context context) {
 		switch (bop.op) {
 		case ADD:
-			return Constant.V_INTEGER(v1.value.add(v2.value));
+			return new Constant.Integer(v1.value().add(v2.value()));
 		case SUB:
-			return Constant.V_INTEGER(v1.value.subtract(v2.value));
+			return new Constant.Integer(v1.value().subtract(v2.value()));
 		case MUL:
-			return Constant.V_INTEGER(v1.value.multiply(v2.value));
+			return new Constant.Integer(v1.value().multiply(v2.value()));
 		case DIV:
-			return Constant.V_INTEGER(v1.value.divide(v2.value));
+			return new Constant.Integer(v1.value().divide(v2.value()));
 		case REM:
-			return Constant.V_INTEGER(v1.value.remainder(v2.value));
+			return new Constant.Integer(v1.value().remainder(v2.value()));
 		}
 		syntaxError(errorMessage(INVALID_NUMERIC_EXPRESSION), context, bop);
 		return null;
@@ -3110,10 +3110,10 @@ private Constant.Array evaluate(Expr.ArrayGenerator bop, Constant element, Const
 		if (count instanceof Constant.Integer) {
 			Constant.Integer c = (Constant.Integer) count;
 			ArrayList items = new ArrayList();
-			for (int i = 0; i != c.value.intValue(); ++i) {
+			for (int i = 0; i != c.value().intValue(); ++i) {
 				items.add(element);
 			}
-			return Constant.V_ARRAY(items);
+			return new Constant.Array(items);
 		}
 		syntaxError(errorMessage(INVALID_ARRAY_EXPRESSION), context, bop);
 		return null;
diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java
index 54df62a686..178a8bab1c 100644
--- a/modules/wyc/src/wyc/io/WhileyFileParser.java
+++ b/modules/wyc/src/wyc/io/WhileyFileParser.java
@@ -2378,32 +2378,32 @@ private Expr parseTermExpression(WhileyFile wf,
 						start, index - 1));
 			}
 		case Null:
-			return new Expr.Constant(wyil.lang.Constant.V_NULL, sourceAttr(
+			return new Expr.Constant(wyil.lang.Constant.Null, sourceAttr(
 					start, index++));
 		case True:
-			return new Expr.Constant(wyil.lang.Constant.V_BOOL(true),
+			return new Expr.Constant(Constant.True,
 					sourceAttr(start, index++));
 		case False:
-			return new Expr.Constant(wyil.lang.Constant.V_BOOL(false),
+			return new Expr.Constant(Constant.False,
 					sourceAttr(start, index++));
 		case ByteValue: {
 			byte val = parseByte(token);
-			return new Expr.Constant(wyil.lang.Constant.V_BYTE(val),
+			return new Expr.Constant(new Constant.Byte(val),
 					sourceAttr(start, index++));
 		}
 		case CharValue: {
 			BigInteger c = parseCharacter(token.text);
-			return new Expr.Constant(wyil.lang.Constant.V_INTEGER(c), sourceAttr(
+			return new Expr.Constant(new Constant.Integer(c), sourceAttr(
 					start, index++));
 		}
 		case IntValue: {
 			BigInteger val = new BigInteger(token.text);
-			return new Expr.Constant(wyil.lang.Constant.V_INTEGER(val),
+			return new Expr.Constant(new Constant.Integer(val),
 					sourceAttr(start, index++));
 		}
 		case StringValue: {
 			List str = parseString(token.text);
-			return new Expr.Constant(wyil.lang.Constant.V_ARRAY(str),
+			return new Expr.Constant(new Constant.Array(str),
 					sourceAttr(start, index++));
 		}
 		case Minus:
@@ -4252,11 +4252,11 @@ protected List parseString(String v) {
 					default:
 						throw new RuntimeException("unknown escape character");
 					}
-					result.add(Constant.V_INTEGER(BigInteger.valueOf(replace)));
+					result.add(new Constant.Integer(BigInteger.valueOf(replace)));
 					i = i + 1;
 				}
 			} else {
-				result.add(Constant.V_INTEGER(BigInteger.valueOf(v.charAt(i))));
+				result.add(new Constant.Integer(BigInteger.valueOf(v.charAt(i))));
 			}
 		}
 		return result;
diff --git a/modules/wyil/src/wyil/builders/VcBranch.java b/modules/wyil/src/wyil/builders/VcBranch.java
index 6571f283c5..8f5111ae08 100644
--- a/modules/wyil/src/wyil/builders/VcBranch.java
+++ b/modules/wyil/src/wyil/builders/VcBranch.java
@@ -127,7 +127,7 @@ public enum State {
 	/**
 	 * The bytecode index into the above block that this branch is currently at.
 	 */
-	private CodeForest.Index pc;
+	private BytecodeForest.Index pc;
 
 	/**
 	 * Indicates the state of this branch. In particular, whether its active,
@@ -148,7 +148,7 @@ public enum State {
 	 *            --- Variable names to use as prefixes when generating register
 	 *            names. If null, the default names are used instead.
 	 */
-	public VcBranch(int numInputs, CodeForest.Index pc, String[] prefixes) {
+	public VcBranch(int numInputs, BytecodeForest.Index pc, String[] prefixes) {
 		int numSlots = numInputs;
 		this.parents = new VcBranch[0];
 		this.environment = new Expr[numSlots];
@@ -216,7 +216,7 @@ private VcBranch(VcBranch[] parents, Expr[] environment,
 	 *
 	 * @return
 	 */
-	public CodeForest.Index pc() {
+	public BytecodeForest.Index pc() {
 		return pc;
 	}
 
@@ -285,7 +285,7 @@ public String[] prefixes() {
 	 * 
 	 * @param pc
 	 */
-	public void goTo(CodeForest.Index pc) {
+	public void goTo(BytecodeForest.Index pc) {
 		if (state != State.ACTIVE) {
 			// Sanity check
 			throw new IllegalArgumentException(
diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java
index 4226904c0d..655925f7b4 100644
--- a/modules/wyil/src/wyil/builders/VcExprGenerator.java
+++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java
@@ -21,7 +21,7 @@
 import wyfs.lang.Path;
 import wyfs.util.Trie;
 import wyil.lang.Bytecode;
-import wyil.lang.CodeForest;
+import wyil.lang.BytecodeForest;
 import wyil.lang.Type;
 import wyil.lang.WyilFile;
 import wyil.util.ErrorMessages;
@@ -47,7 +47,7 @@ public VcExprGenerator(String filename, Builder builder, VcUtils utils) {
 	 * @param branch
 	 *            The branch on entry to the bytecode.
 	 */
-	public void transform(Bytecode code, CodeForest forest, VcBranch branch) {
+	public void transform(Bytecode code, BytecodeForest forest, VcBranch branch) {
 		try {
 			if (code instanceof Bytecode.Operator) {
 				transform((Bytecode.Operator) code, forest, branch);				
@@ -120,7 +120,7 @@ public void transform(Bytecode code, CodeForest forest, VcBranch branch) {
 			null // right shift
 	};
 
-	protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch branch) {
+	protected void transform(Bytecode.Operator code, BytecodeForest forest, VcBranch branch) {
 		switch(code.kind()) {
 		case ASSIGN:
 			for (int i = 0; i != code.operands().length; ++i) {
@@ -182,24 +182,24 @@ protected void transform(Bytecode.Operator code, CodeForest forest, VcBranch bra
 		}
 	}
 	
-	protected void transform(Bytecode.Convert code, CodeForest forest, VcBranch branch) {
+	protected void transform(Bytecode.Convert code, BytecodeForest forest, VcBranch branch) {
 		Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes());
 		Expr result = branch.read(code.operand(0));
 		SyntacticType type = utils.convert(code.result(), forest.get(branch.pc()).attributes());
 		branch.write(code.target(0), new Expr.Cast(type, result, attributes));
 	}
 
-	protected void transform(Bytecode.Const code, CodeForest forest, VcBranch branch) {
+	protected void transform(Bytecode.Const code, BytecodeForest forest, VcBranch branch) {
 		Value val = utils.convert(code.constant(), forest, branch);
 		branch.write(code.target(), new Expr.Constant(val, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())));
 	}
 
-	protected void transform(Bytecode.Debug code, CodeForest forest,
+	protected void transform(Bytecode.Debug code, BytecodeForest forest,
 			VcBranch branch) {
 		// do nout
 	}
 
-	protected void transform(Bytecode.FieldLoad code, CodeForest forest, VcBranch branch) {
+	protected void transform(Bytecode.FieldLoad code, BytecodeForest forest, VcBranch branch) {
 		Type.EffectiveRecord er = (Type.EffectiveRecord) code.type(0); 
 		ArrayList fields = new ArrayList(er.fields().keySet());
 		Collections.sort(fields);
@@ -210,13 +210,13 @@ protected void transform(Bytecode.FieldLoad code, CodeForest forest, VcBranch br
 	}
 
 	protected void transform(Bytecode.IndirectInvoke code,
-			CodeForest forest, VcBranch branch) {
+			BytecodeForest forest, VcBranch branch) {
 		for(int target : code.targets()) {
 			branch.havoc(target);
 		}
 	}
 
-	protected void transform(Bytecode.Invoke code, CodeForest forest,
+	protected void transform(Bytecode.Invoke code, BytecodeForest forest,
 			VcBranch branch) throws Exception {
 		Collection attributes =  forest.get(branch.pc()).attributes();
 		Collection wyccAttributes = VcUtils.toWycsAttributes(attributes);
@@ -268,7 +268,7 @@ protected void transform(Bytecode.Invoke code, CodeForest forest,
 		}
 	}
 
-	protected void transformArrayGenerator(Bytecode.Operator code, CodeForest forest, VcBranch branch) {
+	protected void transformArrayGenerator(Bytecode.Operator code, BytecodeForest forest, VcBranch branch) {
 		Type elementType = ((Type.Array) code.type(0)).element();
 		Collection wyilAttributes = forest.get(branch.pc()).attributes();
 		Collection attributes = VcUtils.toWycsAttributes(wyilAttributes);
@@ -283,12 +283,12 @@ protected void transformArrayGenerator(Bytecode.Operator code, CodeForest forest
 		branch.assume(macro);
 	}
 	
-	protected void transform(Bytecode.Lambda code, CodeForest forest, VcBranch branch) {
+	protected void transform(Bytecode.Lambda code, BytecodeForest forest, VcBranch branch) {
 		// TODO: implement lambdas somehow?
 		branch.havoc(code.target(0));
 	}
 
-	protected void transform(Bytecode.Update code, CodeForest forest, VcBranch branch) {
+	protected void transform(Bytecode.Update code, BytecodeForest forest, VcBranch branch) {
 		Expr result = branch.read(code.result());
 		Expr oldSource = branch.read(code.target(0));
 		Expr newSource = branch.havoc(code.target(0));
@@ -296,7 +296,7 @@ protected void transform(Bytecode.Update code, CodeForest forest, VcBranch branc
 	}
 
 	protected void updateHelper(Iterator iter, Expr oldSource, Expr newSource, Expr result, VcBranch branch,
-			CodeForest forest) {
+			BytecodeForest forest) {
 		Collection attributes = VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes());
 		if (!iter.hasNext()) {
 			branch.assume(new Expr.Binary(Expr.Binary.Op.EQ, newSource, result, attributes));
@@ -346,7 +346,7 @@ protected void updateHelper(Iterator iter, Expr oldSource, Expr n
 	 *            --- The enclosing branch
 	 */
 	protected void transformUnary(Expr.Unary.Op operator, Bytecode code, VcBranch branch,
-			CodeForest forest) {
+			BytecodeForest forest) {
 		Expr lhs = branch.read(code.operand(0));
 		branch.write(code.target(0),
 				new Expr.Unary(operator, lhs, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())));
@@ -366,7 +366,7 @@ protected void transformUnary(Expr.Unary.Op operator, Bytecode code, VcBranch br
 	 *            --- The enclosing branch
 	 */
 	protected void transformBinary(Expr.Binary.Op operator, Bytecode code, VcBranch branch,
-			CodeForest forest) {
+			BytecodeForest forest) {
 		Expr lhs = branch.read(code.operand(0));
 		Expr rhs = branch.read(code.operand(1));
 
@@ -395,7 +395,7 @@ protected void transformBinary(Expr.Binary.Op operator, Bytecode code, VcBranch
 	 *            --- The enclosing branch
 	 */
 	protected void transformNary(Expr.Nary.Op operator, Bytecode code, VcBranch branch,
-			CodeForest forest) {
+			BytecodeForest forest) {
 		int[] code_operands = code.operands();
 		Expr[] vals = new Expr[code_operands.length];
 		for (int i = 0; i != vals.length; ++i) {
@@ -421,7 +421,7 @@ protected void transformNary(Expr.Nary.Op operator, Bytecode code, VcBranch bran
 	 * @throws Exception
 	 */
 	private int countPostconditions(NameID name, Type.FunctionOrMethod fun,
-			CodeForest forest, VcBranch branch) throws Exception {
+			BytecodeForest forest, VcBranch branch) throws Exception {
 		Path.Entry e = builder.project().get(name.module(), WyilFile.ContentType);
 		if (e == null) {
 			syntaxError(errorMessage(ErrorMessages.RESOLUTION_ERROR, name.module().toString()), filename,
diff --git a/modules/wyil/src/wyil/builders/VcGenerator.java b/modules/wyil/src/wyil/builders/VcGenerator.java
index 9db7210f63..a6efbd17c5 100644
--- a/modules/wyil/src/wyil/builders/VcGenerator.java
+++ b/modules/wyil/src/wyil/builders/VcGenerator.java
@@ -39,7 +39,7 @@
 import wyil.attributes.VariableDeclarations;
 import wyil.builders.VcBranch.State;
 import wyil.lang.*;
-import wyil.lang.CodeForest.Index;
+import wyil.lang.BytecodeForest.Index;
 import wyil.util.ErrorMessages;
 import wyil.util.TypeExpander;
 import wycc.lang.Attribute;
@@ -118,12 +118,12 @@ protected void transform(WyilFile.Constant decl, WyilFile wyilFile) {
 	 * @param wyilFile
 	 */
 	protected void transform(WyilFile.Type typeDecl, WyilFile wyilFile) {
-		CodeForest forest = typeDecl.invariant();
+		BytecodeForest forest = typeDecl.invariant();
 		Expr invariant = null;
 		// FIXME: get the register prefix!
 		Expr.Variable var = new Expr.Variable("r0");
 		if (forest.numBlocks() > 0) {
-			CodeForest.Index root = new CodeForest.Index(forest.getRoot(0), 0);
+			BytecodeForest.Index root = new BytecodeForest.Index(forest.getRoot(0), 0);
 			VcBranch master = new VcBranch(Math.max(1, forest.numRegisters()), root, null);
 			master.write(0, var);
 			// Pass the given branch through the type invariant, producing
@@ -155,7 +155,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) {
 		this.method = method;
 
 		Type.FunctionOrMethod fmm = method.type();
-		CodeForest forest = method.code();
+		BytecodeForest forest = method.code();
 		int[] preconditions = method.preconditions();
 		int[] postconditions = method.postconditions();				
 		// First, translate pre- and post-conditions into macro blocks. These
@@ -164,13 +164,13 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) {
 		// an invocation site, we can call this macro directly.
 		String prefix = method.name() + "_requires_";
 		for (int i = 0; i != preconditions.length; ++i) {
-			CodeForest.Index pc = new CodeForest.Index(preconditions[i], 0);
+			BytecodeForest.Index pc = new BytecodeForest.Index(preconditions[i], 0);
 			buildMacroBlock(prefix + i, pc, forest, fmm.params(), true);
 		}
 		prefix = method.name() + "_ensures_";
 		List postEnvironment = append(fmm.params(), fmm.returns());		
 		for (int i = 0; i != postconditions.length; ++i) {
-			CodeForest.Index pc = new CodeForest.Index(postconditions[i], 0);
+			BytecodeForest.Index pc = new BytecodeForest.Index(postconditions[i], 0);
 			buildMacroBlock(prefix + i, pc, forest, postEnvironment, true);
 		}
 
@@ -191,7 +191,7 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) {
 		// declared types in the master branch. The master branch needs to have
 		// at least as many slots as there are parameters, though may require
 		// more if the body uses them.
-		CodeForest.Index pc = new CodeForest.Index(method.body(), 0);
+		BytecodeForest.Index pc = new BytecodeForest.Index(method.body(), 0);
 		VcBranch master = new VcBranch(Math.max(forest.numRegisters(), fmm.params().size()), pc, prefixes);
 
 		Expr[] arguments = new Expr[fmm.params().size()];
@@ -319,10 +319,10 @@ protected void transform(WyilFile.FunctionOrMethod method, WyilFile wyilFile) {
 	 * 
 	 * @return List of branches which reach the end of the block.
 	 */
-	public List transform(VcBranch branch, CodeForest.Index root, boolean isInvariant, Type[] environment,
-			CodeForest forest) {
+	public List transform(VcBranch branch, BytecodeForest.Index root, boolean isInvariant, Type[] environment,
+			BytecodeForest forest) {
 		// Construct the label map which is needed for conditional branches
-		Map labels = Bytecode.buildLabelMap(forest);
+		Map labels = Bytecode.buildLabelMap(forest);
 		Pair> p = transform(root.block(), 0, null, branch, false, isInvariant, environment,
 				labels, forest);
 		// Ok, return list of exit branches
@@ -375,12 +375,12 @@ public List transform(VcBranch branch, CodeForest.Index root, boolean
 	 *         this is marked as terminated.
 	 * 
 	 */
-	protected Pair> transform(int block, int offset, CodeForest.Index parent,
+	protected Pair> transform(int block, int offset, BytecodeForest.Index parent,
 			VcBranch entryState, boolean breakOnInvariant, boolean isInvariant, Type[] environment,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		// Move state to correct location
-		entryState.goTo(new CodeForest.Index(block, offset));
-		CodeForest.Block blk = forest.get(block);
+		entryState.goTo(new BytecodeForest.Index(block, offset));
+		BytecodeForest.Block blk = forest.get(block);
 		// Construct list of branches being processed.
 		Stack worklist = new Stack();
 		ArrayList exitBranches = new ArrayList();
@@ -393,7 +393,7 @@ protected Pair> transform(int block, int offset, CodeFo
 
 			// The program counter represents the current position of the branch
 			// being explored.
-			CodeForest.Index pc = branch.pc();
+			BytecodeForest.Index pc = branch.pc();
 			// Determine whether to continue executing this branch, or whether
 			// it has completed within this scope.
 			if (pc.block() != block || branch.state() != VcBranch.State.ACTIVE) {
@@ -532,7 +532,7 @@ private void joinAll(ArrayList branches) {
 		for (int i = 0; i < branches.size(); ++i) {
 			VcBranch i_branch = branches.get(i);
 			if (i_branch != null) {
-				CodeForest.Index i_pc = i_branch.pc();
+				BytecodeForest.Index i_pc = i_branch.pc();
 				// Now, the goal is to identify all remaining branches which are
 				// at the same location. These can then be all joined together
 				// in one go. First, we count how many their are
@@ -616,8 +616,8 @@ private void joinAll(ArrayList branches) {
 	 *            location information.
 	 */
 	protected List transform(Bytecode.Loop code, VcBranch branch,
-			Type[] environment, Map labels,
-			CodeForest forest) {
+			Type[] environment, Map labels,
+			BytecodeForest forest) {
 		return transformLoopHelper(code, branch, environment, labels, forest).second();
 	}
 
@@ -651,7 +651,7 @@ protected List transform(Bytecode.Loop code, VcBranch branch,
 	 */
 	protected List transform(Bytecode.Quantify code, VcBranch branch,
 			boolean isInvariant, Type[] environment,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		// Write an arbitrary value to the index operand. This is necessary to
 		// ensure that there is something there if it is used within the loop
 		// body.
@@ -707,7 +707,7 @@ protected List extractQuantifiers(Bytecode.Quantify code,
 			VcBranch b = exitBranches.get(i);
 			Expr body = generateAssumptions(b, root);
 			body = new Expr.Binary(Expr.Binary.Op.AND, range, body);
-			CodeForest.Index target = b.pc();
+			BytecodeForest.Index target = b.pc();
 			b = root.fork();
 			b.assume(new Expr.Exists(pattern, body));
 			b.goTo(target);
@@ -736,10 +736,10 @@ protected List extractQuantifiers(Bytecode.Quantify code,
 	 */
 	protected Pair> transformQuantifierHelper(
 			Bytecode.Loop code, VcBranch branch, boolean isInvariant,
-			Type[] environment, Map labels,
-			CodeForest forest) {
+			Type[] environment, Map labels,
+			BytecodeForest forest) {
 		// The loopPc gives the block index of the loop bytecode.
-		CodeForest.Index loopPc = branch.pc();
+		BytecodeForest.Index loopPc = branch.pc();
 		// This is the easy case, as there is no loop invariant. Therefore,
 		// we just havoc modified variables at the beginning and then allow
 		// branches to exit the loop as normal. Branches which reach the end
@@ -774,9 +774,9 @@ protected Pair> transformQuantifierHelper(
 	 * @return
 	 */
 	protected Pair> transformLoopHelper(Bytecode.Loop code, VcBranch branch, Type[] environment,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		// The loopPc gives the block index of the loop bytecode.
-		CodeForest.Index loopPc = branch.pc();
+		BytecodeForest.Index loopPc = branch.pc();
 		int invariantOffset = getInvariantOffset(code,forest);
 
 		// First thing we need to do is determine whether or not this loop has a
@@ -787,14 +787,14 @@ protected Pair> transformLoopHelper(Bytecode.Loop code,
 			// Determine how many invariant blocks there are, as there might be
 			// more than one. In the case that there is more than one, they are
 			// assumed to be arranged consecutively one after the other.
-			CodeForest.Block block = forest.get(code.block());
+			BytecodeForest.Block block = forest.get(code.block());
 			int numberOfInvariants = 0;
 			for (int i = invariantOffset; i < block.size()
 					&& block.get(i).first() instanceof Bytecode.Invariant; ++i) {
 				numberOfInvariants = numberOfInvariants+1;
 			}
 			//
-			CodeForest.Index firstInvariantPc = new CodeForest.Index(code.block(), invariantOffset);
+			BytecodeForest.Index firstInvariantPc = new BytecodeForest.Index(code.block(), invariantOffset);
 			String invariantMacroPrefix = method.name() + "_loopinvariant_";
 			
 			// FIXME: this is a hack to determine which variables should be
@@ -831,7 +831,7 @@ protected Pair> transformLoopHelper(Bytecode.Loop code,
 			// verification condition that asserts each invariant macro given the
 			// current branch state.
 			for (int i = 0; i != numberOfInvariants; ++i) {
-				CodeForest.Index invariantPc = firstInvariantPc.next(i);
+				BytecodeForest.Index invariantPc = firstInvariantPc.next(i);
 				String invariantMacroName = invariantMacroPrefix + invariantPc.toString().replace(":", "_");
 				Expr.Invoke invariant = buildInvariantCall(activeBranch, invariantMacroName, variables);
 				Expr vc = buildVerificationCondition(invariant, activeBranch, environment, forest);
@@ -844,7 +844,7 @@ protected Pair> transformLoopHelper(Bytecode.Loop code,
 			// the invariant macro holds in the current branch state.
 			havocVariables(code.modifiedOperands(), activeBranch);
 			for (int i = 0; i != numberOfInvariants; ++i) {
-				CodeForest.Index invariantPc = firstInvariantPc.next(i);
+				BytecodeForest.Index invariantPc = firstInvariantPc.next(i);
 				String invariantMacroName = invariantMacroPrefix
 						+ invariantPc.toString().replace(":", "_");
 				Expr.Invoke invariant = buildInvariantCall(activeBranch, invariantMacroName,
@@ -864,7 +864,7 @@ protected Pair> transformLoopHelper(Bytecode.Loop code,
 			// verification condition that asserts the invariant macro given the
 			// current branch state.
 			for (int i = 0; i != numberOfInvariants; ++i) {
-				CodeForest.Index invariantPc = firstInvariantPc.next(i);
+				BytecodeForest.Index invariantPc = firstInvariantPc.next(i);
 				String invariantMacroName = invariantMacroPrefix
 						+ invariantPc.toString().replace(":", "_");
 				Expr.Invoke invariant = buildInvariantCall(activeBranch,
@@ -902,8 +902,8 @@ protected Pair> transformLoopHelper(Bytecode.Loop code,
 	 */
 	protected Pair> transformLoopWithoutInvariant(
 			Bytecode.Loop code, VcBranch branch, Type[] environment,
-			Map labels, CodeForest forest) {
-		CodeForest.Index loopPc = branch.pc();
+			Map labels, BytecodeForest forest) {
+		BytecodeForest.Index loopPc = branch.pc();
 		// This is the easy case, as there is no loop invariant. Therefore,
 		// we just havoc modified variables at the beginning and then allow
 		// branches to exit the loop as normal. Branches which reach the end
@@ -941,8 +941,8 @@ protected Pair> transformLoopWithoutInvariant(
 	 * @param block
 	 *            The enclosing code block
 	 */
-	private void buildInvariantMacro(CodeForest.Index invariantPC,
-			boolean[] variables, Type[] environment, CodeForest forest) {
+	private void buildInvariantMacro(BytecodeForest.Index invariantPC,
+			boolean[] variables, Type[] environment, BytecodeForest forest) {
 		Bytecode.Invariant code = (Bytecode.Invariant) forest.get(invariantPC).first();
 		// FIXME: we don't need to include all variables, only those which are
 		// "active".
@@ -955,7 +955,7 @@ private void buildInvariantMacro(CodeForest.Index invariantPC,
 			}
 		}
 		String pc = invariantPC.block() + "_" + invariantPC.offset();
-		CodeForest.Index root = new CodeForest.Index(code.block(),0);
+		BytecodeForest.Index root = new BytecodeForest.Index(code.block(),0);
 		buildMacroBlock(method.name() + "_loopinvariant_" + pc, root, forest, types, true);
 	}
 
@@ -996,8 +996,8 @@ protected Expr.Invoke buildInvariantCall(VcBranch branch, String name,
 	 * @param branch
 	 * @return
 	 */
-	private int getInvariantOffset(Bytecode.Loop loop, CodeForest forest) {
-		CodeForest.Block block = forest.get(loop.block());
+	private int getInvariantOffset(Bytecode.Loop loop, BytecodeForest forest) {
+		BytecodeForest.Block block = forest.get(loop.block());
 		for (int i = 0; i != block.size(); ++i) {
 			if (block.get(i).first() instanceof Bytecode.Invariant) {
 				return i;
@@ -1050,7 +1050,7 @@ public void havocVariables(int[] variables, VcBranch branch) {
 	 *            The list of branches currently being managed.
 	 */
 	protected List transform(Bytecode.If code, VcBranch branch,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		// First, clone and register the true branch
 		VcBranch trueBranch = branch.fork();
 		VcBranch falseBranch = branch.fork();
@@ -1087,7 +1087,7 @@ protected List transform(Bytecode.If code, VcBranch branch,
 	 *            The list of branches currently being managed.
 	 */
 	protected List transform(Bytecode.IfIs code, VcBranch branch,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		ArrayList exitBranches = new ArrayList();
 		// In this case, both branches are reachable.
 		// First, clone and register the branch
@@ -1128,19 +1128,19 @@ protected List transform(Bytecode.IfIs code, VcBranch branch,
 	 *            The list of branches currently being managed.
 	 */
 	protected List transform(Bytecode.Switch code, VcBranch branch,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		ArrayList exitBranches = new ArrayList();
 		VcBranch defaultBranch = branch.fork();
 
 		// Process each case individually, whilst also updating the default
 		// branch.
-		for (int i = 0; i != code.branches.size(); ++i) {
+		for (int i = 0; i != code.branches().size(); ++i) {
 			// First, for each case fork a new branch to traverse it.
 			VcBranch caseBranch = branch.fork();
 			// Second, for each case, assume that the variable switched on
 			// matches the give case value. Likewise, assume that the default
 			// branch does *not* equal this value.
-			Constant caseValue = code.branches.get(i).first();
+			Constant caseValue = code.branches().get(i).first();
 			// Second, on the new branch we need assume that the variable being
 			// switched on matches the given value.
 			Expr src = branch.read(code.operand(0));
@@ -1153,12 +1153,12 @@ protected List transform(Bytecode.Switch code, VcBranch branch,
 			defaultBranch.assume(new Expr.Binary(Expr.Binary.Op.NEQ, src,
 					constant, VcUtils.toWycsAttributes(forest.get(branch.pc()).attributes())));
 			// Finally, dispatch branch
-			caseBranch.goTo(labels.get(code.branches.get(i).second()));
+			caseBranch.goTo(labels.get(code.branches().get(i).second()));
 			exitBranches.add(caseBranch);
 		}
 
 		// Finally, the dispatch the default branch to the default target.
-		defaultBranch.goTo(labels.get(code.defaultTarget));
+		defaultBranch.goTo(labels.get(code.defaultTarget()));
 		exitBranches.add(defaultBranch);
 
 		// TODO: here is where we can add a coverage check. Specifically, that
@@ -1194,14 +1194,14 @@ protected List transform(Bytecode.Switch code, VcBranch branch,
 	 */
 	protected Pair> transform(
 			Bytecode.AssertOrAssume code, boolean isAssert, VcBranch branch,
-			Type[] environment, Map labels,
-			CodeForest forest) {
+			Type[] environment, Map labels,
+			BytecodeForest forest) {
 		int start = wyalFile.declarations().size();
 		// First, transform the given branch through the assert or assume block.
 		// This will produce one or more exit branches, some of which may have
 		// reached failed states and need to be turned into verification
 		// conditions (for asserts only).
-		CodeForest.Index pc = branch.pc();
+		BytecodeForest.Index pc = branch.pc();
 		Pair> p = transform(code.block(), 0, pc, branch, false, true, environment, labels,
 				forest);
 		List exitBranches = p.second();
@@ -1249,7 +1249,7 @@ protected Pair> transform(
 	 *            The list of branches currently being managed.
 	 */
 	protected void transform(Bytecode.Goto code, final VcBranch branch,
-			Map labels, CodeForest forest) {
+			Map labels, BytecodeForest forest) {
 		branch.goTo(labels.get(code.destination()));
 	}
 
@@ -1270,7 +1270,7 @@ protected void transform(Bytecode.Goto code, final VcBranch branch,
 	 *            The list of branches currently being managed.
 	 */
 	protected void transform(Bytecode.Fail code, VcBranch branch,
-			CodeForest forest) {
+			BytecodeForest forest) {
 		// Update status of this branch to failed. This simply indicates that
 		// this branch's location should be unreachable and, hence, we need a
 		// verification condition to enforce this.
@@ -1317,8 +1317,8 @@ protected void transform(Bytecode.Return code, VcBranch branch) {
 	 *            the inputs of the block being translated.
 	 * @return
 	 */
-	protected void buildMacroBlock(String name, CodeForest.Index root,
-			CodeForest forest, List types, boolean isInvariant) {
+	protected void buildMacroBlock(String name, BytecodeForest.Index root,
+			BytecodeForest forest, List types, boolean isInvariant) {
 		int start = wyalFile.declarations().size();
 		
 		// first, generate a branch for traversing the external block.
@@ -1397,7 +1397,7 @@ protected void buildMacroBlock(String name, CodeForest.Index root,
 	 * @return
 	 */
 	protected Expr buildVerificationCondition(Expr assertion, VcBranch branch,
-			Type[] environment, CodeForest forest, Expr... extraAssumptions) {
+			Type[] environment, BytecodeForest forest, Expr... extraAssumptions) {
 		// First construct the assertion which forms the basis of the
 		// verification condition. The assertion must be shown to hold assuming
 		// the assumptions did. Therefore, we construct an implication to
@@ -1564,7 +1564,7 @@ private Expr generateAssumptionsHelper(VcBranch b, VcBranch end) {
 	 * @param elem
 	 * @return
 	 */
-	private Expr.Binary buildCondition(Expr test, CodeForest forest, VcBranch branch) {
+	private Expr.Binary buildCondition(Expr test, BytecodeForest forest, VcBranch branch) {
 		if (test instanceof Expr.Binary) {
 			return (Expr.Binary) test;
 		} else {
diff --git a/modules/wyil/src/wyil/builders/VcUtils.java b/modules/wyil/src/wyil/builders/VcUtils.java
index ba091688d4..83afc7ff18 100644
--- a/modules/wyil/src/wyil/builders/VcUtils.java
+++ b/modules/wyil/src/wyil/builders/VcUtils.java
@@ -27,7 +27,7 @@
 import wyfs.lang.Path;
 import wyil.attributes.VariableDeclarations;
 import wyil.lang.Bytecode;
-import wyil.lang.CodeForest;
+import wyil.lang.BytecodeForest;
 import wyil.lang.Constant;
 import wyil.lang.Type;
 import wyil.lang.WyilFile;
@@ -60,21 +60,21 @@ public VcUtils(String filename, Builder builder, TypeExpander expander) {
 	 *            (for debugging purposes)
 	 * @return
 	 */
-	public Value convert(Constant c, CodeForest forest, VcBranch branch) {
+	public Value convert(Constant c, BytecodeForest forest, VcBranch branch) {
 		if (c instanceof Constant.Null) {
 			return wycs.core.Value.Null;
 		} else if (c instanceof Constant.Bool) {
 			Constant.Bool cb = (Constant.Bool) c;
-			return wycs.core.Value.Bool(cb.value);
+			return wycs.core.Value.Bool(cb.value());
 		} else if (c instanceof Constant.Byte) {
 			Constant.Byte cb = (Constant.Byte) c;
-			return wycs.core.Value.Integer(BigInteger.valueOf(cb.value));
+			return wycs.core.Value.Integer(BigInteger.valueOf(cb.value()));
 		} else if (c instanceof Constant.Integer) {
 			Constant.Integer cb = (Constant.Integer) c;
-			return wycs.core.Value.Integer(cb.value);
+			return wycs.core.Value.Integer(cb.value());
 		} else if (c instanceof Constant.Array) {
 			Constant.Array cb = (Constant.Array) c;
-			List cb_values = cb.values;
+			List cb_values = cb.values();
 			ArrayList items = new ArrayList();
 			for (int i = 0; i != cb_values.size(); ++i) {
 				items.add(convert(cb_values.get(i), forest, branch));				
@@ -90,11 +90,11 @@ public Value convert(Constant c, CodeForest forest, VcBranch branch) {
 			// a general solution. In particular, it would seem to be brokwn for
 			// type testing.
 
-			ArrayList fields = new ArrayList(rb.values.keySet());
+			ArrayList fields = new ArrayList(rb.values().keySet());
 			Collections.sort(fields);
 			ArrayList values = new ArrayList();
 			for (String field : fields) {
-				values.add(convert(rb.values.get(field), forest, branch));
+				values.add(convert(rb.values().get(field), forest, branch));
 			}
 			return wycs.core.Value.Tuple(values);
 		} else {
@@ -361,7 +361,7 @@ public boolean containsNominal(Type t,
 	 * @param block
 	 */
 	public Pair[] getPreconditions(Bytecode code, VcBranch branch,
-			Type[] environment, CodeForest forest) {
+			Type[] environment, BytecodeForest forest) {
 		//
 		try {
 			switch (code.opcode()) {
@@ -473,7 +473,7 @@ public Pair[] arrayGeneratorChecks(Bytecode.Operator code, VcBranch
 	 * @throws Exception
 	 */
 	public Pair[] preconditionCheck(Bytecode.Invoke code, VcBranch branch,
-			Type[] environment, CodeForest forest) throws Exception {
+			Type[] environment, BytecodeForest forest) throws Exception {
 		ArrayList> preconditions = new ArrayList<>();
 		//
 		// First, check for any potentially constrained types.    
@@ -580,7 +580,7 @@ public Pair[] updateChecks(Bytecode.Update code, VcBranch branch) {
 	 * @throws Exception
 	 */
 	public int countPreconditions(NameID name, Type.FunctionOrMethod fun,
-			CodeForest forest, VcBranch branch) throws Exception {
+			BytecodeForest forest, VcBranch branch) throws Exception {
 		Path.Entry e = builder.project().get(name.module(), WyilFile.ContentType);
 		if (e == null) {
 			syntaxError(errorMessage(ErrorMessages.RESOLUTION_ERROR, name.module().toString()), filename,
@@ -654,12 +654,12 @@ private Type expand(Type t, Collection attributes) {
 	 * @param d
 	 * @return
 	 */
-	public static Pair parseRegisterDeclarations(CodeForest forest) {
-		List regs = forest.registers();
+	public static Pair parseRegisterDeclarations(BytecodeForest forest) {
+		List regs = forest.registers();
 		String[] prefixes = new String[regs.size()];
 		Type[] types = new Type[regs.size()];
 		for (int i = 0; i != prefixes.length; ++i) {
-			CodeForest.Register d = regs.get(i);			
+			BytecodeForest.Register d = regs.get(i);			
 			prefixes[i] = d.name();
 			types[i] = d.type();
 		}
diff --git a/modules/wyil/src/wyil/checks/CoercionCheck.java b/modules/wyil/src/wyil/checks/CoercionCheck.java
index 87e9ac3040..c2a3fe164d 100755
--- a/modules/wyil/src/wyil/checks/CoercionCheck.java
+++ b/modules/wyil/src/wyil/checks/CoercionCheck.java
@@ -87,17 +87,17 @@ public void apply(WyilFile module) {
 	}
 
 	public void check(WyilFile.FunctionOrMethod method) {
-		CodeForest forest = method.code();
+		BytecodeForest forest = method.code();
 		for(int i=0;i!=forest.numBlocks();++i) {
 			check(i, forest, method);
 		}
 	}
 
-	protected void check(int blockID, CodeForest forest, WyilFile.FunctionOrMethod method) {
+	protected void check(int blockID, BytecodeForest forest, WyilFile.FunctionOrMethod method) {
 		// Examine all entries in this block looking for a conversion bytecode
-		CodeForest.Block block = forest.get(blockID);
+		BytecodeForest.Block block = forest.get(blockID);
 		for (int i = 0; i != block.size(); ++i) {
-			CodeForest.Entry e = block.get(i);
+			BytecodeForest.Entry e = block.get(i);
 			Bytecode code = e.code();
 			if (code instanceof Bytecode.Convert) {
 				Bytecode.Convert conv = (Bytecode.Convert) code;
diff --git a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java
index 317eeac21a..1eec61fc0f 100755
--- a/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java
+++ b/modules/wyil/src/wyil/checks/DefiniteAssignmentCheck.java
@@ -36,7 +36,7 @@
 import wyil.attributes.SourceLocation;
 import wyil.lang.*;
 import static wycc.lang.SyntaxError.*;
-import static wyil.lang.CodeForest.*;
+import static wyil.lang.BytecodeForest.*;
 import static wyil.util.ErrorMessages.*;
 
 /**
@@ -102,7 +102,7 @@ public HashSet initialStore() {
 	}
 
 	@Override
-	public HashSet propagate(CodeForest.Index index, Bytecode code, HashSet in) {
+	public HashSet propagate(BytecodeForest.Index index, Bytecode code, HashSet in) {
 		checkUses(index, code, in);
 
 		int[] defs = defs(code);
@@ -118,7 +118,7 @@ public HashSet propagate(CodeForest.Index index, Bytecode code, HashSet
 	}
 
 	@Override
-	public Pair, HashSet> propagate(CodeForest.Index index,
+	public Pair, HashSet> propagate(BytecodeForest.Index index,
 			Bytecode.If igoto, HashSet in) {
 
 		if (!in.contains(igoto.operand(0))) {
@@ -130,7 +130,7 @@ public Pair, HashSet> propagate(CodeForest.Index index
 	}
 
 	@Override
-	public Pair, HashSet> propagate(CodeForest.Index index, Bytecode.IfIs iftype,
+	public Pair, HashSet> propagate(BytecodeForest.Index index, Bytecode.IfIs iftype,
 			HashSet in) {
 
 		if (!in.contains(iftype.operand(0))) {
@@ -142,7 +142,7 @@ public Pair, HashSet> propagate(CodeForest.Index index
 	}
 
 	@Override
-	public List> propagate(CodeForest.Index index, Bytecode.Switch sw, HashSet in) {
+	public List> propagate(BytecodeForest.Index index, Bytecode.Switch sw, HashSet in) {
 
 		if (!in.contains(sw.operand(0))) {
 			syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename,
@@ -150,14 +150,14 @@ public List> propagate(CodeForest.Index index, Bytecode.Switch
 		}
 
 		ArrayList> stores = new ArrayList();
-		for (int i = 0; i != sw.branches.size(); ++i) {
+		for (int i = 0; i != sw.branches().size(); ++i) {
 			stores.add(in);
 		}
 		return stores;
 	}
 
 	@Override
-	public HashSet propagate(CodeForest.Index index, Bytecode.Loop loop, HashSet in) {
+	public HashSet propagate(BytecodeForest.Index index, Bytecode.Loop loop, HashSet in) {
 		if (loop instanceof Bytecode.Quantify) {
 			Bytecode.Quantify fall = (Bytecode.Quantify) loop;
 
@@ -185,7 +185,7 @@ protected HashSet join(HashSet s1, HashSet s2) {
 		return r;
 	}
 
-	public void checkUses(CodeForest.Index index, Bytecode code, HashSet in) {
+	public void checkUses(BytecodeForest.Index index, Bytecode code, HashSet in) {
 
 		for (int operand : code.operands()) {
 			if (!in.contains(operand)) {
diff --git a/modules/wyil/src/wyil/checks/ModuleCheck.java b/modules/wyil/src/wyil/checks/ModuleCheck.java
index 6ec6f0f14c..473c5851f2 100755
--- a/modules/wyil/src/wyil/checks/ModuleCheck.java
+++ b/modules/wyil/src/wyil/checks/ModuleCheck.java
@@ -33,7 +33,7 @@
 import wycc.util.Pair;
 import wyil.attributes.SourceLocation;
 import wyil.lang.*;
-import wyil.lang.CodeForest.Index;
+import wyil.lang.BytecodeForest.Index;
 import wyil.lang.Bytecode.*;
 import static wyil.util.ErrorMessages.*;
 
@@ -128,10 +128,10 @@ protected void checkFunctionPure(WyilFile.FunctionOrMethod c) {
 		checkFunctionPure(c.body(),c.code());
 	}
 
-	protected void checkFunctionPure(int blockID, CodeForest forest) {
-		CodeForest.Block block = forest.get(blockID);
+	protected void checkFunctionPure(int blockID, BytecodeForest forest) {
+		BytecodeForest.Block block = forest.get(blockID);
 		for (int i = 0; i != block.size(); ++i) {
-			CodeForest.Entry e = block.get(i);
+			BytecodeForest.Entry e = block.get(i);
 			Bytecode code = e.first();			
 			if(code instanceof Bytecode.Invoke && ((Bytecode.Invoke)code).type(0) instanceof Type.Method) {
 				// internal message send
diff --git a/modules/wyil/src/wyil/io/WyilFilePrinter.java b/modules/wyil/src/wyil/io/WyilFilePrinter.java
index de4aba836f..317a16bb8e 100755
--- a/modules/wyil/src/wyil/io/WyilFilePrinter.java
+++ b/modules/wyil/src/wyil/io/WyilFilePrinter.java
@@ -125,7 +125,7 @@ public void apply(WyilFile module) throws IOException {
 			t_str = t.toString();
 			writeModifiers(td.modifiers(),out);
 			out.println("type " + td.name() + " : " + t_str);						
-			CodeForest forest = td.invariant();
+			BytecodeForest forest = td.invariant();
 			for(int i=0;i!=forest.numRoots();++i) {
 				out.println("where:");
 				write(0, forest.getRoot(i), forest, out);
@@ -142,7 +142,7 @@ public void apply(WyilFile module) throws IOException {
 	}
 
 	private void write(FunctionOrMethod method, PrintWriter out) {
-		CodeForest forest = method.code();
+		BytecodeForest forest = method.code();
 		//
 		writeModifiers(method.modifiers(), out);
 		Type.FunctionOrMethod ft = method.type();
@@ -186,8 +186,8 @@ private void writeParameters(List parameters, PrintWriter out) {
 		out.print(")");
 	}
 	
-	private void write(int indent, int blockID, CodeForest forest, PrintWriter out) {
-		CodeForest.Block block = forest.get(blockID);
+	private void write(int indent, int blockID, BytecodeForest forest, PrintWriter out) {
+		BytecodeForest.Block block = forest.get(blockID);
 		for(int i=0;i!=block.size();++i) {
 			Bytecode code = block.get(i).code();
 			if(code instanceof Bytecode.Label) {
@@ -198,7 +198,7 @@ private void write(int indent, int blockID, CodeForest forest, PrintWriter out)
 		}
 	}
 
-	private void write(int indent, Bytecode c, CodeForest forest, PrintWriter out) {
+	private void write(int indent, Bytecode c, BytecodeForest forest, PrintWriter out) {
 		String line = "null";
 		tabIndent(indent+1,out);
 
diff --git a/modules/wyil/src/wyil/io/WyilFileReader.java b/modules/wyil/src/wyil/io/WyilFileReader.java
index 529af9dbb0..28a5231031 100644
--- a/modules/wyil/src/wyil/io/WyilFileReader.java
+++ b/modules/wyil/src/wyil/io/WyilFileReader.java
@@ -242,17 +242,17 @@ private void readConstantPool(int count) throws IOException {
 
 			switch (code) {
 			case WyilFileWriter.CONSTANT_Null:
-				constant = Constant.V_NULL;
+				constant = Constant.Null;
 				break;
 			case WyilFileWriter.CONSTANT_False:
-				constant = Constant.V_BOOL(false);
+				constant = Constant.False;
 				break;
 			case WyilFileWriter.CONSTANT_True:
-				constant = Constant.V_BOOL(true);
+				constant = Constant.True;
 				break;
 			case WyilFileWriter.CONSTANT_Byte: {
 				byte val = (byte) input.read_u8();
-				constant = Constant.V_BYTE(val);
+				constant = new Constant.Byte(val);
 				break;
 			}
 			case WyilFileWriter.CONSTANT_Int: {
@@ -260,7 +260,7 @@ private void readConstantPool(int count) throws IOException {
 				byte[] bytes = new byte[len];
 				input.read(bytes);
 				BigInteger bi = new BigInteger(bytes);
-				constant = Constant.V_INTEGER(bi);
+				constant = new Constant.Integer(bi);
 				break;
 			}
 			case WyilFileWriter.CONSTANT_Array: {
@@ -270,7 +270,7 @@ private void readConstantPool(int count) throws IOException {
 					int index = input.read_uv();
 					values.add(myConstantPool[index]);
 				}
-				constant = Constant.V_ARRAY(values);
+				constant = new Constant.Array(values);
 				break;
 			}
 			case WyilFileWriter.CONSTANT_Record: {
@@ -282,7 +282,7 @@ private void readConstantPool(int count) throws IOException {
 					String str = stringPool[fieldIndex];
 					tvs.put(str, myConstantPool[constantIndex]);
 				}
-				constant = Constant.V_RECORD(tvs);
+				constant = new Constant.Record(tvs);
 				break;
 			}
 			case WyilFileWriter.CONSTANT_Function:
@@ -291,7 +291,7 @@ private void readConstantPool(int count) throws IOException {
 				int nameIndex = input.read_uv();
 				Type.FunctionOrMethod t = (Type.FunctionOrMethod) typePool[typeIndex];
 				NameID name = namePool[nameIndex];
-				constant = Constant.V_LAMBDA(name, t);
+				constant = new Constant.Lambda(name, t);
 				break;
 			}
 			default:
@@ -461,7 +461,7 @@ private WyilFile.Type readTypeBlock() throws IOException {
 		int modifiers = input.read_uv();
 		int typeIdx = input.read_uv();
 
-		CodeForest forest = readCodeForestBlock();
+		BytecodeForest forest = readCodeForestBlock();
 		
 		return new WyilFile.Type(generateModifiers(modifiers), stringPool[nameIdx], typePool[typeIdx], forest);
 	}
@@ -511,7 +511,7 @@ private WyilFile.FunctionOrMethod readFunctionOrMethodBlock() throws IOException
 
 		Type.FunctionOrMethod type = (Type.FunctionOrMethod) typePool[typeIdx];
 
-		CodeForest forest = readCodeForestBlock();
+		BytecodeForest forest = readCodeForestBlock();
 
 		return new WyilFile.FunctionOrMethod(generateModifiers(modifiers), stringPool[nameIdx], type, forest,
 				nRequires, nEnsures);
@@ -591,7 +591,7 @@ private Collection generateModifiers(int modifiers) {
 	 * @param output
 	 * @throws IOException
 	 */
-	private CodeForest readCodeForestBlock() throws IOException {
+	private BytecodeForest readCodeForestBlock() throws IOException {
 		input.pad_u8();
 		int kind = input.read_uv(); // unused
 		int size = input.read_uv(); // unused
@@ -602,10 +602,10 @@ private CodeForest readCodeForestBlock() throws IOException {
 		int nRoots = input.read_uv();
 		int nAttrs = input.read_uv();
 		
-		CodeForest forest = new CodeForest();
+		BytecodeForest forest = new BytecodeForest();
 		
 		for(int i=0;i!=nRegs;++i) {
-			CodeForest.Register register = readCodeRegister();
+			BytecodeForest.Register register = readCodeRegister();
 			forest.registers().add(register);
 		}
 		
@@ -618,7 +618,7 @@ private CodeForest readCodeForestBlock() throws IOException {
 		
 		int offset = 0;
 		for(int i=0;i!=nBlocks;++i) {
-			CodeForest.Block block = readCodeBlock(offset,labels);
+			BytecodeForest.Block block = readCodeBlock(offset,labels);
 			forest.add(block);
 			offset += block.size();
 		}
@@ -647,11 +647,11 @@ private CodeForest readCodeForestBlock() throws IOException {
 	 * @param output
 	 * @throws IOException
 	 */
-	private CodeForest.Register readCodeRegister() throws IOException {
+	private BytecodeForest.Register readCodeRegister() throws IOException {
 		int nAttrs = input.read_uv();
 		int typeIdx = input.read_uv();
 		// TODO: read any attributes given
-		return new CodeForest.Register(typePool[typeIdx], "unknown");
+		return new BytecodeForest.Register(typePool[typeIdx], "unknown");
 	}
 	
 	/**
@@ -672,10 +672,10 @@ private CodeForest.Register readCodeRegister() throws IOException {
 	 *            The map of offsets to labels being inserted.
 	 * @return
 	 */
-	private int insertLabels(CodeForest forest, HashMap labels) {
+	private int insertLabels(BytecodeForest forest, HashMap labels) {
 		int offset = 0;
 		for (int i = 0; i != forest.numBlocks(); ++i) {
-			CodeForest.Block block = forest.get(i);
+			BytecodeForest.Block block = forest.get(i);
 			for (int j = 0; j != block.size(); ++j) {
 				// First, check whether there is a label to insert
 				Bytecode.Label label = labels.get(offset++);
@@ -717,18 +717,18 @@ private int insertLabels(CodeForest forest, HashMap lab
 	 * @return
 	 * @throws IOException
 	 */
-	public CodeForest.Block readCodeBlock(int offset, HashMap labels)
+	public BytecodeForest.Block readCodeBlock(int offset, HashMap labels)
 			throws IOException {
 		int nCodes = input.read_uv();
 		int nAttrs = input.read_uv();
 
-		ArrayList bytecodes = new ArrayList();
+		ArrayList bytecodes = new ArrayList();
 		for (int i = 0; i < nCodes; ++i) {
 			Bytecode code = readBytecode(i + offset, labels);
-			bytecodes.add(new CodeForest.Entry(code));
+			bytecodes.add(new BytecodeForest.Entry(code));
 		}
 		// TODO: read any attributes given
-		return new CodeForest.Block(bytecodes);
+		return new BytecodeForest.Block(bytecodes);
 	}
 
 	private Bytecode readBytecode(int offset, HashMap labels) throws IOException {
diff --git a/modules/wyil/src/wyil/io/WyilFileWriter.java b/modules/wyil/src/wyil/io/WyilFileWriter.java
index 5eb2e61fb8..5fe14996e6 100644
--- a/modules/wyil/src/wyil/io/WyilFileWriter.java
+++ b/modules/wyil/src/wyil/io/WyilFileWriter.java
@@ -132,7 +132,7 @@ private void writeBlock(int kind, Object data, BinaryOutputStream output) throws
 			bytes = generateFunctionOrMethodBlock((WyilFile.FunctionOrMethod) data);
 			break;
 		case BLOCK_CodeForest:
-			bytes = generateCodeForest((CodeForest) data);
+			bytes = generateCodeForest((BytecodeForest) data);
 			break;
 		}
 
@@ -283,14 +283,14 @@ private void writeConstantPool(BinaryOutputStream output) throws IOException {
 				output.write_uv(CONSTANT_Null);
 			} else if (val instanceof Constant.Bool) {
 				Constant.Bool b = (Constant.Bool) val;
-				output.write_uv(b.value ? CONSTANT_True : CONSTANT_False);
+				output.write_uv(b.value() ? CONSTANT_True : CONSTANT_False);
 			} else if (val instanceof Constant.Byte) {
 				Constant.Byte b = (Constant.Byte) val;
 				output.write_uv(CONSTANT_Byte);
-				output.write_u8(b.value);
+				output.write_u8(b.value());
 			} else if (val instanceof Constant.Integer) {
 				Constant.Integer i = (Constant.Integer) val;
-				BigInteger num = i.value;
+				BigInteger num = i.value();
 				byte[] numbytes = num.toByteArray();
 				output.write_uv(CONSTANT_Int);
 				output.write_uv(numbytes.length);
@@ -298,16 +298,16 @@ private void writeConstantPool(BinaryOutputStream output) throws IOException {
 			} else if (val instanceof Constant.Array) {
 				Constant.Array s = (Constant.Array) val;
 				output.write_uv(CONSTANT_Array);
-				output.write_uv(s.values.size());
-				for (Constant v : s.values) {
+				output.write_uv(s.values().size());
+				for (Constant v : s.values()) {
 					int index = constantCache.get(v);
 					output.write_uv(index);
 				}
 			} else if (val instanceof Constant.Record) {
 				Constant.Record r = (Constant.Record) val;
 				output.write_uv(CONSTANT_Record);
-				output.write_uv(r.values.size());
-				for (java.util.Map.Entry v : r.values.entrySet()) {
+				output.write_uv(r.values().size());
+				for (java.util.Map.Entry v : r.values().entrySet()) {
 					output.write_uv(stringCache.get(v.getKey()));
 					int index = constantCache.get(v.getValue());
 					output.write_uv(index);
@@ -317,7 +317,7 @@ private void writeConstantPool(BinaryOutputStream output) throws IOException {
 				Type.FunctionOrMethod t = fm.type();
 				output.write_uv(t instanceof Type.Function ? CONSTANT_Function : CONSTANT_Method);
 				output.write_uv(typeCache.get(t));
-				output.write_uv(nameCache.get(fm.name));
+				output.write_uv(nameCache.get(fm.name()));
 			} else {
 				throw new RuntimeException("Unknown value encountered - " + val);
 			}
@@ -533,7 +533,7 @@ private byte[] generateFunctionOrMethodBlock(WyilFile.FunctionOrMethod md) throw
 	 *            The set of pre-calculated label offsets
 	 * @throws IOException
 	 */
-	private byte[] generateCodeForest(CodeForest forest) throws IOException {
+	private byte[] generateCodeForest(BytecodeForest forest) throws IOException {
 		ByteArrayOutputStream bytes = new ByteArrayOutputStream();
 		BinaryOutputStream output = new BinaryOutputStream(bytes);
 
@@ -543,7 +543,7 @@ private byte[] generateCodeForest(CodeForest forest) throws IOException {
 		output.write_uv(forest.numRoots());
 		output.write_uv(0); // currently no attributes
 
-		List registers = forest.registers();
+		List registers = forest.registers();
 		for (int i = 0; i != registers.size(); ++i) {
 			writeCodeRegister(registers.get(i), output);
 		}
@@ -556,7 +556,7 @@ private byte[] generateCodeForest(CodeForest forest) throws IOException {
 
 		int offset = 0;
 		for (int i = 0; i != forest.numBlocks(); ++i) {
-			CodeForest.Block block = forest.get(i);
+			BytecodeForest.Block block = forest.get(i);
 			offset = writeCodeBlock(block, offset, labels, output);
 		}
 
@@ -588,11 +588,11 @@ private byte[] generateCodeForest(CodeForest forest) throws IOException {
 	 *            Lab map being constructed
 	 * @return
 	 */
-	private HashMap buildLabelsMap(CodeForest forest) {
+	private HashMap buildLabelsMap(BytecodeForest forest) {
 		HashMap labels = new HashMap();
 		int offset = 0;
 		for (int i = 0; i != forest.numBlocks(); ++i) {
-			CodeForest.Block block = forest.get(i);
+			BytecodeForest.Block block = forest.get(i);
 			for (int j = 0; j != block.size(); ++j) {
 				Bytecode code = block.get(j).code();
 				if (code instanceof Bytecode.Label) {
@@ -625,7 +625,7 @@ private HashMap buildLabelsMap(CodeForest forest) {
 	 * @param output
 	 * @throws  
 	 */
-	private void writeCodeRegister(CodeForest.Register register, BinaryOutputStream output) throws IOException {
+	private void writeCodeRegister(BytecodeForest.Register register, BinaryOutputStream output) throws IOException {
 		// TODO: write out register attributes (including name)
 		output.write_uv(0);
 		// Write out the type index
@@ -664,7 +664,7 @@ private void writeCodeRegister(CodeForest.Register register, BinaryOutputStream
 	 * @return
 	 * @throws IOException
 	 */
-	private int writeCodeBlock(CodeForest.Block block, int offset, HashMap labels,
+	private int writeCodeBlock(BytecodeForest.Block block, int offset, HashMap labels,
 			BinaryOutputStream output) throws IOException {
 		// First, determine how many labels there are in this block (since
 		// labels are not real bytecodes)
@@ -830,8 +830,8 @@ private void writeRest(Bytecode code, int offset, HashMap label
 			}
 		} else if (code instanceof Bytecode.Switch) {
 			Bytecode.Switch c = (Bytecode.Switch) code;
-			List> branches = c.branches;
-			int target = labels.get(c.defaultTarget);
+			List> branches = c.branches();
+			int target = labels.get(c.defaultTarget());
 			output.write_uv(target);
 			output.write_uv(branches.size());
 			for (Pair b : branches) {
@@ -867,7 +867,7 @@ private int generateModifiers(Collection modifiers) {
 	 * @param block
 	 * @return
 	 */
-	private int countLabels(CodeForest.Block block) {
+	private int countLabels(BytecodeForest.Block block) {
 		int nlabels = 0;
 		for (int i = 0; i != block.size(); ++i) {
 			Bytecode code = block.get(i).code();
@@ -930,7 +930,7 @@ private void buildPools(WyilFile.FunctionOrMethod declaration) {
 		buildPools(declaration.code());		
 	}
 
-	private void buildPools(CodeForest forest) {		
+	private void buildPools(BytecodeForest forest) {		
 		for(int i=0;i!=forest.numRegisters();++i) {
 			buildPools(forest.getRegister(i));
 		}
@@ -939,13 +939,13 @@ private void buildPools(CodeForest forest) {
 		}
 	}
 
-	private void buildPools(CodeForest.Register reg) {
+	private void buildPools(BytecodeForest.Register reg) {
 		addTypeItem(reg.type());
 	}
 	
-	private void buildPools(CodeForest.Block block) {
+	private void buildPools(BytecodeForest.Block block) {
 		for (int i = 0; i != block.size(); ++i) {
-			CodeForest.Entry entry = block.get(i);
+			BytecodeForest.Entry entry = block.get(i);
 			buildPools(entry.code());			
 			// TODO: handle entry attributes
 		}
@@ -976,7 +976,7 @@ private void buildPools(Bytecode code) {
 			}
 		} else if (code instanceof Bytecode.Switch) {
 			Bytecode.Switch s = (Bytecode.Switch) code;
-			for (Pair b : s.branches) {
+			for (Pair b : s.branches()) {
 				addConstantItem(b.first());
 			}
 		}
@@ -1062,19 +1062,19 @@ private int addConstantItem(Constant v) {
 	private void addConstantSubitems(Constant v) {
 		if (v instanceof Constant.Array) {
 			Constant.Array l = (Constant.Array) v;
-			for (Constant e : l.values) {
+			for (Constant e : l.values()) {
 				addConstantItem(e);
 			}
 		} else if (v instanceof Constant.Record) {
 			Constant.Record r = (Constant.Record) v;
-			for (Map.Entry e : r.values.entrySet()) {
+			for (Map.Entry e : r.values().entrySet()) {
 				addStringItem(e.getKey());
 				addConstantItem(e.getValue());
 			}
 		} else if (v instanceof Constant.Lambda) {
 			Constant.Lambda fm = (Constant.Lambda) v;
 			addTypeItem(fm.type());
-			addNameItem(fm.name);
+			addNameItem(fm.name());
 		}
 	}
 
diff --git a/modules/wyil/src/wyil/lang/Bytecode.java b/modules/wyil/src/wyil/lang/Bytecode.java
index 5c4629365b..7d941799ca 100755
--- a/modules/wyil/src/wyil/lang/Bytecode.java
+++ b/modules/wyil/src/wyil/lang/Bytecode.java
@@ -28,10 +28,7 @@
 import java.util.*;
 
 import wycc.lang.NameID;
-import wycc.lang.SyntacticElement;
 import wycc.util.Pair;
-import wyil.lang.Bytecode.Branching;
-import wyil.lang.Bytecode.Compound;
 
 /**
  * Represents a WyIL bytecode. The Whiley Intermediate Language (WyIL) employs
@@ -1810,8 +1807,8 @@ public String toString() {
 	 *
 	 */
 	public static final class Switch extends Bytecode {
-		public final ArrayList> branches;
-		public final String defaultTarget;
+		private final ArrayList> branches;
+		private final String defaultTarget;
 
 		public Switch(Type type, int operand, String defaultTarget, Collection> branches) {
 			super(new Type[] { type }, new int[0], operand);
@@ -1826,7 +1823,7 @@ public int opcode() {
 
 		public Switch relabel(Map labels) {
 			ArrayList> nbranches = new ArrayList();
-			for (Pair p : branches) {
+			for (Pair p : branches()) {
 				String nlabel = labels.get(p.second());
 				if (nlabel == null) {
 					nbranches.add(p);
@@ -1835,9 +1832,9 @@ public Switch relabel(Map labels) {
 				}
 			}
 
-			String nlabel = labels.get(defaultTarget);
+			String nlabel = labels.get(defaultTarget());
 			if (nlabel == null) {
-				return new Switch(types[0], operands[0], defaultTarget, nbranches);
+				return new Switch(types[0], operands[0], defaultTarget(), nbranches);
 			} else {
 				return new Switch(types[0], operands[0], nlabel, nbranches);
 			}
@@ -1846,8 +1843,8 @@ public Switch relabel(Map labels) {
 		public boolean equals(Object o) {
 			if (o instanceof Switch) {
 				Switch ig = (Switch) o;
-				return operands[0] == ig.operands[0] && defaultTarget.equals(ig.defaultTarget)
-						&& branches.equals(ig.branches) && types[0].equals(ig.types[0]);
+				return operands[0] == ig.operands[0] && defaultTarget().equals(ig.defaultTarget())
+						&& branches().equals(ig.branches()) && types[0].equals(ig.types[0]);
 			}
 			return false;
 		}
@@ -1855,23 +1852,31 @@ public boolean equals(Object o) {
 		public String toString() {
 			String table = "";
 			boolean firstTime = true;
-			for (Pair p : branches) {
+			for (Pair p : branches()) {
 				if (!firstTime) {
 					table += ", ";
 				}
 				firstTime = false;
 				table += p.first() + "->" + p.second();
 			}
-			table += ", *->" + defaultTarget;
+			table += ", *->" + defaultTarget();
 			return "switch %" + operands[0] + " " + table;
 		}
+
+		
+		public String defaultTarget() {
+			return defaultTarget;
+		}
+
+		public ArrayList> branches() {
+			return branches;
+		}
 	}
 
 	// =============================================================
 	// Helpers
 	// =============================================================
 
-
 	/**
 	 * Construct a mapping from labels to their block indices within a root
 	 * block. This is useful so they can easily be resolved during the
@@ -1880,16 +1885,16 @@ public String toString() {
 	 * @param block
 	 * @return
 	 */
-	public static Map buildLabelMap(CodeForest forest) {
-		HashMap labels = new HashMap();
+	public static Map buildLabelMap(BytecodeForest forest) {
+		HashMap labels = new HashMap();
 		for (int i = 0; i != forest.numBlocks(); ++i) {
-			CodeForest.Block block = forest.get(i);
+			BytecodeForest.Block block = forest.get(i);
 			for (int j = 0; j != block.size(); ++j) {
 				Bytecode code = block.get(j).code();
 				if (code instanceof Bytecode.Label) {
 					// Found a label, so register it in the labels map
 					Bytecode.Label label = (Bytecode.Label) code;
-					labels.put(label.label(), new CodeForest.Index(i, j));
+					labels.put(label.label(), new BytecodeForest.Index(i, j));
 				}
 			}
 		}
diff --git a/modules/wyil/src/wyil/lang/CodeForest.java b/modules/wyil/src/wyil/lang/BytecodeForest.java
similarity index 96%
rename from modules/wyil/src/wyil/lang/CodeForest.java
rename to modules/wyil/src/wyil/lang/BytecodeForest.java
index e1e161d1c2..ce5deb407f 100644
--- a/modules/wyil/src/wyil/lang/CodeForest.java
+++ b/modules/wyil/src/wyil/lang/BytecodeForest.java
@@ -12,7 +12,7 @@
 import wycc.util.Pair;
 
 /**
- * A CodeBlock can be thought of in different ways. For example, it can be
+ * A bytecode forest can be thought of in different ways. For example, it can be
  * thought of as a forest of rooted trees; or, more simply, as an array of
  * bytecode sequences. Some bytecodes can be thought of as compound structures
  * containing nested blocks of bytecodes. In reality, such compound bytecodes
@@ -22,16 +22,16 @@
  * @author David J. Pearce
  *
  */
-public class CodeForest {
+public class BytecodeForest {
 	private final ArrayList registers;
 	private final ArrayList roots;
 	private final ArrayList blocks;
 
-	public CodeForest() {
+	public BytecodeForest() {
 		this(Collections.EMPTY_LIST);
 	}
 	
-	public CodeForest(CodeForest forest) {
+	public BytecodeForest(BytecodeForest forest) {
 		this.registers = new ArrayList(forest.registers);
 		this.roots = new ArrayList(forest.roots);
 		this.blocks = new ArrayList();
@@ -40,7 +40,7 @@ public CodeForest(CodeForest forest) {
 		}		
 	}
 	
-	public CodeForest(List registers) {
+	public BytecodeForest(List registers) {
 		this.registers = new ArrayList(registers);
 		this.roots = new ArrayList();
 		this.blocks = new ArrayList();
diff --git a/modules/wyil/src/wyil/lang/Constant.java b/modules/wyil/src/wyil/lang/Constant.java
index 54f675c25e..ea3a38ccc9 100755
--- a/modules/wyil/src/wyil/lang/Constant.java
+++ b/modules/wyil/src/wyil/lang/Constant.java
@@ -25,50 +25,40 @@
 
 package wyil.lang;
 
-import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.*;
 
 import wycc.lang.NameID;
-import wycc.util.Pair;
-import wyautl.util.BigRational;
 
 public abstract class Constant implements Comparable {
 
-	public static final Null V_NULL = new Null();
+	/**
+	 * The Null constant value
+	 */
+	public static final Null Null = new Null();
 
-	public abstract wyil.lang.Type type();
-
-	public static Bool V_BOOL(boolean value) {
-		return get(new Bool(value));
-	}
-
-	public static Byte V_BYTE(byte value) {
-		return get(new Byte(value));
-	}
-
-	public static Integer V_INTEGER(BigInteger value) {
-		return get(new Integer(value));
-	}
-
-	public static Array V_ARRAY(Collection values) {
-		return get(new Array(values));
-	}
-
-	public static Record V_RECORD(java.util.Map values) {
-		return get(new Record(values));
-	}
-
-	public static Type V_TYPE(wyil.lang.Type type) {
-		return get(new Type(type));
-	}
-
-	public static Lambda V_LAMBDA(NameID name,
-			wyil.lang.Type.FunctionOrMethod type, Constant... arguments) {
-		return get(new Lambda(name, type, arguments));
+	/**
+	 * The Bool true constant
+	 */
+	public static final Bool True = new Constant.Bool(true);
+	
+	/**
+	 * The Bool false constant
+	 */
+	public static final Bool False = new Constant.Bool(false);
+	
+	/**
+	 * Get the appropriate Bool constant corresponding to a Java boolean.
+	 * 
+	 * @param f
+	 * @return
+	 */
+	public static final Bool Bool(boolean f) {
+		return f ? True : False;
 	}
-
 	
+	public abstract wyil.lang.Type type();
+
 	public static final class Null extends Constant {
 		public wyil.lang.Type type() {
 			return wyil.lang.Type.T_NULL;
@@ -92,7 +82,7 @@ public int compareTo(Constant v) {
 	}
 
 	public static final class Bool extends Constant {
-		public final boolean value;
+		private final boolean value;
 		private Bool(boolean value) {
 			this.value = value;
 		}
@@ -128,11 +118,14 @@ public String toString() {
 				return "false";
 			}
 		}
+		public boolean value() {
+			return value;
+		}
 	}
 
 	public static final class Byte extends Constant {
-		public final byte value;
-		private Byte(byte value) {
+		private final byte value;
+		public Byte(byte value) {
 			this.value = value;
 		}
 		public wyil.lang.Type type() {
@@ -176,11 +169,14 @@ public String toString() {
 			}
 			return r;
 		}
+		public byte value() {
+			return value;
+		}
 	}
 
 	public static final class Integer extends Constant {
-		public final BigInteger value;
-		private Integer(BigInteger value) {
+		private final BigInteger value;
+		public Integer(BigInteger value) {
 			this.value = value;
 		}
 		public wyil.lang.Type type() {
@@ -208,30 +204,14 @@ public int compareTo(Constant v) {
 		public String toString() {
 			return value.toString();
 		}
-
-		public Constant.Integer add(Constant.Integer val) {
-			return Constant.V_INTEGER(value.add(val.value));
-		}
-		public Constant.Integer subtract(Constant.Integer val) {
-			return Constant.V_INTEGER(value.subtract(val.value));
-		}
-		public Constant.Integer multiply(Constant.Integer val) {
-			return Constant.V_INTEGER(value.multiply(val.value));
-		}
-		public Constant.Integer divide(Constant.Integer val) {
-			return Constant.V_INTEGER(value.divide(val.value));
-		}
-		public Constant.Integer remainder(Constant.Integer val) {
-			return Constant.V_INTEGER(value.remainder(val.value));
-		}
-		public Constant.Integer negate() {
-			return Constant.V_INTEGER(value.negate());
+		public BigInteger value() {
+			return value;
 		}
 	}
 
 	public static final class Array extends Constant {
-		public final ArrayList values;
-		private Array(Collection value) {
+		private final ArrayList values;
+		public Array(Collection value) {
 			this.values = new ArrayList(value);
 		}
 		public wyil.lang.Type.Array type() {
@@ -285,11 +265,14 @@ public String toString() {
 			}
 			return r + "]";
 		}
+		public ArrayList values() {
+			return values;
+		}
 	}
 
 	public static final class Record extends Constant {
-		public final HashMap values;
-		private Record(java.util.Map value) {
+		private final HashMap values;
+		public Record(java.util.Map value) {
 			this.values = new HashMap(value);
 		}
 
@@ -355,23 +338,27 @@ public String toString() {
 			}
 			return r + "}";
 		}
+
+		public HashMap values() {
+			return values;
+		}
 	}
 
 	public static final class Type extends Constant {
-		public final wyil.lang.Type type;
-		private Type(wyil.lang.Type type) {
-			this.type = type;
+		private final wyil.lang.Type value;
+		public Type(wyil.lang.Type type) {
+			this.value = type;
 		}
 		public wyil.lang.Type.Meta type() {
 			return wyil.lang.Type.T_META;
 		}
 		public int hashCode() {
-			return type.hashCode();
+			return value.hashCode();
 		}
 		public boolean equals(Object o) {
 			if(o instanceof Type) {
 				Type i = (Type) o;
-				return type == i.type;
+				return value == i.value;
 			}
 			return false;
 		}
@@ -379,22 +366,25 @@ public int compareTo(Constant v) {
 			if(v instanceof Type) {
 				Type t = (Type) v;
 				// FIXME: following is an ugly hack!
-				return type.toString().compareTo(t.toString());
+				return value.toString().compareTo(t.toString());
 			} else {
 				return 1; // everything is above a type constant
 			}
 		}
 		public String toString() {
-			return type.toString();
+			return value.toString();
+		}
+		public wyil.lang.Type value() {
+			return value;
 		}
 	}
 
 	public static final class Lambda extends Constant {
-		public final NameID name;
-		public final wyil.lang.Type.FunctionOrMethod type;
-		public final ArrayList arguments;
+		private final NameID name;
+		private final wyil.lang.Type.FunctionOrMethod type;
+		private final ArrayList arguments;
 		
-		private Lambda(NameID name, wyil.lang.Type.FunctionOrMethod type, Constant... arguments) {
+		public Lambda(NameID name, wyil.lang.Type.FunctionOrMethod type, Constant... arguments) {
 			this.name = name;
 			this.type = type;
 			this.arguments = new ArrayList();
@@ -403,7 +393,7 @@ private Lambda(NameID name, wyil.lang.Type.FunctionOrMethod type, Constant... ar
 			}
 		}
 
-		private Lambda(NameID name, wyil.lang.Type.FunctionOrMethod type, Collection arguments) {
+		public Lambda(NameID name, wyil.lang.Type.FunctionOrMethod type, Collection arguments) {
 			this.name = name;
 			this.type = type;
 			this.arguments = new ArrayList(arguments);
@@ -458,19 +448,13 @@ public String toString() {
 			}
 			return "&" + name.toString() + "(" + args + "):" + type.toString();
 		}
-	}
 
-	private static final ArrayList values = new ArrayList();
-	private static final HashMap cache = new HashMap();
+		public NameID name() {
+			return name;
+		}
 
-	private static  T get(T type) {
-		java.lang.Integer idx = cache.get(type);
-		if(idx != null) {
-			return (T) values.get(idx);
-		} else {
-			cache.put(type, values.size());
-			values.add(type);
-			return type;
+		public ArrayList arguments() {
+			return arguments;
 		}
 	}
 }
diff --git a/modules/wyil/src/wyil/lang/WyilFile.java b/modules/wyil/src/wyil/lang/WyilFile.java
index bd40ddd5e3..2eb2018840 100755
--- a/modules/wyil/src/wyil/lang/WyilFile.java
+++ b/modules/wyil/src/wyil/lang/WyilFile.java
@@ -451,16 +451,16 @@ public boolean hasModifier(Modifier modifier) {
 	 */
 	public static final class Type extends Declaration {
 		private wyil.lang.Type type;
-		private CodeForest invariant;
+		private BytecodeForest invariant;
 
-		public Type(Collection modifiers, String name, wyil.lang.Type type, CodeForest invariant,
+		public Type(Collection modifiers, String name, wyil.lang.Type type, BytecodeForest invariant,
 				Attribute... attributes) {
 			super(name, modifiers, attributes);
 			this.type = type;
 			this.invariant = invariant;
 		}
 
-		public Type(Collection modifiers, String name, wyil.lang.Type type, CodeForest invariant,
+		public Type(Collection modifiers, String name, wyil.lang.Type type, BytecodeForest invariant,
 				Collection attributes) {
 			super(name, modifiers, attributes);
 			this.type = type;
@@ -471,7 +471,7 @@ public wyil.lang.Type type() {
 			return type;
 		}
 
-		public CodeForest invariant() {
+		public BytecodeForest invariant() {
 			return invariant;
 		}
 	}
@@ -511,10 +511,10 @@ public static final class FunctionOrMethod extends
 		private wyil.lang.Type.FunctionOrMethod type;
 		private int numPreconditions;
 		private int numPostconditions;
-		private final CodeForest forest;
+		private final BytecodeForest forest;
 		
 		public FunctionOrMethod(Collection modifiers, String name, wyil.lang.Type.FunctionOrMethod type,
-				CodeForest forest, int numPreconditions, int numPostconditions, Attribute... attributes) {
+				BytecodeForest forest, int numPreconditions, int numPostconditions, Attribute... attributes) {
 			super(name, modifiers, attributes);
 			this.type = type;
 			this.forest = forest;
@@ -523,7 +523,7 @@ public FunctionOrMethod(Collection modifiers, String name, wyil.lang.T
 		}
 
 		public FunctionOrMethod(Collection modifiers, String name, wyil.lang.Type.FunctionOrMethod type,
-				CodeForest forest, int numPreconditions, int numPostconditions, Collection attributes) {
+				BytecodeForest forest, int numPreconditions, int numPostconditions, Collection attributes) {
 			super(name, modifiers, attributes);
 			this.type = type;
 			this.forest = forest;
@@ -543,7 +543,7 @@ public boolean isMethod() {
 			return type instanceof wyil.lang.Type.Method;
 		}
 		
-		public CodeForest code() {
+		public BytecodeForest code() {
 			return forest;
 		}
 		
diff --git a/modules/wyil/src/wyil/transforms/LoopVariants.java b/modules/wyil/src/wyil/transforms/LoopVariants.java
index b8f9474374..d04863a850 100644
--- a/modules/wyil/src/wyil/transforms/LoopVariants.java
+++ b/modules/wyil/src/wyil/transforms/LoopVariants.java
@@ -7,7 +7,7 @@
 
 import wybs.lang.Builder;
 import wycc.lang.Transform;
-import wyil.lang.CodeForest;
+import wyil.lang.BytecodeForest;
 import wyil.lang.Bytecode;
 import wyil.lang.Type;
 import wyil.lang.WyilFile;
@@ -84,14 +84,14 @@ public void apply(WyilFile module) {
 	}
 
 	public void infer(WyilFile.Type type) {
-		CodeForest forest = type.invariant();
+		BytecodeForest forest = type.invariant();
 		for(int i=0;i!=forest.numRoots();++i) {
 			infer(forest.getRoot(i),forest);
 		}
 	}
 
 	public void infer(WyilFile.FunctionOrMethod method) {		
-		CodeForest forest = method.code();
+		BytecodeForest forest = method.code();
 		for(int i=0;i!=forest.numRoots();++i) {
 			infer(forest.getRoot(i),forest);
 		}		
@@ -106,12 +106,12 @@ public void infer(WyilFile.FunctionOrMethod method) {
 	 * @param method
 	 * @return
 	 */
-	protected BitSet infer(int blockID, CodeForest forest) {
-		CodeForest.Block block = forest.get(blockID);
+	protected BitSet infer(int blockID, BytecodeForest forest) {
+		BytecodeForest.Block block = forest.get(blockID);
 		BitSet modified = new BitSet(forest.registers().size());
 		int size = block.size();
 		for (int i = 0; i < size; ++i) {
-			CodeForest.Entry e = block.get(i);
+			BytecodeForest.Entry e = block.get(i);
 			Bytecode code = e.code();
 			for (int target : code.targets()) {
 				modified.set(target);
diff --git a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java
index 61d4e27909..0ab88782bb 100755
--- a/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java
+++ b/modules/wyil/src/wyil/util/dfa/BackwardFlowAnalysis.java
@@ -53,7 +53,7 @@ public abstract class BackwardFlowAnalysis {
 	/**
 	 * The root block currently being propagated through.
 	 */
-	protected CodeForest forest;
+	protected BytecodeForest forest;
 
 	/**
 	 * The temporary abstract stores being generated during propagation.
@@ -117,13 +117,13 @@ protected WyilFile.FunctionOrMethod propagate(
 	 * @return
 	 */
 	protected T propagate(int blockID, T store, List> handlers) {
-		CodeForest.Block block = forest.get(blockID);
+		BytecodeForest.Block block = forest.get(blockID);
 		
 		for (int i = block.size()-1; i >= 0; --i) {
 			Bytecode code = block.get(i).code();
 
 			// Construct the bytecode index
-			CodeForest.Index id = new CodeForest.Index(blockID,i);
+			BytecodeForest.Index id = new BytecodeForest.Index(blockID,i);
 
 			try {
 				// First, check for a label which may have incoming information.
@@ -146,11 +146,11 @@ protected T propagate(int blockID, T store, List> handlers) {
 					Bytecode.Switch sw = (Bytecode.Switch) code;
 
 					ArrayList swStores = new ArrayList();
-					for(int j=0;j!=sw.branches.size();++j){
-						String target = sw.branches.get(j).second();
+					for(int j=0;j!=sw.branches().size();++j){
+						String target = sw.branches().get(j).second();
 						swStores.add(stores.get(target));
 					}
-					T defStore = stores.get(sw.defaultTarget);
+					T defStore = stores.get(sw.defaultTarget());
 
 					store = propagate(id, sw, swStores, defStore);
 				} else if (code instanceof Bytecode.Goto) {
@@ -194,7 +194,7 @@ protected T propagate(int blockID, T store, List> handlers) {
 	 *            statement on the false branch.
 	 * @return
 	 */
-	protected abstract T propagate(CodeForest.Index index, Bytecode.If ifgoto,
+	protected abstract T propagate(BytecodeForest.Index index, Bytecode.If ifgoto,
 			T trueStore, T falseStore);
 
 	/**
@@ -216,7 +216,7 @@ protected abstract T propagate(CodeForest.Index index, Bytecode.If ifgoto,
 	 *            statement on the false branch.
 	 * @return
 	 */
-	protected abstract T propagate(CodeForest.Index index, Bytecode.IfIs iftype, T trueStore,
+	protected abstract T propagate(BytecodeForest.Index index, Bytecode.IfIs iftype, T trueStore,
 			T falseStore);
 
 	/**
@@ -236,7 +236,7 @@ protected abstract T propagate(CodeForest.Index index, Bytecode.IfIs iftype, T t
 	 *            --- abstract store coming from default branch
 	 * @return
 	 */
-	protected abstract T propagate(CodeForest.Index index, Bytecode.Switch sw,
+	protected abstract T propagate(BytecodeForest.Index index, Bytecode.Switch sw,
 			List stores, T defStore);
 
 	/**
@@ -254,7 +254,7 @@ protected abstract T propagate(CodeForest.Index index, Bytecode.Switch sw,
 	 *            statement.
 	 * @return
 	 */
-	protected abstract T propagate(CodeForest.Index index, Bytecode.Loop code, T store,
+	protected abstract T propagate(BytecodeForest.Index index, Bytecode.Loop code, T store,
 			List> handlers);
 
 	/**
@@ -272,7 +272,7 @@ protected abstract T propagate(CodeForest.Index index, Bytecode.Loop code, T sto
 	 *            statement.
 	 * @return
 	 */
-	protected abstract T propagate(CodeForest.Index index, Bytecode code, T store);
+	protected abstract T propagate(BytecodeForest.Index index, Bytecode code, T store);
 
 	/**
 	 * Propagate from an exception handler.
diff --git a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java
index 110439fcb2..8d304b660d 100755
--- a/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java
+++ b/modules/wyil/src/wyil/util/dfa/ForwardFlowAnalysis.java
@@ -53,7 +53,7 @@ public abstract class ForwardFlowAnalysis {
 	/**
 	 * The code forest currently being propagated through.
 	 */
-	protected CodeForest forest;
+	protected BytecodeForest forest;
 
 	/**
 	 * The temporary abstract stores being generated during propagation.
@@ -117,12 +117,12 @@ protected WyilFile.FunctionOrMethod propagate(
 	 */
 	protected T propagate(int blockID, T store) {
 		
-		CodeForest.Block block = forest.get(blockID);
+		BytecodeForest.Block block = forest.get(blockID);
 		for (int i = 0; i < block.size(); ++i) {
 			Bytecode code = block.get(i).code();
 
 			// Construct the bytecode ID
-			CodeForest.Index id = new CodeForest.Index(blockID,i);
+			BytecodeForest.Index id = new BytecodeForest.Index(blockID,i);
 
 			try {
 				// First, check for a label which may have incoming information.
@@ -163,12 +163,12 @@ protected T propagate(int blockID, T store) {
 
 					// assert r.second().size() == nsw.branches.size()
 					Bytecode.Switch nsw = (Bytecode.Switch) code;
-					for (int j = 0; j != nsw.branches.size(); ++j) {
-						String target = nsw.branches.get(j).second();
+					for (int j = 0; j != nsw.branches().size(); ++j) {
+						String target = nsw.branches().get(j).second();
 						T nstore = r.get(j);
 						merge(target, nstore, stores);
 					}
-					merge(sw.defaultTarget, store, stores);
+					merge(sw.defaultTarget(), store, stores);
 					store = null;
 				} else if (code instanceof Bytecode.Goto) {
 					Bytecode.Goto gto = (Bytecode.Goto) code;
@@ -220,7 +220,7 @@ private void merge(String target, T store, Map stores) {
 	 *            statement.
 	 * @return
 	 */
-	protected abstract Pair propagate(CodeForest.Index index, Bytecode.If ifgoto, T store);
+	protected abstract Pair propagate(BytecodeForest.Index index, Bytecode.If ifgoto, T store);
 
 	/**
 	 * 

@@ -240,7 +240,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract Pair propagate(CodeForest.Index index, Bytecode.IfIs iftype, T store); + protected abstract Pair propagate(BytecodeForest.Index index, Bytecode.IfIs iftype, T store); /** *

@@ -257,7 +257,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract List propagate(CodeForest.Index index, Bytecode.Switch sw, T store); + protected abstract List propagate(BytecodeForest.Index index, Bytecode.Switch sw, T store); /** *

@@ -280,7 +280,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract T propagate(CodeForest.Index index, Bytecode.Loop code, T store); + protected abstract T propagate(BytecodeForest.Index index, Bytecode.Loop code, T store); /** *

@@ -297,7 +297,7 @@ private void merge(String target, T store, Map stores) { * statement. * @return */ - protected abstract T propagate(CodeForest.Index index, Bytecode code, T store); + protected abstract T propagate(BytecodeForest.Index index, Bytecode code, T store); /** * Determine the initial store for the current method case. diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index 6bfa690918..264c74faf0 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -89,7 +89,7 @@ public Constant[] execute(NameID nid, Type.FunctionOrMethod sig, Constant... arg throw new IllegalArgumentException("incorrect number of arguments: " + nid + ", " + sig); } // Third, get and check the function or method body - CodeForest code = fm.code(); + BytecodeForest code = fm.code(); if (fm.body() == null) { // FIXME: add support for native functions or methods throw new IllegalArgumentException("no function or method body found: " + nid + ", " + sig); @@ -101,7 +101,7 @@ public Constant[] execute(NameID nid, Type.FunctionOrMethod sig, Constant... arg frame[i] = args[i]; } // Finally, let's do it! - CodeForest.Index pc = new CodeForest.Index(fm.body(), 0); + BytecodeForest.Index pc = new BytecodeForest.Index(fm.body(), 0); return (Constant[]) executeAllWithin(frame, new Context(pc, code)); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); @@ -118,16 +118,16 @@ public Constant[] execute(NameID nid, Type.FunctionOrMethod sig, Constant... arg * @return */ private Object executeAllWithin(Constant[] frame, Context context) { - CodeForest forest = context.forest; - CodeForest.Index pc = context.pc; + BytecodeForest forest = context.forest; + BytecodeForest.Index pc = context.pc; int block = pc.block(); - CodeForest.Block codes = forest.get(pc.block()); + BytecodeForest.Block codes = forest.get(pc.block()); while (pc.block() == block && pc.offset() < codes.size()) { Object r = execute(frame, new Context(pc, context.forest)); // Now, see whether we are continuing or not - if (r instanceof CodeForest.Index) { - pc = (CodeForest.Index) r; + if (r instanceof BytecodeForest.Index) { + pc = (BytecodeForest.Index) r; } else { return r; } @@ -214,7 +214,7 @@ private Object execute(Constant[] frame, Context context) { */ private Object execute(Bytecode.AssertOrAssume bytecode, Constant[] frame, Context context) { // - CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); + BytecodeForest.Index pc = new BytecodeForest.Index(bytecode.block(), 0); Object r = executeAllWithin(frame, new Context(pc, context.forest)); // if (r == null) { @@ -327,7 +327,7 @@ private Constant convert(Constant value, Type to, Context context) { private Constant convert(Constant value, Type.Record to, Context context) { checkType(value, context, Constant.Record.class); Constant.Record rv = (Constant.Record) value; - HashSet rv_fields = new HashSet(rv.values.keySet()); + HashSet rv_fields = new HashSet(rv.values().keySet()); // Check fields in value are subset of those in target type if (!rv_fields.containsAll(to.keys())) { error("cannot convert between records with differing fields", context); @@ -335,10 +335,10 @@ private Constant convert(Constant value, Type.Record to, Context context) { } else { HashMap nValues = new HashMap(); for (String field : to.keys()) { - Constant nValue = convert(rv.values.get(field), to.field(field), context); + Constant nValue = convert(rv.values().get(field), to.field(field), context); nValues.put(field, nValue); } - return Constant.V_RECORD(nValues); + return new Constant.Record(nValues); } } @@ -355,11 +355,11 @@ private Constant convert(Constant value, Type.Record to, Context context) { private Constant convert(Constant value, Type.Array to, Context context) { checkType(value, context, Constant.Array.class); Constant.Array lv = (Constant.Array) value; - ArrayList values = new ArrayList(lv.values); + ArrayList values = new ArrayList(lv.values()); for (int i = 0; i != values.size(); ++i) { values.set(i, convert(values.get(i), to.element(), context)); } - return Constant.V_ARRAY(values); + return new Constant.Array(values); } /** @@ -413,8 +413,8 @@ private Constant convert(Constant value, Type.FunctionOrMethod to, Context conte private Object execute(Bytecode.Debug bytecode, Constant[] frame, Context context) { // Constant.Array list = (Constant.Array) frame[bytecode.operand(0)]; - for (Constant item : list.values) { - BigInteger b = ((Constant.Integer) item).value; + for (Constant item : list.values()) { + BigInteger b = ((Constant.Integer) item).value(); char c = (char) b.intValue(); debug.print(c); } @@ -440,7 +440,7 @@ private Object execute(Bytecode.Fail bytecode, Constant[] frame, Context context private Object execute(Bytecode.FieldLoad bytecode, Constant[] frame, Context context) { Constant.Record rec = (Constant.Record) frame[bytecode.operand(0)]; - frame[bytecode.target(0)] = rec.values.get(bytecode.fieldName()); + frame[bytecode.target(0)] = rec.values().get(bytecode.fieldName()); return context.pc.next(); } @@ -451,13 +451,13 @@ private Object execute(Bytecode.Quantify bytecode, Constant[] frame, Context con checkType(endOperand, context, Constant.Integer.class); Constant.Integer so = (Constant.Integer) startOperand; Constant.Integer eo = (Constant.Integer) endOperand; - int start = so.value.intValue(); - int end = eo.value.intValue(); + int start = so.value().intValue(); + int end = eo.value().intValue(); for (int i = start; i < end; ++i) { // Assign the index variable - frame[bytecode.indexOperand()] = Constant.V_INTEGER(BigInteger.valueOf(i)); + frame[bytecode.indexOperand()] = new Constant.Integer(BigInteger.valueOf(i)); // Execute loop body for one iteration - CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); + BytecodeForest.Index pc = new BytecodeForest.Index(bytecode.block(), 0); Object r = executeAllWithin(frame, new Context(pc, context.forest)); // Now, check whether we fell through to the end or not. If not, // then we must have exited somehow so return to signal that. @@ -476,7 +476,7 @@ private Object execute(Bytecode.Goto bytecode, Constant[] frame, Context context private Object execute(Bytecode.If bytecode, Constant[] frame, Context context) { Constant.Bool operand = checkType(frame[bytecode.operand(0)],context,Constant.Bool.class); - if (operand.value) { + if (operand.value()) { // branch taken, so jump to destination label return context.getLabel(bytecode.destination()); } else { @@ -532,7 +532,7 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { Constant.Array t = (Constant.Array) value; Type element = ((Type.Array) type).element(); boolean r = true; - for (Constant val : t.values) { + for (Constant val : t.values()) { r &= isMemberOfType(val, element, context); } return r; @@ -542,14 +542,14 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { if (value instanceof Constant.Record) { Type.Record rt = (Type.Record) type; Constant.Record t = (Constant.Record) value; - Set fields = t.values.keySet(); + Set fields = t.values().keySet(); if (!fields.containsAll(rt.keys()) || (!rt.keys().containsAll(fields) && !rt.isOpen())) { // In this case, the set of fields does not match properly return false; } boolean r = true; for (String field : fields) { - r &= isMemberOfType(t.values.get(field), rt.field(field), context); + r &= isMemberOfType(t.values().get(field), rt.field(field), context); } return r; } @@ -568,7 +568,7 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { } else if (type instanceof Type.FunctionOrMethod) { if (value instanceof Constant.Lambda) { Constant.Lambda l = (Constant.Lambda) value; - if (Type.isSubtype(type, l.type)) { + if (Type.isSubtype(type, l.type())) { return true; } } @@ -590,11 +590,11 @@ public boolean isMemberOfType(Constant value, Type type, Context context) { return false; } // Check any invariant associated with this type - CodeForest invariant = td.invariant(); + BytecodeForest invariant = td.invariant(); if (invariant.numBlocks() > 0) { Constant[] frame = new Constant[invariant.numRegisters()]; frame[0] = value; - CodeForest.Index pc = new CodeForest.Index(invariant.getRoot(0), 0); + BytecodeForest.Index pc = new BytecodeForest.Index(invariant.getRoot(0), 0); executeAllWithin(frame, new Context(pc, invariant)); } // Done @@ -635,7 +635,7 @@ private Object execute(Bytecode.IndirectInvoke bytecode, Constant[] frame, Conte // constant arguments provided in the lambda itself along with those // operands provided for the "holes". Constant.Lambda func = (Constant.Lambda) operand; - List func_arguments = func.arguments; + List func_arguments = func.arguments(); int[] operands = bytecode.operands(); Constant[] arguments = new Constant[func_arguments.size() + (operands.length - 1)]; { @@ -644,11 +644,11 @@ private Object execute(Bytecode.IndirectInvoke bytecode, Constant[] frame, Conte arguments[i++] = frame[operands[j]]; } for (int j = 0; j != func_arguments.size(); ++j) { - arguments[i++] = func.arguments.get(j); + arguments[i++] = func.arguments().get(j); } } // Make the actual call - Constant[] results = execute(func.name, func.type(), arguments); + Constant[] results = execute(func.name(), func.type(), arguments); // Check whether a return value was expected or not int[] targets = bytecode.targets(); List returns = bytecode.type(0).returns(); @@ -703,7 +703,7 @@ private Object execute(Bytecode.Lambda bytecode, Constant[] frame, Context conte arguments[i] = frame[reg]; } // FIXME: need to do something with the operands here. - frame[bytecode.target(0)] = Constant.V_LAMBDA(bytecode.name(), bytecode.type(0), arguments); + frame[bytecode.target(0)] = new Constant.Lambda(bytecode.name(), bytecode.type(0), arguments); // return context.pc.next(); } @@ -712,7 +712,7 @@ private Object execute(Bytecode.Loop bytecode, Constant[] frame, Context context Object r; do { // Keep executing the loop body until we exit it somehow. - CodeForest.Index pc = new CodeForest.Index(bytecode.block(), 0); + BytecodeForest.Index pc = new BytecodeForest.Index(bytecode.block(), 0); r = executeAllWithin(frame, new Context(pc, context.forest)); } while (r == null); @@ -745,13 +745,13 @@ private Object execute(Bytecode.Return bytecode, Constant[] frame, Context conte private Object execute(Bytecode.Switch bytecode, Constant[] frame, Context context) { // Constant operand = frame[bytecode.operand(0)]; - for (Pair branch : bytecode.branches) { + for (Pair branch : bytecode.branches()) { Constant caseOperand = branch.first(); if (caseOperand.equals(operand)) { return context.getLabel(branch.second()); } } - return context.getLabel(bytecode.defaultTarget); + return context.getLabel(bytecode.defaultTarget()); } private Object execute(Bytecode.Update bytecode, Constant[] frame, Context context) { @@ -794,20 +794,20 @@ private Constant update(Constant lhs, Iterator descriptor, Consta checkType(operand, context, Constant.Integer.class); checkType(lhs, context, Constant.Array.class); Constant.Array list = (Constant.Array) lhs; - int index = ((Constant.Integer) operand).value.intValue(); - ArrayList values = new ArrayList(list.values); + int index = ((Constant.Integer) operand).value().intValue(); + ArrayList values = new ArrayList(list.values()); rhs = update(values.get(index), descriptor, rhs, frame, context); values.set(index, rhs); - return Constant.V_ARRAY(values); + return new Constant.Array(values); } else if (lval instanceof Bytecode.RecordLVal) { // Record Bytecode.RecordLVal lv = (Bytecode.RecordLVal) lval; checkType(lhs, context, Constant.Record.class); Constant.Record record = (Constant.Record) lhs; - HashMap values = new HashMap(record.values); + HashMap values = new HashMap(record.values()); rhs = update(values.get(lv.field), descriptor, rhs, frame, context); values.put(lv.field, rhs); - return Constant.V_RECORD(values); + return new Constant.Record(values); } else { // Reference Bytecode.ReferenceLVal lv = (Bytecode.ReferenceLVal) lval; @@ -882,16 +882,16 @@ private Object deadCode(Context context) { * */ public static class Context { - public final CodeForest.Index pc; - public final CodeForest forest; - private Map labels; + public final BytecodeForest.Index pc; + public final BytecodeForest forest; + private Map labels; - public Context(CodeForest.Index pc, CodeForest block) { + public Context(BytecodeForest.Index pc, BytecodeForest block) { this.pc = pc; this.forest = block; } - public CodeForest.Index getLabel(String label) { + public BytecodeForest.Index getLabel(String label) { if (labels == null) { labels = Bytecode.buildLabelMap(forest); } diff --git a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java index 1ddf360dfe..6b2d23e732 100644 --- a/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java +++ b/modules/wyil/src/wyil/util/interpreter/StandardFunctions.java @@ -90,7 +90,7 @@ private static final class Not implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { Constant.Bool i = checkType(operands[0], context, Constant.Bool.class); - return Constant.V_BOOL(!i.value); + return Constant.Bool(!i.value()); } } @@ -102,7 +102,7 @@ private static final class Negate implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { Constant.Integer i = checkType(operands[0], context, Constant.Integer.class); - return i.negate(); + return new Constant.Integer(i.value().negate()); } } @@ -111,7 +111,7 @@ private static final class Add implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - return lhs.add(rhs); + return new Constant.Integer(lhs.value().add(rhs.value())); } } @@ -120,7 +120,7 @@ private static final class Subtract implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - return lhs.subtract(rhs); + return new Constant.Integer(lhs.value().subtract(rhs.value())); } } @@ -129,7 +129,7 @@ private static final class Multiply implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - return lhs.multiply(rhs); + return new Constant.Integer(lhs.value().multiply(rhs.value())); } } private static final class Divide implements InternalFunction { @@ -137,7 +137,7 @@ private static final class Divide implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - return lhs.divide(rhs); + return new Constant.Integer(lhs.value().divide(rhs.value())); } } @@ -146,19 +146,21 @@ private static final class Remainder implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Integer lhs = checkType(operands[0], context, Constant.Integer.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - return lhs.remainder(rhs); + return new Constant.Integer(lhs.value().remainder(rhs.value())); } } + + private static final class Equal implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { - return Constant.V_BOOL(operands[0].equals(operands[1])); + return Constant.Bool(operands[0].equals(operands[1])); } } private static final class NotEqual implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { - return Constant.V_BOOL(!operands[0].equals(operands[1])); + return Constant.Bool(!operands[0].equals(operands[1])); } } private static final class LessThan implements InternalFunction { @@ -194,7 +196,7 @@ private static final class BitwiseInvert implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { Constant.Byte b = checkType(operands[0], context, Constant.Byte.class); - return Constant.V_BYTE((byte) ~b.value); + return new Constant.Byte((byte) ~b.value()); } } @@ -203,8 +205,8 @@ private static final class BitwiseOr implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); Constant.Byte rhs = checkType(operands[1], context, Constant.Byte.class); - int result = lhs.value | rhs.value; - return Constant.V_BYTE((byte) result); + int result = lhs.value() | rhs.value(); + return new Constant.Byte((byte) result); } } private static final class BitwiseXor implements InternalFunction { @@ -212,8 +214,8 @@ private static final class BitwiseXor implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); Constant.Byte rhs = checkType(operands[1], context, Constant.Byte.class); - int result = lhs.value ^ rhs.value; - return Constant.V_BYTE((byte) result); + int result = lhs.value() ^ rhs.value(); + return new Constant.Byte((byte) result); } } private static final class BitwiseAnd implements InternalFunction { @@ -221,8 +223,8 @@ private static final class BitwiseAnd implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); Constant.Byte rhs = checkType(operands[1], context, Constant.Byte.class); - int result = lhs.value & rhs.value; - return Constant.V_BYTE((byte) result); + int result = lhs.value() & rhs.value(); + return new Constant.Byte((byte) result); } } private static final class LeftShift implements InternalFunction { @@ -230,8 +232,8 @@ private static final class LeftShift implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - int result = lhs.value << rhs.value.intValue(); - return Constant.V_BYTE((byte) result); + int result = lhs.value() << rhs.value().intValue(); + return new Constant.Byte((byte) result); } } private static final class RightShift implements InternalFunction { @@ -239,8 +241,8 @@ private static final class RightShift implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Byte lhs = checkType(operands[0], context, Constant.Byte.class); Constant.Integer rhs = checkType(operands[1], context, Constant.Integer.class); - int result = lhs.value >> rhs.value.intValue(); - return Constant.V_BYTE((byte) result); + int result = lhs.value() >> rhs.value().intValue(); + return new Constant.Byte((byte) result); } } @@ -251,8 +253,8 @@ private static final class ArrayLength implements InternalFunction { @Override public Constant apply(Constant[] operands, Context context) { Constant.Array array = checkType(operands[0], context, Constant.Array.class); - BigInteger length = BigInteger.valueOf(array.values.size()); - return Constant.V_INTEGER(length); + BigInteger length = BigInteger.valueOf(array.values().size()); + return new Constant.Integer(length); } } private static final class ArrayIndex implements InternalFunction { @@ -260,12 +262,12 @@ private static final class ArrayIndex implements InternalFunction { public Constant apply(Constant[] operands, Context context) { Constant.Array src = checkType(operands[0], context, Constant.Array.class); Constant.Integer index = checkType(operands[1], context, Constant.Integer.class); - int i = index.value.intValue(); - if (i < 0 || i >= src.values.size()) { + int i = index.value().intValue(); + if (i < 0 || i >= src.values().size()) { error("index-out-of-bounds", context); } // Ok, get the element at that index - return src.values.get(index.value.intValue()); + return src.values().get(index.value().intValue()); } } private static final class ArrayGenerator implements InternalFunction { @@ -274,12 +276,12 @@ public Constant apply(Constant[] operands, Context context) { Constant element = operands[0]; Constant.Integer count = checkType(operands[1], context, Constant.Integer.class); // Check that we have a integer count - int n = count.value.intValue(); + int n = count.value().intValue(); ArrayList values = new ArrayList(); for (int i = 0; i != n; ++i) { values.add(element); } - return Constant.V_ARRAY(values); + return new Constant.Array(values); } } private static final class ArrayConstructor implements InternalFunction { @@ -289,7 +291,7 @@ public Constant apply(Constant[] operands, Context context) { for (Constant operand : operands) { values.add(operand); } - return Constant.V_ARRAY(values); + return new Constant.Array(values); } } @@ -307,7 +309,7 @@ public Constant apply(Constant[] operands, Context context) { for (int i = 0; i != operands.length; ++i) { values.put(fields.get(i), operands[i]); } - return Constant.V_RECORD(values); + return new Constant.Record(values); } } @@ -315,6 +317,8 @@ public Constant apply(Constant[] operands, Context context) { // ==================================================================================== // Helpers // ==================================================================================== + + private static Constant.Bool lessThan(Constant lhs, Constant rhs, boolean isStrict, Context context) { checkType(lhs, context, Constant.Integer.class); checkType(rhs, context, Constant.Integer.class); @@ -324,9 +328,9 @@ private static Constant.Bool lessThan(Constant lhs, Constant rhs, boolean isStri // In the strict case, the lhs must be strictly below the rhs. In the // non-strict case, they can be equal. if (isStrict) { - return Constant.V_BOOL(result < 0); + return Constant.Bool(result < 0); } else { - return Constant.V_BOOL(result <= 0); + return Constant.Bool(result <= 0); } } diff --git a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java index 5d7f9ef6c1..23a1354038 100755 --- a/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java +++ b/modules/wyjc/src/wyjc/Wyil2JavaBuilder.java @@ -369,7 +369,7 @@ private ClassFile.Method build(WyilFile.Type td) { translateInvariantTest(falseBranch, td.type(), 0, 1, constants, bytecodes); // Second, generate code for invariant (if applicable). // FIXME: use of patchInvariantBlock is not ideal - CodeForest invariant = patchInvariantBlock(falseBranch, td.invariant()); + BytecodeForest invariant = patchInvariantBlock(falseBranch, td.invariant()); for(int i=0;i!=invariant.numRoots();++i) { translate(invariant.getRoot(i), 1, invariant, bytecodes); } @@ -392,18 +392,18 @@ private ClassFile.Method build(WyilFile.Type td) { * @param falseBranch * @param forest */ - private CodeForest patchInvariantBlock(String falseBranch, CodeForest forest) { - CodeForest nForest = new CodeForest(forest); + private BytecodeForest patchInvariantBlock(String falseBranch, BytecodeForest forest) { + BytecodeForest nForest = new BytecodeForest(forest); for(int i=0;i!=forest.numBlocks();++i) { patchInvariantBlockHelper(falseBranch, nForest.get(i)); } return nForest; } - private void patchInvariantBlockHelper(String falseBranch, CodeForest.Block block) { + private void patchInvariantBlockHelper(String falseBranch, BytecodeForest.Block block) { for (int i = 0; i != block.size(); ++i) { // This is still a valid index - CodeForest.Entry e = block.get(i); + BytecodeForest.Entry e = block.get(i); wyil.lang.Bytecode c = e.code(); if (c instanceof Return) { @@ -516,7 +516,7 @@ private ClassFile.Method translate(WyilFile.FunctionOrMethod method) { lineNumbers = new ArrayList(); ArrayList bytecodes = new ArrayList(); - CodeForest forest = method.code(); + BytecodeForest forest = method.code(); translate(method.body(), forest.numRegisters(), forest, bytecodes); jasm.attributes.Code code = new jasm.attributes.Code(bytecodes, Collections.EMPTY_LIST, cm); @@ -538,8 +538,8 @@ private ClassFile.Method translate(WyilFile.FunctionOrMethod method) { * @param bytecodes * --- list to insert bytecodes into * */ - private void translate(int blk, int freeSlot, CodeForest forest, ArrayList bytecodes) { - translate(new CodeForest.Index(blk, 0), freeSlot, forest, bytecodes); + private void translate(int blk, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { + translate(new BytecodeForest.Index(blk, 0), freeSlot, forest, bytecodes); } /** @@ -556,10 +556,10 @@ private void translate(int blk, int freeSlot, CodeForest forest, ArrayList bytecodes) { - CodeForest.Block block = forest.get(pc.block()); + private void translate(BytecodeForest.Index pc, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { + BytecodeForest.Block block = forest.get(pc.block()); for (int i = 0; i != block.size(); ++i) { - CodeForest.Index index = new CodeForest.Index(pc.block(), i); + BytecodeForest.Index index = new BytecodeForest.Index(pc.block(), i); SourceLocation loc = forest.get(index).attribute(SourceLocation.class); if (loc != null) { // FIXME: figure our how to get line number! @@ -586,7 +586,7 @@ private void translate(CodeForest.Index pc, int freeSlot, CodeForest forest, Arr * The list of bytecodes being accumulated * @return */ - private int translate(CodeForest.Index pc, wyil.lang.Bytecode code, int freeSlot, CodeForest forest, + private int translate(BytecodeForest.Index pc, wyil.lang.Bytecode code, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { try { @@ -645,9 +645,9 @@ private int translate(CodeForest.Index pc, wyil.lang.Bytecode code, int freeSlot } - private void translate(CodeForest.Index index, AssertOrAssume c, - int freeSlot, CodeForest forest, ArrayList bytecodes) { - CodeForest.Index pc = new CodeForest.Index(c.block(), 0); + private void translate(BytecodeForest.Index index, AssertOrAssume c, + int freeSlot, BytecodeForest forest, ArrayList bytecodes) { + BytecodeForest.Index pc = new BytecodeForest.Index(c.block(), 0); if(c instanceof Invariant) { // essentially a no-op for now } else { @@ -655,8 +655,8 @@ private void translate(CodeForest.Index index, AssertOrAssume c, } } - private void translate(CodeForest.Index index, Const c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, Const c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { Constant constant = c.constant(); JvmType jt = convertUnderlyingType(constant.type()); @@ -673,14 +673,14 @@ private void translate(CodeForest.Index index, Const c, int freeSlot, bytecodes.add(new Bytecode.Store(c.target(), jt)); } - private void translate(CodeForest.Index index, Convert c, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Convert c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); addCoercion(c.type(0), c.result(), freeSlot, constants, bytecodes); bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.result()))); } - private void translate(CodeForest.Index index, Update code, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Update code, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(code.target(0), convertUnderlyingType(code.type(0)))); translateUpdate(code.iterator(), code, bytecodes); @@ -824,8 +824,8 @@ private void translateUpdate(ReferenceLVal lval, Iterator iterator, Update bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "setState", setFunType, Bytecode.InvokeMode.VIRTUAL)); } - private void translate(CodeForest.Index index, Return c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, Return c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { JvmType jt = null; int[] operands = c.operands(); if(operands.length == 1) { @@ -839,12 +839,12 @@ private void translate(CodeForest.Index index, Return c, int freeSlot, bytecodes.add(new Bytecode.Return(jt)); } - private void translate(CodeForest.Index index, Switch c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, Switch c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { ArrayList> cases = new ArrayList(); boolean canUseSwitchBytecode = true; - for (Pair p : c.branches) { + for (Pair p : c.branches()) { // first, check whether the switch value is indeed an integer. Constant v = (Constant) p.first(); if (!(v instanceof Constant.Integer)) { @@ -853,8 +853,8 @@ private void translate(CodeForest.Index index, Switch c, int freeSlot, } // second, check whether integer value can fit into a Java int Constant.Integer vi = (Constant.Integer) v; - int iv = vi.value.intValue(); - if (!BigInteger.valueOf(iv).equals(vi.value)) { + int iv = vi.value().intValue(); + if (!BigInteger.valueOf(iv).equals(vi.value())) { canUseSwitchBytecode = false; break; } @@ -866,11 +866,11 @@ private void translate(CodeForest.Index index, Switch c, int freeSlot, JvmType.Function ftype = new JvmType.Function(T_INT); bytecodes.add(new Bytecode.Load(c.operand(0), convertUnderlyingType(c.type(0)))); bytecodes.add(new Bytecode.Invoke(WHILEYINT, "intValue", ftype, Bytecode.InvokeMode.VIRTUAL)); - bytecodes.add(new Bytecode.Switch(c.defaultTarget, cases)); + bytecodes.add(new Bytecode.Switch(c.defaultTarget(), cases)); } else { // ok, in this case we have to fall back to series of the if // conditions. Not ideal. - for (Pair p : c.branches) { + for (Pair p : c.branches()) { Constant value = p.first(); String target = p.second(); translate(value, freeSlot, bytecodes); @@ -880,11 +880,11 @@ private void translate(CodeForest.Index index, Switch c, int freeSlot, bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, target)); } - bytecodes.add(new Bytecode.Goto(c.defaultTarget)); + bytecodes.add(new Bytecode.Goto(c.defaultTarget())); } } - private void translateIfGoto(CodeForest.Index index, If code, int freeSlot, CodeForest forest, + private void translateIfGoto(BytecodeForest.Index index, If code, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { JvmType jt = convertUnderlyingType(code.type(0)); bytecodes.add(new Bytecode.Load(code.operand(0), jt)); @@ -894,8 +894,8 @@ private void translateIfGoto(CodeForest.Index index, If code, int freeSlot, Code } - private void translate(CodeForest.Index index, IfIs c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, IfIs c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { // In this case, we're updating the type of a local variable. To // make this work, we must update the JVM type of that slot as well @@ -988,7 +988,7 @@ protected void translateTypeTest(String falseTarget, Type test, bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, falseTarget)); } else { // Fall-back to an external (recursive) check - Constant constant = Constant.V_TYPE(test); + Constant constant = new Constant.Type(test); int id = JvmValue.get(constant, constants); String name = "constant$" + id; bytecodes.add(new Bytecode.GetField(owner, name, WHILEYTYPE, @@ -1088,20 +1088,20 @@ private void translateInvariantTest(String falseTarget, Type type, int rootSlot, } } - private void translate(CodeForest.Index index, Loop c, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Loop c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { // Allocate header label for loop String loopHeader = freshLabel(); bytecodes.add(new Bytecode.Label(loopHeader)); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(new CodeForest.Index(c.block(), 0), freeSlot, forest, bytecodes); + translate(new BytecodeForest.Index(c.block(), 0), freeSlot, forest, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); } - private int translate(CodeForest.Index index, Quantify c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private int translate(BytecodeForest.Index index, Quantify c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.startOperand(), WHILEYINT)); bytecodes.add(new Bytecode.Load(c.endOperand(), WHILEYINT)); JvmType.Function ftype = new JvmType.Function(WHILEYARRAY, WHILEYINT, WHILEYINT); @@ -1123,7 +1123,7 @@ private int translate(CodeForest.Index index, Quantify c, int freeSlot, bytecodes.add(new Bytecode.Store(c.indexOperand(), convertUnderlyingType(Type.T_INT))); // Translate body of loop. The cast is required to ensure correct method // is called. - translate(new CodeForest.Index(c.block(), 0), freeSlot + 1, forest, bytecodes); + translate(new BytecodeForest.Index(c.block(), 0), freeSlot + 1, forest, bytecodes); // Terminate loop by branching back to head of loop bytecodes.add(new Bytecode.Goto(loopHeader)); bytecodes.add(new Bytecode.Label(loopExit)); @@ -1131,24 +1131,24 @@ private int translate(CodeForest.Index index, Quantify c, int freeSlot, return freeSlot; } - private void translate(CodeForest.Index index, Goto c, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Goto c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Goto(c.destination())); } - private void translate(CodeForest.Index index, Label c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, Label c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Label(c.label())); } - private void translate(CodeForest.Index index, Debug c, int freeSlot, - CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, Debug c, int freeSlot, + BytecodeForest forest, ArrayList bytecodes) { JvmType.Function ftype = new JvmType.Function(T_VOID, WHILEYARRAY); bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYARRAY)); bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "print", ftype, Bytecode.InvokeMode.STATIC)); } - private void translate(CodeForest.Index index, Fail c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, Fail c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION)); bytecodes.add(new Bytecode.LoadConst("runtime fault encountered")); @@ -1157,7 +1157,7 @@ private void translate(CodeForest.Index index, Fail c, int freeSlot, CodeForest bytecodes.add(new Bytecode.Throw()); } - private void translate(CodeForest.Index index, FieldLoad c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, FieldLoad c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYRECORD)); bytecodes.add(new Bytecode.LoadConst(c.fieldName())); JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, WHILEYRECORD, JAVA_LANG_STRING); @@ -1166,13 +1166,13 @@ private void translate(CodeForest.Index index, FieldLoad c, int freeSlot, CodeFo bytecodes.add(new Bytecode.Store(c.target(0), convertUnderlyingType(c.fieldType()))); } - private void translate(CodeForest.Index index, Operator c, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Operator c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { Context context = new Context(forest, index, freeSlot, bytecodes); generators[c.opcode()].translate(c, context); } - private void translate(CodeForest.Index index, Lambda c, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Lambda c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { // First, build and register lambda class which calls the given function @@ -1231,7 +1231,7 @@ private void translate(CodeForest.Index index, Lambda c, int freeSlot, CodeFores bytecodes.add(new Bytecode.Store(c.target(0), clazz)); } - private void translate(CodeForest.Index index, Invoke c, int freeSlot, CodeForest forest, + private void translate(BytecodeForest.Index index, Invoke c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { for (int i = 0; i != c.operands().length; ++i) { @@ -1259,7 +1259,7 @@ private void translate(CodeForest.Index index, Invoke c, int freeSlot, CodeFores } } - private void translate(CodeForest.Index index, IndirectInvoke c, int freeSlot, CodeForest forest, ArrayList bytecodes) { + private void translate(BytecodeForest.Index index, IndirectInvoke c, int freeSlot, BytecodeForest forest, ArrayList bytecodes) { Type.FunctionOrMethod ft = c.type(0); JvmType.Clazz owner = (JvmType.Clazz) convertUnderlyingType(ft); bytecodes.add(new Bytecode.Load(c.reference(), convertUnderlyingType(ft))); @@ -1315,7 +1315,7 @@ protected void translate(Constant.Null e, int freeSlot, ArrayList byte } protected void translate(Constant.Bool e, int freeSlot, ArrayList bytecodes) { - if (e.value) { + if (e.value()) { bytecodes.add(new Bytecode.LoadConst(1)); } else { bytecodes.add(new Bytecode.LoadConst(0)); @@ -1331,7 +1331,7 @@ protected void translate(Constant.Type e, int freeSlot, BinaryOutputStream bout = new BinaryOutputStream(jout); Type.BinaryWriter writer = new Type.BinaryWriter(bout); try { - writer.write(e.type); + writer.write(e.value()); writer.close(); } catch (IOException ex) { throw new RuntimeException(ex.getMessage(), ex); @@ -1343,13 +1343,13 @@ protected void translate(Constant.Type e, int freeSlot, } protected void translate(Constant.Byte e, int freeSlot, ArrayList bytecodes) { - bytecodes.add(new Bytecode.LoadConst(e.value)); + bytecodes.add(new Bytecode.LoadConst(e.value())); JvmType.Function ftype = new JvmType.Function(WHILEYBYTE, T_BYTE); bytecodes.add(new Bytecode.Invoke(WHILEYBYTE, "valueOf", ftype, Bytecode.InvokeMode.STATIC)); } protected void translate(Constant.Integer e, int freeSlot, ArrayList bytecodes) { - BigInteger num = e.value; + BigInteger num = e.value(); if (num.bitLength() < 32) { bytecodes.add(new Bytecode.LoadConst(num.intValue())); @@ -1387,12 +1387,12 @@ protected void translate(Constant.Array lv, int freeSlot, ArrayList l ArrayList bytecodes) { bytecodes.add(new Bytecode.New(WHILEYARRAY)); bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); - bytecodes.add(new Bytecode.LoadConst(lv.values.size())); + bytecodes.add(new Bytecode.LoadConst(lv.values().size())); JvmType.Function ftype = new JvmType.Function(T_VOID, T_INT); bytecodes.add(new Bytecode.Invoke(WHILEYARRAY, "", ftype, Bytecode.InvokeMode.SPECIAL)); ftype = new JvmType.Function(T_BOOL, JAVA_LANG_OBJECT); - for (Constant e : lv.values) { + for (Constant e : lv.values()) { bytecodes.add(new Bytecode.Dup(WHILEYARRAY)); translate(e, freeSlot, bytecodes); addWriteConversion(e.type(), bytecodes); @@ -1405,7 +1405,7 @@ protected void translate(Constant.Record expr, int freeSlot, ArrayList bytecodes) { JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT, JAVA_LANG_OBJECT, JAVA_LANG_OBJECT); construct(WHILEYRECORD, freeSlot, bytecodes); - for (Map.Entry e : expr.values.entrySet()) { + for (Map.Entry e : expr.values().entrySet()) { Type et = e.getValue().type(); bytecodes.add(new Bytecode.Dup(WHILEYRECORD)); bytecodes.add(new Bytecode.LoadConst(e.getKey())); @@ -1422,7 +1422,7 @@ protected void translate(Constant.Lambda c, int freeSlot, ArrayList l // First, build and register lambda class which calls the given function // or method. This class will extend class wyjc.runtime.WyLambda. int lambda_id = lambdas.size(); - lambdas.add(buildLambda(c.name, c.type, lambda_id)); + lambdas.add(buildLambda(c.name(), c.type(), lambda_id)); // Second, create and duplicate new lambda object. This will then stay // on the stack (whilst the parameters are constructed) until the @@ -2178,12 +2178,12 @@ public final class Context { /** * The code forest in which we are currently operating */ - private final CodeForest forest; + private final BytecodeForest forest; /** * The index of the bytecode being translated */ - private final CodeForest.Index pc; + private final BytecodeForest.Index pc; /** * The list of bytecodes that have been generated so far @@ -2195,7 +2195,7 @@ public final class Context { */ private final int freeSlot; - public Context(CodeForest forest, CodeForest.Index pc, int freeSlot, ArrayList bytecodes) { + public Context(BytecodeForest forest, BytecodeForest.Index pc, int freeSlot, ArrayList bytecodes) { this.forest = forest; this.bytecodes = bytecodes; this.pc = pc; From e139bb1335e277b2c8213316fab3f5367d2bfdd7 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Mon, 18 Apr 2016 22:29:07 +1200 Subject: [PATCH 29/43] WyC: Refactoring CodeGenerator #621 Have merged various parameters into a single "GenerationContext" which has tidied things up a little. Also removed the concept of a "LoopScope" and have also folded this into the GenerationContext. It's not compiling yet, but we are getting there. --- .../wyc/src/wyc/builder/CodeGenerator.java | 873 +++++++++--------- 1 file changed, 441 insertions(+), 432 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index f8aa92d7ee..204a5acdab 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -26,6 +26,7 @@ package wyc.builder; import java.util.*; +import java.util.jar.Attributes; import static wyc.lang.WhileyFile.internalFailure; import static wyc.lang.WhileyFile.syntaxError; @@ -182,19 +183,17 @@ private WyilFile.Constant generate(WhileyFile.Constant cd) { * @throws Exception */ private WyilFile.Type generate(WhileyFile.Type td) throws Exception { - BytecodeForest forest = new BytecodeForest(); - Environment environment = new Environment(); + GenerationContext context = new GenerationContext(td); // Allocate declared parameter - environment.allocate(td.resolvedType.raw(), td.parameter.name()); + context.allocate(td.resolvedType.raw(), td.parameter.name()); // Generate code for each invariant condition for (Expr invariant : td.invariant) { - int root = generateInvariantBlock(invariant, environment, forest, td); - forest.addRoot(root); + generateInvariantBlock(invariant, context.createRootBlock()); } // Add all registers used within the invariant forest.registers().addAll(environment.asRegisters()); // done - return new WyilFile.Type(td.modifiers(), td.name(), td.resolvedType.nominal(), forest); + return new WyilFile.Type(td.modifiers(), td.name(), td.resolvedType.nominal(), context.forest()); } // ========================================================================= @@ -209,8 +208,7 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw // Construct environments // ================================================================== - BytecodeForest forest = new BytecodeForest(); - Environment environment = new Environment(); + GenerationContext context = new GenerationContext(fd); ArrayList declarations = new ArrayList(); addDeclaredParameters(fd.parameters, fd.resolvedType().params(), environment, declarations); addDeclaredParameters(fd.returns, fd.resolvedType().returns(), environment, declarations); @@ -222,16 +220,14 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw // Generate pre-condition // ================================================================== for (Expr precondition : fd.requires) { - int root = generateInvariantBlock(precondition, environment, forest, fd); - forest.addRoot(root); + generateInvariantBlock(precondition, context.createRootBlock()); } // ================================================================== // Generate post-condition // ================================================================== for (Expr postcondition : fd.ensures) { - int root = generateInvariantBlock(postcondition, environment, forest, fd); - forest.addRoot(root); + generateInvariantBlock(postcondition, context.createRootBlock()); } // ================================================================== @@ -249,16 +245,15 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw // fail because they have the same type). environment = resetEnvironment(environment, declarations); - BytecodeForest.Block body = new BytecodeForest.Block(); - forest.addAsRoot(body); + context = context.createRootBlock(); for (Stmt s : fd.statements) { - generate(s, environment, body, forest, fd); + generate(s, context); } // The following is sneaky. It guarantees that every method ends in a // return. For methods that actually need a value, this is either // removed as dead-code or remains and will cause an error. - body.add(new Bytecode.Return(), attributes(fd)); + context.add(new Bytecode.Return(), attributes(fd)); WyilFile.FunctionOrMethod declaration; @@ -267,12 +262,12 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw if (fd instanceof WhileyFile.Function) { WhileyFile.Function f = (WhileyFile.Function) fd; - declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), f.resolvedType.nominal(), forest, - fd.requires.size(), fd.ensures.size()); + declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), f.resolvedType.nominal(), + context.forest(), fd.requires.size(), fd.ensures.size()); } else { WhileyFile.Method md = (WhileyFile.Method) fd; - declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), md.resolvedType.nominal(), forest, - fd.requires.size(), fd.ensures.size()); + declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), md.resolvedType.nominal(), + context.forest(), fd.requires.size(), fd.ensures.size()); } // Done. @@ -288,15 +283,13 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw * @param forest * @param context */ - private int generateInvariantBlock(Expr invariant, Environment environment, BytecodeForest forest, Context context) { - BytecodeForest.Block precondition = new BytecodeForest.Block(); - int index = forest.add(precondition); + private int generateInvariantBlock(Expr invariant, GenerationContext context) { String endLab = freshLabel(); - generateCondition(endLab, invariant, environment, precondition, forest, context); - precondition.add(new Bytecode.Fail(), attributes(invariant)); - precondition.add(new Bytecode.Label(endLab)); - precondition.add(new Bytecode.Return()); - return index; + generateCondition(endLab, invariant, context); + context.add(new Bytecode.Fail(), attributes(invariant)); + context.add(new Bytecode.Label(endLab)); + context.add(new Bytecode.Return()); + return context.blockIndex(); } /** @@ -335,7 +328,7 @@ private void addDeclaredParameters(List parameters, List parameters, List operands = new ArrayList(); @@ -516,11 +506,11 @@ private void generate(Assign s, Environment environment, BytecodeForest.Block bl for (Nominal t : me.returns()) { types.add(t.raw()); } - operands.addAll(toIntegerList(generate(me, environment, block, forest, context))); + operands.addAll(toIntegerList(generate(me, context))); } else { // The assigned rval is a simple expression which returns a // single value - operands.add(generate(e, environment, block, forest, context)); + operands.add(generate(e, context)); types.add(e.result().raw()); } } @@ -531,20 +521,19 @@ private void generate(Assign s, Environment environment, BytecodeForest.Block bl // FlowTypeChecker. for (int i = 0; i != s.lvals.size(); ++i) { Expr.LVal lval = s.lvals.get(i); - generateAssignment(lval, operands.get(i), types.get(i), environment, block, forest, context); + generateAssignment(lval, operands.get(i), types.get(i), context); } } - public void generateAssignment(Expr.LVal lval, int operand, Type type, Environment environment, - BytecodeForest.Block block, BytecodeForest forest, Context context) { + public void generateAssignment(Expr.LVal lval, int operand, Type type, GenerationContext context) { if (lval instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) lval; // This is the easiest case. Having translated the right-hand side // expression, we now assign it directly to the register allocated // for variable on the left-hand side. - int[] targets = new int[] { environment.get(v.var) }; + int[] targets = new int[] { context.get(v.var) }; int[] operands = new int[] { operand }; - block.add(new Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); + context.add(new Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); } else if (lval instanceof Expr.IndexOf || lval instanceof Expr.FieldAccess || lval instanceof Expr.Dereference) { // This is the more complicated case, since the left-hand side @@ -555,12 +544,12 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme // updated. ArrayList fields = new ArrayList(); ArrayList operands = new ArrayList(); - Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, environment, block, forest, context); - int target = environment.get(lhs.var); - block.add(new Bytecode.Update(lhs.type.raw(), target, toIntArray(operands), operand, lhs.afterType.raw(), + Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, context); + int target = context.get(lhs.var); + context.add(new Bytecode.Update(lhs.type.raw(), target, toIntArray(operands), operand, lhs.afterType.raw(), fields), attributes(lval)); } else { - WhileyFile.syntaxError("invalid assignment", context, lval); + WhileyFile.syntaxError("invalid assignment", context.enclosingDeclaration(), lval); } } @@ -593,27 +582,27 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Environme * @return */ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, ArrayList operands, - Environment environment, BytecodeForest.Block block, BytecodeForest forest, Context context) { + GenerationContext context) { if (e instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) e; return v; } else if (e instanceof Expr.Dereference) { Expr.Dereference pa = (Expr.Dereference) e; - return extractLVal(pa.src, fields, operands, environment, block, forest, context); + return extractLVal(pa.src, fields, operands, context); } else if (e instanceof Expr.IndexOf) { Expr.IndexOf la = (Expr.IndexOf) e; - int operand = generate(la.index, environment, block, forest, context); - Expr.AssignedVariable l = extractLVal(la.src, fields, operands, environment, block, forest, context); + int operand = generate(la.index, context); + Expr.AssignedVariable l = extractLVal(la.src, fields, operands, context); operands.add(operand); return l; } else if (e instanceof Expr.FieldAccess) { Expr.FieldAccess ra = (Expr.FieldAccess) e; - Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, environment, block, forest, context); + Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, context); fields.add(ra.name); return r; } else { - WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), context, e); + WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), context.enclosingDeclaration(), e); return null; // dead code } } @@ -633,18 +622,15 @@ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, Arra * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Assert s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.Assert s, GenerationContext context) { // First, create assert block body - BytecodeForest.Block subblock = new BytecodeForest.Block(); - int body = forest.add(subblock); + GenerationContext subcontext = context.createBlock(); String endLab = freshLabel(); - generateCondition(endLab, s.expr, environment, subblock, forest, context); - subblock.add(new Bytecode.Fail(), attributes(s.expr)); - subblock.add(new Bytecode.Label(endLab)); + generateCondition(endLab, s.expr, subcontext); + subcontext.add(new Bytecode.Fail(), attributes(s.expr)); + subcontext.add(new Bytecode.Label(endLab)); // Second, create assert bytecode - block.add(new Bytecode.Assert(body), attributes(s)); - + context.add(new Bytecode.Assert(subcontext.blockIndex()), attributes(s)); } /** @@ -662,17 +648,15 @@ private void generate(Stmt.Assert s, Environment environment, BytecodeForest.Blo * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Assume s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.Assume s, GenerationContext context) { // First, create assume block body - BytecodeForest.Block subblock = new BytecodeForest.Block(); - int body = forest.add(subblock); + GenerationContext subcontext = context.createBlock(); String endLab = freshLabel(); - generateCondition(endLab, s.expr, environment, subblock, forest, context); - subblock.add(new Bytecode.Fail(), attributes(s.expr)); - subblock.add(new Bytecode.Label(endLab)); + generateCondition(endLab, s.expr, subcontext); + subcontext.add(new Bytecode.Fail(), attributes(s.expr)); + subcontext.add(new Bytecode.Label(endLab)); // Second, create assert bytecode - block.add(new Bytecode.Assume(body), attributes(s)); + context.add(new Bytecode.Assume(subcontext.blockIndex()), attributes(s)); } /** @@ -708,14 +692,13 @@ private void generate(Stmt.Assume s, Environment environment, BytecodeForest.Blo * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Return s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.Return s, GenerationContext context) { List returns = s.returns; // Here, we don't put the type propagated for the return expression. // Instead, we use the declared return type of this function. This // has the effect of forcing an implicit coercion between the // actual value being returned and its required type. - List returnTypes = ((WhileyFile.FunctionOrMethod) context).resolvedType().raw().returns(); + List returnTypes = context.getEnclosingFunctionType().raw().returns(); Type[] types = returnTypes.toArray(new Type[returnTypes.size()]); int[] operands = new int[types.length]; int index = 0; @@ -723,15 +706,15 @@ private void generate(Stmt.Return s, Environment environment, BytecodeForest.Blo Expr e = returns.get(i); // FIXME: this is a rather ugly if (e instanceof Expr.Multi) { - int[] results = generate((Expr.Multi) e, environment, block, forest, context); + int[] results = generate((Expr.Multi) e, context); for (int r : results) { operands[index++] = r; } } else { - operands[index++] = generate(e, environment, block, forest, context); + operands[index++] = generate(e, context); } } - block.add(new Bytecode.Return(types, operands), attributes(s)); + context.add(new Bytecode.Return(types, operands), attributes(s)); } /** @@ -749,8 +732,7 @@ private void generate(Stmt.Return s, Environment environment, BytecodeForest.Blo * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Skip s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.Skip s, GenerationContext context) { // TODO: should actually generate a NOP bytecode. This is an assignment // from zero operands to zero targets. At the moment, I cannot encode // this however because it will fail in the interpreter. @@ -787,10 +769,9 @@ private void generate(Stmt.Skip s, Environment environment, BytecodeForest.Block * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Debug s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int operand = generate(s.expr, environment, block, forest, context); - block.add(new Bytecode.Debug(operand), attributes(s)); + private void generate(Stmt.Debug s, GenerationContext context) { + int operand = generate(s.expr, context); + context.add(new Bytecode.Debug(operand), attributes(s)); } /** @@ -818,9 +799,8 @@ private void generate(Stmt.Debug s, Environment environment, BytecodeForest.Bloc * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Fail s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - block.add(new Bytecode.Fail(), attributes(s)); + private void generate(Stmt.Fail s, GenerationContext context) { + context.add(new Bytecode.Fail(), attributes(s)); } /** @@ -868,26 +848,25 @@ private void generate(Stmt.Fail s, Environment environment, BytecodeForest.Block * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.IfElse s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.IfElse s, GenerationContext context) { String falseLab = freshLabel(); String exitLab = s.falseBranch.isEmpty() ? falseLab : freshLabel(); - generateCondition(falseLab, invert(s.condition), environment, block, forest, context); + generateCondition(falseLab, invert(s.condition), context); for (Stmt st : s.trueBranch) { - generate(st, environment, block, forest, context); + generate(st, context); } if (!s.falseBranch.isEmpty()) { - block.add(new Bytecode.Goto(exitLab)); - block.add(new Bytecode.Label(falseLab)); + context.add(new Bytecode.Goto(exitLab)); + context.add(new Bytecode.Label(falseLab)); for (Stmt st : s.falseBranch) { - generate(st, environment, block, forest, context); + generate(st, context); } } - block.add(new Bytecode.Label(exitLab)); + context.add(new Bytecode.Label(exitLab)); } /** @@ -935,13 +914,13 @@ private void generate(Stmt.IfElse s, Environment environment, BytecodeForest.Blo * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Break s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - LoopScope scope = findEnclosingScope(LoopScope.class); - if (scope == null) { - WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context, s); + private void generate(Stmt.Break s, GenerationContext context) { + String breakLabel = context.getBreakLabel(); + if (breakLabel == null) { + // FIXME: this should be located elsewhere + WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context.enclosingDeclaration(), s); } - block.add(new Bytecode.Goto(scope.breakLabel)); + context.add(new Bytecode.Goto(breakLabel)); } /** @@ -992,13 +971,12 @@ private void generate(Stmt.Break s, Environment environment, BytecodeForest.Bloc * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Continue s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - LoopScope scope = findEnclosingScope(LoopScope.class); - if (scope == null) { - WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context, s); + private void generate(Stmt.Continue s, GenerationContext context) { + String continueLabel = context.getContinueLabel(); + if (continueLabel == null) { + WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context.enclosingDeclaration(), s); } - block.add(new Bytecode.Goto(scope.continueLabel)); + context.add(new Bytecode.Goto(continueLabel)); } /** @@ -1055,10 +1033,9 @@ private void generate(Stmt.Continue s, Environment environment, BytecodeForest.B * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.Switch s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) throws Exception { + private void generate(Stmt.Switch s, GenerationContext context) throws Exception { String exitLab = freshLabel(); - int operand = generate(s.expr, environment, block, forest, context); + int operand = generate(s.expr, context); String defaultTarget = exitLab; HashSet values = new HashSet<>(); ArrayList> cases = new ArrayList<>(); @@ -1070,19 +1047,19 @@ private void generate(Stmt.Switch s, Environment environment, BytecodeForest.Blo // must check that we have not already seen a case with an empty // match (otherwise, we'd have two default labels ;) if (defaultTarget != exitLab) { - WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context, c); + WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context.enclosingDeclaration(), c); } else { defaultTarget = freshLabel(); - block.add(new Bytecode.Label(defaultTarget), attributes(c)); + context.add(new Bytecode.Label(defaultTarget), attributes(c)); for (Stmt st : c.stmts) { - generate(st, environment, block, forest, context); + generate(st, context); } - block.add(new Bytecode.Goto(exitLab), attributes(c)); + context.add(new Bytecode.Goto(exitLab), attributes(c)); } } else if (defaultTarget == exitLab) { String target = freshLabel(); - block.add(new Bytecode.Label(target), attributes(c)); + context.add(new Bytecode.Label(target), attributes(c)); // Case statements in Whiley may have multiple matching constant // values. Therefore, we iterate each matching value and @@ -1093,27 +1070,27 @@ private void generate(Stmt.Switch s, Environment environment, BytecodeForest.Blo // Check whether this case constant has already been used as // a case constant elsewhere. If so, then report an error. if (values.contains(constant)) { - WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), context, c); + WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), context.enclosingDeclaration(), c); } cases.add(new Pair<>(constant, target)); values.add(constant); } for (Stmt st : c.stmts) { - generate(st, environment, block, forest, context); + generate(st, context); } - block.add(new Bytecode.Goto(exitLab), attributes(c)); + context.add(new Bytecode.Goto(exitLab), attributes(c)); } else { // This represents the case where we have another non-default // case after the default case. Such code cannot be executed, // and is therefore reported as an error. - WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), context, c); + WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), context.enclosingDeclaration(), c); } } - block.add(start, new Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); - block.add(new Bytecode.Label(exitLab), attributes(s)); + context.add(start, new Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); + context.add(new Bytecode.Label(exitLab), attributes(s)); } /** @@ -1157,8 +1134,7 @@ private void generate(Stmt.Switch s, Environment environment, BytecodeForest.Blo * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.While s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.While s, GenerationContext context) { // A label marking where execution continues after the while // loop finishes. Used when the loop condition evaluates to false // or when a break statement is encountered. @@ -1167,25 +1143,22 @@ private void generate(Stmt.While s, Environment environment, BytecodeForest.Bloc // by the continue statement. String continueLab = freshLabel(); - BytecodeForest.Block bodyBlock = new BytecodeForest.Block(); - int body = forest.add(bodyBlock); + GenerationContext subcontext = context.createBlock(exitLab,continueLab); for (Expr condition : s.invariants) { - int invariant = generateInvariantBlock(condition, environment, forest, context); - bodyBlock.add(new Bytecode.Invariant(invariant), attributes(condition)); + int invariant = generateInvariantBlock(condition,subcontext.createBlock()); + subcontext.add(new Bytecode.Invariant(invariant), attributes(condition)); } - generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); + generateCondition(exitLab, invert(s.condition), subcontext); - scopes.push(new LoopScope(continueLab, exitLab)); for (Stmt st : s.body) { - generate(st, environment, bodyBlock, forest, context); + generate(st, subcontext); } - scopes.pop(); // break - bodyBlock.add(new Bytecode.Label(continueLab), attributes(s)); - block.add(new Bytecode.Loop(new int[] {}, body), attributes(s)); - block.add(new Bytecode.Label(exitLab), attributes(s)); + subcontext.add(new Bytecode.Label(continueLab), attributes(s)); + context.add(new Bytecode.Loop(new int[] {}, subcontext.blockIndex()), attributes(s)); + context.add(new Bytecode.Label(exitLab), attributes(s)); } /** @@ -1230,8 +1203,7 @@ private void generate(Stmt.While s, Environment environment, BytecodeForest.Bloc * with error reporting as it determines the enclosing file. * @return */ - private void generate(Stmt.DoWhile s, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private void generate(Stmt.DoWhile s, GenerationContext context) { // A label marking where execution continues after the do-while // loop finishes. Used when the loop condition evaluates to false // or when a break statement is encountered. @@ -1240,25 +1212,22 @@ private void generate(Stmt.DoWhile s, Environment environment, BytecodeForest.Bl // by the continue statement. String continueLab = freshLabel(); - BytecodeForest.Block bodyBlock = new BytecodeForest.Block(); - int body = forest.add(bodyBlock); + GenerationContext subcontext = context.createBlock(exitLab,continueLab); - scopes.push(new LoopScope(continueLab, exitLab)); for (Stmt st : s.body) { - generate(st, environment, bodyBlock, forest, context); + generate(st, subcontext.createBlock()); } - scopes.pop(); // break for (Expr condition : s.invariants) { - int invariant = generateInvariantBlock(condition, environment, forest, context); - bodyBlock.add(new Bytecode.Invariant(invariant), attributes(condition)); + int invariant = generateInvariantBlock(condition, subcontext); + subcontext.add(new Bytecode.Invariant(invariant), attributes(condition)); } - bodyBlock.add(new Bytecode.Label(continueLab), attributes(s)); - generateCondition(exitLab, invert(s.condition), environment, bodyBlock, forest, context); + subcontext.add(new Bytecode.Label(continueLab), attributes(s)); + generateCondition(exitLab, invert(s.condition), subcontext); - block.add(new Bytecode.Loop(new int[] {}, body), attributes(s)); - block.add(new Bytecode.Label(exitLab), attributes(s)); + context.add(new Bytecode.Loop(new int[] {}, subcontext.blockIndex()), attributes(s)); + context.add(new Bytecode.Label(exitLab), attributes(s)); } // ========================================================================= @@ -1316,21 +1285,20 @@ private void generate(Stmt.DoWhile s, Environment environment, BytecodeForest.Bl * appended. * @return */ - public void generateCondition(String target, Expr condition, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) { + public void generateCondition(String target, Expr condition, GenerationContext context) { try { // First, we see whether or not we can employ a special handler for // translating this condition. if (condition instanceof Expr.Constant) { - generateCondition(target, (Expr.Constant) condition, environment, block, forest, context); + generateCondition(target, (Expr.Constant) condition, context); } else if (condition instanceof Expr.UnOp) { - generateCondition(target, (Expr.UnOp) condition, environment, block, forest, context); + generateCondition(target, (Expr.UnOp) condition, context); } else if (condition instanceof Expr.BinOp) { - generateCondition(target, (Expr.BinOp) condition, environment, block, forest, context); + generateCondition(target, (Expr.BinOp) condition, context); } else if (condition instanceof Expr.Quantifier) { - generateCondition(target, (Expr.Quantifier) condition, environment, block, forest, context); + generateCondition(target, (Expr.Quantifier) condition, context); } else if (condition instanceof Expr.ConstantAccess || condition instanceof Expr.LocalVariable || condition instanceof Expr.AbstractInvoke || condition instanceof Expr.AbstractIndirectInvoke || condition instanceof Expr.FieldAccess || condition instanceof Expr.IndexOf) { @@ -1340,17 +1308,17 @@ public void generateCondition(String target, Expr condition, Environment environ // true. In some cases, we could actually do better. For // example, !(x < 5) could be rewritten into x >= 5. - int result = generate(condition, environment, block, forest, context); - block.add(new Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); + int result = generate(condition, context); + context.add(new Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); } else { - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, condition); + syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context.enclosingDeclaration(), condition); } } catch (SyntaxError se) { throw se; } catch (Exception ex) { - internalFailure(ex.getMessage(), context, condition, ex); + internalFailure(ex.getMessage(), context.enclosingDeclaration(), condition, ex); } } @@ -1383,11 +1351,10 @@ public void generateCondition(String target, Expr condition, Environment environ * appended. * @return */ - private void generateCondition(String target, Expr.Constant c, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) { + private void generateCondition(String target, Expr.Constant c, GenerationContext context) { Constant.Bool b = (Constant.Bool) c.value; if (b.value()) { - block.add(new Bytecode.Goto(target)); + context.add(new Bytecode.Goto(target)); } else { // do nout } @@ -1413,49 +1380,48 @@ private void generateCondition(String target, Expr.Constant c, Environment envir * appended. * @return */ - private void generateCondition(String target, Expr.BinOp v, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) throws Exception { + private void generateCondition(String target, Expr.BinOp v, GenerationContext context) throws Exception { Expr.BOp bop = v.op; if (bop == Expr.BOp.OR) { - generateCondition(target, v.lhs, environment, block, forest, context); - generateCondition(target, v.rhs, environment, block, forest, context); + generateCondition(target, v.lhs, context); + generateCondition(target, v.rhs, context); } else if (bop == Expr.BOp.AND) { String exitLabel = freshLabel(); - generateCondition(exitLabel, invert(v.lhs), environment, block, forest, context); - generateCondition(target, v.rhs, environment, block, forest, context); - block.add(new Bytecode.Label(exitLabel)); + generateCondition(exitLabel, invert(v.lhs), context); + generateCondition(target, v.rhs, context); + context.add(new Bytecode.Label(exitLabel)); } else if (bop == Expr.BOp.IS) { - generateTypeCondition(target, v, environment, block, forest, context); + generateTypeCondition(target, v, context); } else { if (bop == Expr.BOp.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.Null) { // this is a simple rewrite to enable type inference. Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; - if (environment.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); + if (context.get(lhs.var) == null) { + syntaxError(errorMessage(UNKNOWN_VARIABLE), context.enclosingDeclaration(), v.lhs); } - int slot = environment.get(lhs.var); - block.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); + int slot = context.get(lhs.var); + context.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); } else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.Null) { // this is a simple rewrite to enable type inference. String exitLabel = freshLabel(); Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; - if (environment.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context, v.lhs); + if (context.get(lhs.var) == null) { + syntaxError(errorMessage(UNKNOWN_VARIABLE), context.enclosingDeclaration(), v.lhs); } - int slot = environment.get(lhs.var); - block.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); - block.add(new Bytecode.Goto(target)); - block.add(new Bytecode.Label(exitLabel)); + int slot = context.get(lhs.var); + context.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); + context.add(new Bytecode.Goto(target)); + context.add(new Bytecode.Label(exitLabel)); } else { - int result = generate(v, environment, block, forest, context); - block.add(new Bytecode.If(v.srcType.raw(), result, target), attributes(v)); + int result = generate(v, context); + context.add(new Bytecode.If(v.srcType.raw(), result, target), attributes(v)); } } } @@ -1484,8 +1450,7 @@ private void generateCondition(String target, Expr.BinOp v, Environment environm * appended. * @return */ - private void generateTypeCondition(String target, Expr.BinOp condition, Environment environment, - BytecodeForest.Block block, BytecodeForest forest, Context context) throws Exception { + private void generateTypeCondition(String target, Expr.BinOp condition, GenerationContext context) throws Exception { int leftOperand; if (condition.lhs instanceof Expr.LocalVariable) { @@ -1495,22 +1460,22 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Environm // variable (since, otherwise, we'll retype the temporary but not // the intended variable). Expr.LocalVariable lhs = (Expr.LocalVariable) condition.lhs; - if (environment.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context, condition.lhs); + if (context.get(lhs.var) == null) { + syntaxError(errorMessage(UNKNOWN_VARIABLE), context.enclosingDeclaration(), condition.lhs); } - leftOperand = environment.get(lhs.var); + leftOperand = context.get(lhs.var); } else { // This is the general case whether the lhs is an arbitrary variable // and, hence, retyping does not apply. Therefore, we can simply // evaluate the lhs into a temporary register as per usual. - leftOperand = generate(condition.lhs, environment, block, forest, context); + leftOperand = generate(condition.lhs, context); } // Note, the type checker guarantees that the rhs is a type val, so the // following cast is always safe. Expr.TypeVal rhs = (Expr.TypeVal) condition.rhs; - block.add(new Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); + context.add(new Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); } /** @@ -1536,8 +1501,7 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Environm * appended. * @return */ - private void generateCondition(String target, Expr.UnOp v, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) { + private void generateCondition(String target, Expr.UnOp v, GenerationContext context) { Expr.UOp uop = v.op; switch (uop) { case NOT: @@ -1546,13 +1510,13 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme // through case we branch to our true destination. String label = freshLabel(); - generateCondition(label, v.mhs, environment, block, forest, context); - block.add(new Bytecode.Goto(target)); - block.add(new Bytecode.Label(label)); + generateCondition(label, v.mhs, context); + context.add(new Bytecode.Goto(target)); + context.add(new Bytecode.Label(label)); return; default: // Nothing else is a valud boolean condition here. - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context, v); + syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context.enclosingDeclaration(), v); } } @@ -1577,55 +1541,53 @@ private void generateCondition(String target, Expr.UnOp v, Environment environme * appended. * @return */ - private void generateCondition(String target, Expr.Quantifier e, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) { + private void generateCondition(String target, Expr.Quantifier e, GenerationContext context) { String exit = freshLabel(); - generate(e.sources.iterator(), target, exit, e, environment, block, forest, context); + generate(e.sources.iterator(), target, exit, e, context); switch (e.cop) { case NONE: - block.add(new Bytecode.Goto(target)); - block.add(new Bytecode.Label(exit)); + context.add(new Bytecode.Goto(target)); + context.add(new Bytecode.Label(exit)); break; case SOME: break; case ALL: - block.add(new Bytecode.Goto(target)); - block.add(new Bytecode.Label(exit)); + context.add(new Bytecode.Goto(target)); + context.add(new Bytecode.Label(exit)); break; } } private void generate(Iterator> srcIterator, String trueLabel, String falseLabel, - Expr.Quantifier e, Environment environment, BytecodeForest.Block block, BytecodeForest forest, Context context) { + Expr.Quantifier e, GenerationContext context) { if (srcIterator.hasNext()) { // This is the inductive case (i.e. an outer loop) Triple src = srcIterator.next(); // First, determine the src slot. - int varSlot = environment.allocate(Type.T_INT, src.first()); - int startSlot = generate(src.second(), environment, block, forest, context); - int endSlot = generate(src.third(), environment, block, forest, context); + int varSlot = context.allocate(Type.T_INT, src.first()); + int startSlot = generate(src.second(), context); + int endSlot = generate(src.third(), context); // Second, recursively generate remaining parts - BytecodeForest.Block bodyBlock = new BytecodeForest.Block(); - int body = forest.add(bodyBlock); - generate(srcIterator, trueLabel, falseLabel, e, environment, bodyBlock, forest, context); + GenerationContext subcontext = context.createBlock(); + generate(srcIterator, trueLabel, falseLabel, e, subcontext); // Finally, create the forall loop bytecode - block.add(new Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], body), attributes(e)); + context.add(new Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], subcontext.blockIndex()), attributes(e)); } else { // This is the base case (i.e. the innermost loop) switch (e.cop) { case NONE: - generateCondition(falseLabel, e.condition, environment, block, forest, context); + generateCondition(falseLabel, e.condition, context); break; case SOME: - generateCondition(trueLabel, e.condition, environment, block, forest, context); + generateCondition(trueLabel, e.condition, context); break; case ALL: - generateCondition(falseLabel, invert(e.condition), environment, block, forest, context); + generateCondition(falseLabel, invert(e.condition), context); break; } } @@ -1635,48 +1597,45 @@ private void generate(Iterator> srcIterator, String t // Multi-Expressions // ========================================================================= - public int[] generate(Expr.Multi expression, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + public int[] generate(Expr.Multi expression, GenerationContext context) { List returns = expression.returns(); int[] targets = new int[returns.size()]; for (int i = 0; i != targets.length; ++i) { - targets[i] = environment.allocate(returns.get(i).raw()); + targets[i] = context.allocate(returns.get(i).raw()); } try { if (expression instanceof Expr.FunctionOrMethodCall) { Expr.FunctionOrMethodCall fmc = (Expr.FunctionOrMethodCall) expression; - generateStmt(fmc, environment, block, forest, context, targets); + generateStmt(fmc, context, targets); } else if (expression instanceof Expr.IndirectFunctionOrMethodCall) { Expr.IndirectFunctionOrMethodCall fmc = (Expr.IndirectFunctionOrMethodCall) expression; - generateStmt(fmc, environment, block, forest, context, targets); + generateStmt(fmc, context, targets); } else { // should be dead-code - internalFailure("unknown expression: " + expression.getClass().getName(), context, expression); + internalFailure("unknown expression: " + expression.getClass().getName(), context.enclosingDeclaration(), expression); } } catch (ResolveError rex) { - syntaxError(rex.getMessage(), context, expression, rex); + syntaxError(rex.getMessage(), context.enclosingDeclaration(), expression, rex); } catch (SyntaxError se) { throw se; } catch (Exception ex) { - internalFailure(ex.getMessage(), context, expression, ex); + internalFailure(ex.getMessage(), context.enclosingDeclaration(), expression, ex); } // done return targets; } - public void generateStmt(Expr.FunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context, int... targets) throws ResolveError { + public void generateStmt(Expr.FunctionOrMethodCall expr, GenerationContext context, int... targets) throws ResolveError { // - int[] operands = generate(expr.arguments, environment, block, forest, context); - block.add(new Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); + int[] operands = generate(expr.arguments, context); + context.add(new Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); } - public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context, int... targets) throws ResolveError { + public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, GenerationContext context, int... targets) throws ResolveError { // - int operand = generate(expr.src, environment, block, forest, context); - int[] operands = generate(expr.arguments, environment, block, forest, context); - block.add(new Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); + int operand = generate(expr.src, context); + int[] operands = generate(expr.arguments, context); + context.add(new Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); } // ========================================================================= @@ -1698,95 +1657,89 @@ public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, Environment env * * @return --- the register */ - public int generate(Expr expression, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + public int generate(Expr expression, GenerationContext context) { try { if (expression instanceof Expr.Constant) { - return generate((Expr.Constant) expression, environment, block, forest, context); + return generate((Expr.Constant) expression, context); } else if (expression instanceof Expr.LocalVariable) { - return generate((Expr.LocalVariable) expression, environment, block, forest, context); + return generate((Expr.LocalVariable) expression, context); } else if (expression instanceof Expr.ConstantAccess) { - return generate((Expr.ConstantAccess) expression, environment, block, forest, context); + return generate((Expr.ConstantAccess) expression, context); } else if (expression instanceof Expr.ArrayInitialiser) { - return generate((Expr.ArrayInitialiser) expression, environment, block, forest, context); + return generate((Expr.ArrayInitialiser) expression, context); } else if (expression instanceof Expr.ArrayGenerator) { - return generate((Expr.ArrayGenerator) expression, environment, block, forest, context); + return generate((Expr.ArrayGenerator) expression, context); } else if (expression instanceof Expr.BinOp) { - return generate((Expr.BinOp) expression, environment, block, forest, context); + return generate((Expr.BinOp) expression, context); } else if (expression instanceof Expr.Dereference) { - return generate((Expr.Dereference) expression, environment, block, forest, context); + return generate((Expr.Dereference) expression, context); } else if (expression instanceof Expr.Cast) { - return generate((Expr.Cast) expression, environment, block, forest, context); + return generate((Expr.Cast) expression, context); } else if (expression instanceof Expr.IndexOf) { - return generate((Expr.IndexOf) expression, environment, block, forest, context); + return generate((Expr.IndexOf) expression, context); } else if (expression instanceof Expr.UnOp) { - return generate((Expr.UnOp) expression, environment, block, forest, context); + return generate((Expr.UnOp) expression, context); } else if (expression instanceof Expr.FunctionOrMethodCall) { - return generate((Expr.FunctionOrMethodCall) expression, environment, block, forest, context); + return generate((Expr.FunctionOrMethodCall) expression, context); } else if (expression instanceof Expr.IndirectFunctionCall) { - return generate((Expr.IndirectFunctionCall) expression, environment, block, forest, context); + return generate((Expr.IndirectFunctionCall) expression, context); } else if (expression instanceof Expr.IndirectMethodCall) { - return generate((Expr.IndirectMethodCall) expression, environment, block, forest, context); + return generate((Expr.IndirectMethodCall) expression, context); } else if (expression instanceof Expr.Quantifier) { - return generate((Expr.Quantifier) expression, environment, block, forest, context); + return generate((Expr.Quantifier) expression, context); } else if (expression instanceof Expr.FieldAccess) { - return generate((Expr.FieldAccess) expression, environment, block, forest, context); + return generate((Expr.FieldAccess) expression, context); } else if (expression instanceof Expr.Record) { - return generate((Expr.Record) expression, environment, block, forest, context); + return generate((Expr.Record) expression, context); } else if (expression instanceof Expr.FunctionOrMethod) { - return generate((Expr.FunctionOrMethod) expression, environment, block, forest, context); + return generate((Expr.FunctionOrMethod) expression, context); } else if (expression instanceof Expr.Lambda) { - return generate((Expr.Lambda) expression, environment, block, forest, context); + return generate((Expr.Lambda) expression, context); } else if (expression instanceof Expr.New) { - return generate((Expr.New) expression, environment, block, forest, context); + return generate((Expr.New) expression, context); } else { // should be dead-code - internalFailure("unknown expression: " + expression.getClass().getName(), context, expression); + internalFailure("unknown expression: " + expression.getClass().getName(), context.enclosingDeclaration(), expression); } } catch (ResolveError rex) { - syntaxError(rex.getMessage(), context, expression, rex); + syntaxError(rex.getMessage(), context.enclosingDeclaration(), expression, rex); } catch (SyntaxError se) { throw se; } catch (Exception ex) { - internalFailure(ex.getMessage(), context, expression, ex); + internalFailure(ex.getMessage(), context.enclosingDeclaration(), expression, ex); } return -1; // deadcode } - public int generate(Expr.FunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) throws ResolveError { - int target = environment.allocate(expr.result().raw()); - generateStmt(expr, environment, block, forest, context, target); + public int generate(Expr.FunctionOrMethodCall expr, GenerationContext context) throws ResolveError { + int target = context.allocate(expr.result().raw()); + generateStmt(expr, context, target); return target; } - public int generate(Expr.IndirectFunctionOrMethodCall expr, Environment environment, BytecodeForest.Block block, - BytecodeForest forest, Context context) throws ResolveError { - int target = environment.allocate(expr.result().raw()); - generateStmt(expr, environment, block, forest, context, target); + public int generate(Expr.IndirectFunctionOrMethodCall expr, GenerationContext context) throws ResolveError { + int target = context.allocate(expr.result().raw()); + generateStmt(expr, context, target); return target; } - private int generate(Expr.Constant expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private int generate(Expr.Constant expr, GenerationContext context) { Constant val = expr.value; - int target = environment.allocate(val.type()); - block.add(new Bytecode.Const(target, expr.value), attributes(expr)); + int target = context.allocate(val.type()); + context.add(new Bytecode.Const(target, expr.value), attributes(expr)); return target; } - private int generate(Expr.FunctionOrMethod expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private int generate(Expr.FunctionOrMethod expr, GenerationContext context) { Type.FunctionOrMethod rawType = expr.type.raw(); Type.FunctionOrMethod nominalType = expr.type.nominal(); - int target = environment.allocate(rawType); - block.add(new Bytecode.Lambda(nominalType, target, new int[0], expr.nid), attributes(expr)); + int target = context.allocate(rawType); + context.add(new Bytecode.Lambda(nominalType, target, new int[0], expr.nid), attributes(expr)); return target; } - private int generate(Expr.Lambda expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private int generate(Expr.Lambda expr, GenerationContext context) { Type.FunctionOrMethod tfm = expr.type.raw(); List tfm_params = tfm.params(); List expr_params = expr.parameters; @@ -1794,34 +1747,32 @@ private int generate(Expr.Lambda expr, Environment environment, BytecodeForest.B // Create environment for the lambda body. ArrayList operands = new ArrayList(); ArrayList paramTypes = new ArrayList(); - BytecodeForest bodyForest = new BytecodeForest(); + GenerationContext lambdaContext = new GenerationContext(context.enclosingDeclaration()); List declarations = bodyForest.registers(); - Environment benv = new Environment(); + for (int i = 0; i != tfm_params.size(); ++i) { Type type = tfm_params.get(i); String name = expr_params.get(i).name; - benv.allocate(type, name); + lambdaContext.allocate(type, name); paramTypes.add(type); declarations.add(new BytecodeForest.Register(type, name)); } - for (Pair v : Exprs.uses(expr.body, context)) { - if (benv.get(v.second()) == null) { + for (Pair v : Exprs.uses(expr.body, context.enclosingDeclaration())) { + if (lambdaContext.get(v.second()) == null) { Type type = v.first(); - benv.allocate(type, v.second()); + lambdaContext.allocate(type, v.second()); paramTypes.add(type); - operands.add(environment.get(v.second())); + operands.add(context.get(v.second())); declarations.add(new BytecodeForest.Register(type, v.second())); } } // Generate body based on current environment - BytecodeForest.Block bodyBlock = new BytecodeForest.Block(); - bodyForest.addAsRoot(bodyBlock); if (tfm.returns().isEmpty()) { - bodyBlock.add(new Bytecode.Return(), attributes(expr)); + lambdaContext.add(new Bytecode.Return(), attributes(expr)); } else { - int target = generate(expr.body, benv, bodyBlock, bodyForest, context); - bodyBlock.add(new Bytecode.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), + int target = generate(expr.body, lambdaContext); + lambdaContext.add(new Bytecode.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), attributes(expr)); } @@ -1845,194 +1796,180 @@ private int generate(Expr.Lambda expr, Environment environment, BytecodeForest.B String name = "$lambda" + id; ArrayList modifiers = new ArrayList(); modifiers.add(Modifier.PRIVATE); - WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod(modifiers, name, cfm, bodyForest, 0, 0, + WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod(modifiers, name, cfm, lambdaContext.forest(), 0, 0, attributes(expr)); lambdas.add(lambda); Path.ID mid = context.file().module; NameID nid = new NameID(mid, name); // Finally, create the lambda - int target = environment.allocate(tfm); - block.add(new Bytecode.Lambda(cfm, target, toIntArray(operands), nid), attributes(expr)); + int target = context.allocate(tfm); + context.add(new Bytecode.Lambda(cfm, target, toIntArray(operands), nid), attributes(expr)); return target; } - private int generate(Expr.ConstantAccess expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) throws ResolveError { + private int generate(Expr.ConstantAccess expr, GenerationContext context) throws ResolveError { Constant val = expr.value; - int target = environment.allocate(val.type()); - block.add(new Bytecode.Const(target, val), attributes(expr)); + int target = context.allocate(val.type()); + context.add(new Bytecode.Const(target, val), attributes(expr)); return target; } - private int generate(Expr.LocalVariable expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) throws ResolveError { + private int generate(Expr.LocalVariable expr, GenerationContext context) throws ResolveError { - if (environment.get(expr.var) != null) { - int target = environment.get(expr.var); + if (context.get(expr.var) != null) { + int target = context.get(expr.var); Type type = expr.result().raw(); return target; } else { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), context, expr); + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), context.enclosingDeclaration(), expr); return -1; } } - private int generate(Expr.UnOp expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int[] operands = new int[] { generate(expr.mhs, environment, block, forest, context) }; - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; + private int generate(Expr.UnOp expr, GenerationContext context) { + int[] operands = new int[] { generate(expr.mhs, context) }; + int[] targets = new int[] { context.allocate(expr.result().raw()) }; switch (expr.op) { case NEG: - block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), + context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), attributes(expr)); break; case INVERT: - block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), + context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), attributes(expr)); break; case NOT: - block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NOT), + context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NOT), attributes(expr)); break; case ARRAYLENGTH: - block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); + context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); break; default: // should be dead-code - internalFailure("unexpected unary operator encountered", context, expr); + internalFailure("unexpected unary operator encountered", context.enclosingDeclaration(), expr); return -1; } return targets[0]; } - private int generate(Expr.Dereference expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int[] operands = new int[] { generate(expr.src, environment, block, forest, context) }; - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), + private int generate(Expr.Dereference expr, GenerationContext context) { + int[] operands = new int[] { generate(expr.src, context) }; + int[] targets = new int[] { context.allocate(expr.result().raw()) }; + context.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), attributes(expr)); return targets[0]; } - private int generate(Expr.IndexOf expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int[] operands = { generate(expr.src, environment, block, forest, context), - generate(expr.index, environment, block, forest, context) }; - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); + private int generate(Expr.IndexOf expr, GenerationContext context) { + int[] operands = { generate(expr.src, context), + generate(expr.index, context) }; + int[] targets = new int[] { context.allocate(expr.result().raw()) }; + context.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); return targets[0]; } - private int generate(Expr.Cast expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int operand = generate(expr.expr, environment, block, forest, context); + private int generate(Expr.Cast expr, GenerationContext context) { + int operand = generate(expr.expr, context); Type from = expr.expr.result().raw(); Type to = expr.result().raw(); - int target = environment.allocate(to); - block.add(new Bytecode.Convert(from, target, operand, to), attributes(expr)); + int target = context.allocate(to); + context.add(new Bytecode.Convert(from, target, operand, to), attributes(expr)); return target; } - private int generate(Expr.BinOp v, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) throws Exception { + private int generate(Expr.BinOp v, GenerationContext context) throws Exception { // could probably use a range test for this somehow if(v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { String trueLabel = freshLabel(); String exitLabel = freshLabel(); - generateCondition(trueLabel, v, environment, block, forest, context); - int target = environment.allocate(Type.T_BOOL); - block.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(v)); - block.add(new Bytecode.Goto(exitLabel)); - block.add(new Bytecode.Label(trueLabel)); - block.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(v)); - block.add(new Bytecode.Label(exitLabel)); + generateCondition(trueLabel, v, context); + int target = context.allocate(Type.T_BOOL); + context.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(v)); + context.add(new Bytecode.Goto(exitLabel)); + context.add(new Bytecode.Label(trueLabel)); + context.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(v)); + context.add(new Bytecode.Label(exitLabel)); return target; } else { Type result = v.result().raw(); - int[] targets = new int[] { environment.allocate(result) }; + int[] targets = new int[] { context.allocate(result) }; int[] operands = { - generate(v.lhs, environment, block, forest, context), - generate(v.rhs, environment, block, forest, context) + generate(v.lhs, context), + generate(v.rhs, context) }; - block.add(new Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context)), attributes(v)); + context.add(new Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context.enclosingDeclaration())), attributes(v)); return targets[0]; } } - private int generate(Expr.ArrayInitialiser expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int[] operands = generate(expr.arguments, environment, block, forest, context); - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), + private int generate(Expr.ArrayInitialiser expr, GenerationContext context) { + int[] operands = generate(expr.arguments, context); + int[] targets = new int[] { context.allocate(expr.result().raw()) }; + context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), attributes(expr)); return targets[0]; } - private int generate(Expr.ArrayGenerator expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int[] operands = new int[] { generate(expr.element, environment, block, forest, context), - generate(expr.count, environment, block, forest, context) }; - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); + private int generate(Expr.ArrayGenerator expr, GenerationContext context) { + int[] operands = new int[] { generate(expr.element, context), + generate(expr.count, context) }; + int[] targets = new int[] { context.allocate(expr.result().raw()) }; + context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); return targets[0]; } - private int generate(Expr.Quantifier e, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private int generate(Expr.Quantifier e, GenerationContext context) { String trueLabel = freshLabel(); String exitLabel = freshLabel(); - generateCondition(trueLabel, e, environment, block, forest, context); - int target = environment.allocate(Type.T_BOOL); - block.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(e)); - block.add(new Bytecode.Goto(exitLabel)); - block.add(new Bytecode.Label(trueLabel)); - block.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(e)); - block.add(new Bytecode.Label(exitLabel)); + generateCondition(trueLabel, e, context); + int target = context.allocate(Type.T_BOOL); + context.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(e)); + context.add(new Bytecode.Goto(exitLabel)); + context.add(new Bytecode.Label(trueLabel)); + context.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(e)); + context.add(new Bytecode.Label(exitLabel)); return target; } - private int generate(Expr.Record expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private int generate(Expr.Record expr, GenerationContext context) { ArrayList keys = new ArrayList(expr.fields.keySet()); Collections.sort(keys); int[] operands = new int[expr.fields.size()]; for (int i = 0; i != operands.length; ++i) { String key = keys.get(i); Expr arg = expr.fields.get(key); - operands[i] = generate(arg, environment, block, forest, context); + operands[i] = generate(arg, context); } - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), + int[] targets = new int[] { context.allocate(expr.result().raw()) }; + context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), attributes(expr)); return targets[0]; } - private int generate(Expr.FieldAccess expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { - int operand = generate(expr.src, environment, block, forest, context); - int target = environment.allocate(expr.result().raw()); - block.add(new Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), + private int generate(Expr.FieldAccess expr, GenerationContext context) { + int operand = generate(expr.src, context); + int target = context.allocate(expr.result().raw()); + context.add(new Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), attributes(expr)); return target; } - private int generate(Expr.New expr, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) throws ResolveError { - int[] operands = new int[] { generate(expr.expr, environment, block, forest, context) }; - int[] targets = new int[] { environment.allocate(expr.result().raw()) }; - block.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); + private int generate(Expr.New expr, GenerationContext context) throws ResolveError { + int[] operands = new int[] { generate(expr.expr, context) }; + int[] targets = new int[] { context.allocate(expr.result().raw()) }; + context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); return targets[0]; } - private int[] generate(List arguments, Environment environment, BytecodeForest.Block block, BytecodeForest forest, - Context context) { + private int[] generate(List arguments, GenerationContext context) { int[] operands = new int[arguments.size()]; for (int i = 0; i != operands.length; ++i) { Expr arg = arguments.get(i); - operands[i] = generate(arg, environment, block, forest, context); + operands[i] = generate(arg, context); } return operands; } @@ -2238,6 +2175,120 @@ public static String freshLabel() { return "blklab" + _idx++; } + private static final class GenerationContext { + /** + * Maps variables to their WyIL register number and type. + */ + private final Environment environment; + /** + * The outermost forest (needed for creating new subblocks). + */ + private final BytecodeForest forest; + /** + * The enclosing source file context (needed for error reporting) + */ + private final WhileyFile.Context context; + /** + * The enclosing bytecode block into which bytecodes are being written. + */ + private final BytecodeForest.Block block; + /** + * Get the index of the bytecode block into which bytecodes are being written + */ + private final int blockIndex; + + /** + * Get the target for any continue statement encountered + */ + private final String continueLabel; + + /** + * Get the target for any break statement encountered + */ + private final String breakLabel; + + public GenerationContext(WhileyFile.Context context) { + this(new Environment(), new BytecodeForest(), context, -1); + } + + private GenerationContext(Environment environment, BytecodeForest forest, WhileyFile.Context context, int blockIndex) { + this(environment,forest,context,blockIndex,null,null); + } + + private GenerationContext(Environment environment, BytecodeForest forest, WhileyFile.Context context, + int blockIndex, String breakLabel, String continueLabel) { + this.environment = environment; + this.forest = forest; + this.context = context; + this.blockIndex = blockIndex; + this.block = blockIndex == -1 ? null : forest.get(blockIndex); + this.breakLabel = breakLabel; + this.continueLabel = continueLabel; + } + + public int blockIndex() { + return blockIndex; + } + + public BytecodeForest forest() { + return forest; + } + + public WhileyFile.Context enclosingDeclaration() { + return context; + } + + public String getBreakLabel() { + return breakLabel; + } + + public String getContinueLabel() { + return breakLabel; + } + + public Nominal.FunctionOrMethod getEnclosingFunctionType() { + WhileyFile.FunctionOrMethod m = (WhileyFile.FunctionOrMethod) context; + return m.resolvedType(); + } + + public Integer get(String name) { + return environment.get(name); + } + + public int allocate(Type t) { + return environment.allocate(t); + } + + public int allocate(Type t, String name) { + return environment.allocate(t,name); + } + + public void add(Bytecode b) { + block.add(b); + } + + public void add(Bytecode b, List attributes) { + block.add(b,attributes); + } + + public GenerationContext createRootBlock() { + BytecodeForest.Block block = new BytecodeForest.Block(); + int index = forest.addAsRoot(block); + return new GenerationContext(environment,forest,context,index); + } + + public GenerationContext createBlock() { + BytecodeForest.Block block = new BytecodeForest.Block(); + int index = forest.add(block); + return new GenerationContext(environment,forest,context,index); + } + + public GenerationContext createBlock(String breakLabel, String continueLabel) { + BytecodeForest.Block block = new BytecodeForest.Block(); + int index = forest.add(block); + return new GenerationContext(environment,forest,context,index,breakLabel,continueLabel); + } + } /** * Maintains a mapping from Variable names to their allocated register slot, @@ -2311,46 +2362,4 @@ public String toString() { return idx2type.toString() + "," + var2idx.toString(); } } - - private T findEnclosingScope(Class c) { - for (int i = scopes.size() - 1; i >= 0; --i) { - Scope s = scopes.get(i); - if (c.isInstance(s)) { - return (T) s; - } - } - return null; - } - - /** - * Respresents the scope of a statement or block (e.g. for a While or For - * loop). The allows additional information to be retained about that scope - * and passed down to statements which need it (e.g. break / continued - * statements). It also allows such statements to determine what the - * "nearest" enclosing scope is. - * - * @author David J. Pearce - * - */ - private abstract class Scope { - } - - /** - * A scope representing a given loop statement, allowing contained - * statements to determine where the break and continue statements should be - * directed. - * - * - * @author David J. Pearce - * - */ - private class LoopScope extends Scope { - public final String continueLabel; - public final String breakLabel; - - public LoopScope(String cl, String bl) { - continueLabel = cl; - breakLabel = bl; - } - } } From 9c7e6e16413a7ea143a93eb1f61defc908cb096e Mon Sep 17 00:00:00 2001 From: DavePearce Date: Tue, 19 Apr 2016 21:34:33 +1200 Subject: [PATCH 30/43] WyC: Finished refactoring CodeGenerator #621 Have simplified the variable allocation process by dropping the requirement that all local variables are allocated first. This led to some useful simplifications. Lambda bytecodes remain something of a thorn as their translation is really ugly. I've opened a new issue to fix them by using a nested block instead of current hack. --- .../wyc/src/wyc/builder/CodeGenerator.java | 1278 +++++++---------- modules/wyc/src/wyc/lang/Exprs.java | 8 +- .../testing/AllValidVerificationTests.java | 3 - 3 files changed, 516 insertions(+), 773 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 204a5acdab..2bc5f794ef 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -26,7 +26,6 @@ package wyc.builder; import java.util.*; -import java.util.jar.Attributes; import static wyc.lang.WhileyFile.internalFailure; import static wyc.lang.WhileyFile.syntaxError; @@ -42,7 +41,6 @@ import wycc.util.ResolveError; import wycc.util.Triple; import wyfs.lang.Path; -import wyil.attributes.VariableDeclarations; import wyil.lang.*; /** @@ -91,14 +89,6 @@ public final class CodeGenerator { */ private final ArrayList lambdas = new ArrayList(); - /** - * The scopes stack is used for determining the correct scoping for continue - * and break statements. Whenever we begin translating a loop of some kind, - * a LoopScope is pushed on the stack. Once the translation of - * that loop is complete, this is then popped off the stack. - */ - private Stack scopes = new Stack(); - /** * Construct a code generator object for translating WhileyFiles into * WyilFiles. @@ -183,17 +173,15 @@ private WyilFile.Constant generate(WhileyFile.Constant cd) { * @throws Exception */ private WyilFile.Type generate(WhileyFile.Type td) throws Exception { - GenerationContext context = new GenerationContext(td); + EnclosingScope scope = new EnclosingScope(td); // Allocate declared parameter - context.allocate(td.resolvedType.raw(), td.parameter.name()); + scope.allocate(td.resolvedType, td.parameter.name()); // Generate code for each invariant condition for (Expr invariant : td.invariant) { - generateInvariantBlock(invariant, context.createRootBlock()); + generateInvariantBlock(invariant, scope.createRootBlock()); } - // Add all registers used within the invariant - forest.registers().addAll(environment.asRegisters()); // done - return new WyilFile.Type(td.modifiers(), td.name(), td.resolvedType.nominal(), context.forest()); + return new WyilFile.Type(td.modifiers(), td.name(), td.resolvedType.nominal(), scope.getForest()); } // ========================================================================= @@ -201,73 +189,51 @@ private WyilFile.Type generate(WhileyFile.Type td) throws Exception { // ========================================================================= private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throws Exception { - // Type.FunctionOrMethod rawFnType = fd.resolvedType().raw(); - Type.FunctionOrMethod nominalFnType = fd.resolvedType().nominal(); - // ================================================================== // Construct environments // ================================================================== - - GenerationContext context = new GenerationContext(fd); - ArrayList declarations = new ArrayList(); - addDeclaredParameters(fd.parameters, fd.resolvedType().params(), environment, declarations); - addDeclaredParameters(fd.returns, fd.resolvedType().returns(), environment, declarations); - // Allocate all declared variables now. This ensures that all declared - // variables occur before any temporary variables. - buildVariableDeclarations(fd.statements, declarations, environment, fd); + EnclosingScope scope = new EnclosingScope(fd); + addDeclaredParameters(fd.parameters, fd.resolvedType().params(), scope); + addDeclaredParameters(fd.returns, fd.resolvedType().returns(), scope); // ================================================================== // Generate pre-condition // ================================================================== for (Expr precondition : fd.requires) { - generateInvariantBlock(precondition, context.createRootBlock()); + generateInvariantBlock(precondition, scope.createRootBlock()); } // ================================================================== // Generate post-condition // ================================================================== for (Expr postcondition : fd.ensures) { - generateInvariantBlock(postcondition, context.createRootBlock()); + generateInvariantBlock(postcondition, scope.createRootBlock()); } // ================================================================== // Generate body // ================================================================== - // First, reset the environment. This is necessary because the - // environment may have been contaminated with local variables arising - // in the precondition or postcondition. Such variables are possible if - // quantifiers are used. - - // FIXME: using resetEnvironment feels ugly and could fail when multiple - // variables of the same name are declared in the body of a function - // or method (currently this is only possible via quantifiers, but won't - // fail because they have the same type). - environment = resetEnvironment(environment, declarations); - - context = context.createRootBlock(); + scope = scope.createRootBlock(); for (Stmt s : fd.statements) { - generate(s, context); + generate(s, scope); } // The following is sneaky. It guarantees that every method ends in a // return. For methods that actually need a value, this is either // removed as dead-code or remains and will cause an error. - context.add(new Bytecode.Return(), attributes(fd)); + scope.add(new Bytecode.Return(), attributes(fd)); WyilFile.FunctionOrMethod declaration; - // Second, add the corresponding attribute to the enclosing method. - forest.registers().addAll(createVariableDeclarations(environment, declarations)); - if (fd instanceof WhileyFile.Function) { WhileyFile.Function f = (WhileyFile.Function) fd; declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), f.resolvedType.nominal(), - context.forest(), fd.requires.size(), fd.ensures.size()); + scope.getForest(), fd.requires.size(), fd.ensures.size()); } else { WhileyFile.Method md = (WhileyFile.Method) fd; declaration = new WyilFile.FunctionOrMethod(fd.modifiers(), fd.name(), md.resolvedType.nominal(), - context.forest(), fd.requires.size(), fd.ensures.size()); + scope.getForest(), fd.requires.size(), fd.ensures.size()); } // Done. @@ -279,36 +245,15 @@ private WyilFile.FunctionOrMethod generate(WhileyFile.FunctionOrMethod fd) throw * precondition, postcondition or type invariant. * * @param invariant - * @param environment - * @param forest - * @param context + * @param scope */ - private int generateInvariantBlock(Expr invariant, GenerationContext context) { + private int generateInvariantBlock(Expr invariant, EnclosingScope scope) { String endLab = freshLabel(); - generateCondition(endLab, invariant, context); - context.add(new Bytecode.Fail(), attributes(invariant)); - context.add(new Bytecode.Label(endLab)); - context.add(new Bytecode.Return()); - return context.blockIndex(); - } - - /** - * Construct register declarations for this function or method. The register - * declarations stores information about the names and declared types of all - * registers. Technically speaking, this information is not necessary to - * compile and run a Whiley program. However, it is very useful for - * debugging and performing verification. - */ - private List createVariableDeclarations(Environment environment, - List declarations) { - // FIXME: this is a hack. In essence, we're trying to get the types of - // all intermediate registers used in code generation. To do this, we're - // looking at their type having typed the entire function. - for (int i = declarations.size(); i < environment.size(); i = i + 1) { - Type t = environment.type(i); - declarations.add(new BytecodeForest.Register(t, null)); - } - return declarations; + generateCondition(endLab, invariant, scope); + scope.add(new Bytecode.Fail(), attributes(invariant)); + scope.add(new Bytecode.Label(endLab)); + scope.add(new Bytecode.Return()); + return scope.blockIndex(); } /** @@ -322,13 +267,11 @@ private List createVariableDeclarations(Environment env * --- List of declarations being constructed */ private void addDeclaredParameters(List parameters, List types, - Environment environment, List declarations) { + EnclosingScope scope) { for (int i = 0; i != parameters.size(); ++i) { WhileyFile.Parameter parameter = parameters.get(i); // allocate parameter to register in the current block - declarations.add(new BytecodeForest.Register(types.get(i).nominal(), parameter.name)); - // allocate parameter to register in the current block - context.allocate(types.get(i).raw(), parameter.name); + scope.allocate(types.get(i), parameter.name); } } @@ -342,64 +285,56 @@ private void addDeclaredParameters(List parameters, List operands = new ArrayList(); @@ -506,11 +431,11 @@ private void generate(Assign s, GenerationContext context) { for (Nominal t : me.returns()) { types.add(t.raw()); } - operands.addAll(toIntegerList(generate(me, context))); + operands.addAll(toIntegerList(generate(me, scope))); } else { // The assigned rval is a simple expression which returns a // single value - operands.add(generate(e, context)); + operands.add(generate(e, scope)); types.add(e.result().raw()); } } @@ -521,19 +446,19 @@ private void generate(Assign s, GenerationContext context) { // FlowTypeChecker. for (int i = 0; i != s.lvals.size(); ++i) { Expr.LVal lval = s.lvals.get(i); - generateAssignment(lval, operands.get(i), types.get(i), context); + generateAssignment(lval, operands.get(i), types.get(i), scope); } } - public void generateAssignment(Expr.LVal lval, int operand, Type type, GenerationContext context) { + public void generateAssignment(Expr.LVal lval, int operand, Type type, EnclosingScope scope) { if (lval instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) lval; // This is the easiest case. Having translated the right-hand side // expression, we now assign it directly to the register allocated // for variable on the left-hand side. - int[] targets = new int[] { context.get(v.var) }; + int[] targets = new int[] { scope.get(v.var) }; int[] operands = new int[] { operand }; - context.add(new Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); + scope.add(new Bytecode.Operator(type, targets, operands, Bytecode.OperatorKind.ASSIGN), attributes(lval)); } else if (lval instanceof Expr.IndexOf || lval instanceof Expr.FieldAccess || lval instanceof Expr.Dereference) { // This is the more complicated case, since the left-hand side @@ -544,12 +469,12 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Generatio // updated. ArrayList fields = new ArrayList(); ArrayList operands = new ArrayList(); - Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, context); - int target = context.get(lhs.var); - context.add(new Bytecode.Update(lhs.type.raw(), target, toIntArray(operands), operand, lhs.afterType.raw(), + Expr.AssignedVariable lhs = extractLVal(lval, fields, operands, scope); + int target = scope.get(lhs.var); + scope.add(new Bytecode.Update(lhs.type.raw(), target, toIntArray(operands), operand, lhs.afterType.raw(), fields), attributes(lval)); } else { - WhileyFile.syntaxError("invalid assignment", context.enclosingDeclaration(), lval); + WhileyFile.syntaxError("invalid assignment", scope.getSourceContext(), lval); } } @@ -571,38 +496,32 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Generatio * The list of temporary registers in which evaluated index * expression are stored. Initially, this is empty and is filled * by this method as it traverses the lval. - * @param environment - * Mapping from variable names to block registers. - * @param block - * Code block into which this statement is to be translated. - * @param context - * Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * Enclosing scope of this statement. * @return */ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, ArrayList operands, - GenerationContext context) { + EnclosingScope scope) { if (e instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) e; return v; } else if (e instanceof Expr.Dereference) { Expr.Dereference pa = (Expr.Dereference) e; - return extractLVal(pa.src, fields, operands, context); + return extractLVal(pa.src, fields, operands, scope); } else if (e instanceof Expr.IndexOf) { Expr.IndexOf la = (Expr.IndexOf) e; - int operand = generate(la.index, context); - Expr.AssignedVariable l = extractLVal(la.src, fields, operands, context); + int operand = generate(la.index, scope); + Expr.AssignedVariable l = extractLVal(la.src, fields, operands, scope); operands.add(operand); return l; } else if (e instanceof Expr.FieldAccess) { Expr.FieldAccess ra = (Expr.FieldAccess) e; - Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, context); + Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, scope); fields.add(ra.name); return r; } else { - WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), context.enclosingDeclaration(), e); + WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), scope.getSourceContext(), e); return null; // dead code } } @@ -612,25 +531,19 @@ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, Arra * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Assert s, GenerationContext context) { + private void generate(Stmt.Assert s, EnclosingScope scope) { // First, create assert block body - GenerationContext subcontext = context.createBlock(); + EnclosingScope subscope = scope.createBlock(); String endLab = freshLabel(); - generateCondition(endLab, s.expr, subcontext); - subcontext.add(new Bytecode.Fail(), attributes(s.expr)); - subcontext.add(new Bytecode.Label(endLab)); + generateCondition(endLab, s.expr, subscope); + subscope.add(new Bytecode.Fail(), attributes(s.expr)); + subscope.add(new Bytecode.Label(endLab)); // Second, create assert bytecode - context.add(new Bytecode.Assert(subcontext.blockIndex()), attributes(s)); + scope.add(new Bytecode.Assert(subscope.blockIndex()), attributes(s)); } /** @@ -638,25 +551,19 @@ private void generate(Stmt.Assert s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Assume s, GenerationContext context) { + private void generate(Stmt.Assume s, EnclosingScope scope) { // First, create assume block body - GenerationContext subcontext = context.createBlock(); + EnclosingScope subscope = scope.createBlock(); String endLab = freshLabel(); - generateCondition(endLab, s.expr, subcontext); - subcontext.add(new Bytecode.Fail(), attributes(s.expr)); - subcontext.add(new Bytecode.Label(endLab)); + generateCondition(endLab, s.expr, subscope); + subscope.add(new Bytecode.Fail(), attributes(s.expr)); + subscope.add(new Bytecode.Label(endLab)); // Second, create assert bytecode - context.add(new Bytecode.Assume(subcontext.blockIndex()), attributes(s)); + scope.add(new Bytecode.Assume(subscope.blockIndex()), attributes(s)); } /** @@ -682,23 +589,17 @@ private void generate(Stmt.Assume s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Return s, GenerationContext context) { + private void generate(Stmt.Return s, EnclosingScope scope) { List returns = s.returns; // Here, we don't put the type propagated for the return expression. // Instead, we use the declared return type of this function. This // has the effect of forcing an implicit coercion between the // actual value being returned and its required type. - List returnTypes = context.getEnclosingFunctionType().raw().returns(); + List returnTypes = scope.getEnclosingFunctionType().raw().returns(); Type[] types = returnTypes.toArray(new Type[returnTypes.size()]); int[] operands = new int[types.length]; int index = 0; @@ -706,15 +607,15 @@ private void generate(Stmt.Return s, GenerationContext context) { Expr e = returns.get(i); // FIXME: this is a rather ugly if (e instanceof Expr.Multi) { - int[] results = generate((Expr.Multi) e, context); + int[] results = generate((Expr.Multi) e, scope); for (int r : results) { operands[index++] = r; } } else { - operands[index++] = generate(e, context); + operands[index++] = generate(e, scope); } } - context.add(new Bytecode.Return(types, operands), attributes(s)); + scope.add(new Bytecode.Return(types, operands), attributes(s)); } /** @@ -722,17 +623,11 @@ private void generate(Stmt.Return s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Skip s, GenerationContext context) { + private void generate(Stmt.Skip s, EnclosingScope scope) { // TODO: should actually generate a NOP bytecode. This is an assignment // from zero operands to zero targets. At the moment, I cannot encode // this however because it will fail in the interpreter. @@ -759,19 +654,13 @@ private void generate(Stmt.Skip s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Debug s, GenerationContext context) { - int operand = generate(s.expr, context); - context.add(new Bytecode.Debug(operand), attributes(s)); + private void generate(Stmt.Debug s, EnclosingScope scope) { + int operand = generate(s.expr, scope); + scope.add(new Bytecode.Debug(operand), attributes(s)); } /** @@ -789,18 +678,12 @@ private void generate(Stmt.Debug s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param codes - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Fail s, GenerationContext context) { - context.add(new Bytecode.Fail(), attributes(s)); + private void generate(Stmt.Fail s, EnclosingScope scope) { + scope.add(new Bytecode.Fail(), attributes(s)); } /** @@ -838,35 +721,34 @@ private void generate(Stmt.Fail s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.IfElse s, GenerationContext context) { - + private void generate(Stmt.IfElse s, EnclosingScope scope) { + // We need to clone the scope's here to isolate variables declared in + // the true/false branches from the enclosing scope. In particular, + // the case where two variables of the same name are declared with + // different types. + EnclosingScope trueScope = scope.clone(); + EnclosingScope falseScope = scope.clone(); String falseLab = freshLabel(); String exitLab = s.falseBranch.isEmpty() ? falseLab : freshLabel(); - generateCondition(falseLab, invert(s.condition), context); + generateCondition(falseLab, invert(s.condition), scope); for (Stmt st : s.trueBranch) { - generate(st, context); + generate(st, trueScope); } if (!s.falseBranch.isEmpty()) { - context.add(new Bytecode.Goto(exitLab)); - context.add(new Bytecode.Label(falseLab)); + scope.add(new Bytecode.Goto(exitLab)); + scope.add(new Bytecode.Label(falseLab)); for (Stmt st : s.falseBranch) { - generate(st, context); + generate(st, falseScope); } } - context.add(new Bytecode.Label(exitLab)); + scope.add(new Bytecode.Label(exitLab)); } /** @@ -904,23 +786,17 @@ private void generate(Stmt.IfElse s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Break s, GenerationContext context) { - String breakLabel = context.getBreakLabel(); + private void generate(Stmt.Break s, EnclosingScope scope) { + String breakLabel = scope.getBreakLabel(); if (breakLabel == null) { // FIXME: this should be located elsewhere - WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), context.enclosingDeclaration(), s); + WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), scope.getSourceContext(), s); } - context.add(new Bytecode.Goto(breakLabel)); + scope.add(new Bytecode.Goto(breakLabel)); } /** @@ -961,22 +837,16 @@ private void generate(Stmt.Break s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Continue s, GenerationContext context) { - String continueLabel = context.getContinueLabel(); + private void generate(Stmt.Continue s, EnclosingScope scope) { + String continueLabel = scope.getContinueLabel(); if (continueLabel == null) { - WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), context.enclosingDeclaration(), s); + WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), scope.getSourceContext(), s); } - context.add(new Bytecode.Goto(continueLabel)); + scope.add(new Bytecode.Goto(continueLabel)); } /** @@ -1023,23 +893,17 @@ private void generate(Stmt.Continue s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.Switch s, GenerationContext context) throws Exception { + private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { String exitLab = freshLabel(); - int operand = generate(s.expr, context); + int operand = generate(s.expr, scope); String defaultTarget = exitLab; HashSet values = new HashSet<>(); ArrayList> cases = new ArrayList<>(); - int start = block.size(); + int start = scope.getBlock().size(); for (Stmt.Case c : s.cases) { if (c.expr.isEmpty()) { @@ -1047,19 +911,22 @@ private void generate(Stmt.Switch s, GenerationContext context) throws Exception // must check that we have not already seen a case with an empty // match (otherwise, we'd have two default labels ;) if (defaultTarget != exitLab) { - WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), context.enclosingDeclaration(), c); + WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), scope.getSourceContext(), c); } else { defaultTarget = freshLabel(); - context.add(new Bytecode.Label(defaultTarget), attributes(c)); + scope.add(new Bytecode.Label(defaultTarget), attributes(c)); + // We need to clone the scope here to isolate variables + // declared in the default block from the enclosing scope + EnclosingScope defaultScope = scope.clone(); for (Stmt st : c.stmts) { - generate(st, context); + generate(st, defaultScope); } - context.add(new Bytecode.Goto(exitLab), attributes(c)); + scope.add(new Bytecode.Goto(exitLab), attributes(c)); } } else if (defaultTarget == exitLab) { String target = freshLabel(); - context.add(new Bytecode.Label(target), attributes(c)); + scope.add(new Bytecode.Label(target), attributes(c)); // Case statements in Whiley may have multiple matching constant // values. Therefore, we iterate each matching value and @@ -1070,27 +937,30 @@ private void generate(Stmt.Switch s, GenerationContext context) throws Exception // Check whether this case constant has already been used as // a case constant elsewhere. If so, then report an error. if (values.contains(constant)) { - WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), context.enclosingDeclaration(), c); + // FIXME: this should be located elsewhere + WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), scope.getSourceContext(), c); } cases.add(new Pair<>(constant, target)); values.add(constant); } - + // We need to clone the scope here to isolate variables + // declared in the case block from the enclosing scope + EnclosingScope caseScope = scope.clone(); for (Stmt st : c.stmts) { - generate(st, context); + generate(st, caseScope); } - context.add(new Bytecode.Goto(exitLab), attributes(c)); + scope.add(new Bytecode.Goto(exitLab), attributes(c)); } else { // This represents the case where we have another non-default // case after the default case. Such code cannot be executed, // and is therefore reported as an error. - WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), context.enclosingDeclaration(), c); + WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), scope.getSourceContext(), c); } } - context.add(start, new Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); - context.add(new Bytecode.Label(exitLab), attributes(s)); + scope.add(start, new Bytecode.Switch(s.expr.result().raw(), operand, defaultTarget, cases), attributes(s)); + scope.add(new Bytecode.Label(exitLab), attributes(s)); } /** @@ -1124,17 +994,11 @@ private void generate(Stmt.Switch s, GenerationContext context) throws Exception * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.While s, GenerationContext context) { + private void generate(Stmt.While s, EnclosingScope scope) { // A label marking where execution continues after the while // loop finishes. Used when the loop condition evaluates to false // or when a break statement is encountered. @@ -1143,22 +1007,22 @@ private void generate(Stmt.While s, GenerationContext context) { // by the continue statement. String continueLab = freshLabel(); - GenerationContext subcontext = context.createBlock(exitLab,continueLab); + EnclosingScope subscope = scope.createBlock(exitLab,continueLab); for (Expr condition : s.invariants) { - int invariant = generateInvariantBlock(condition,subcontext.createBlock()); - subcontext.add(new Bytecode.Invariant(invariant), attributes(condition)); + int invariant = generateInvariantBlock(condition,subscope.createBlock()); + subscope.add(new Bytecode.Invariant(invariant), attributes(condition)); } - generateCondition(exitLab, invert(s.condition), subcontext); + generateCondition(exitLab, invert(s.condition), subscope); for (Stmt st : s.body) { - generate(st, subcontext); + generate(st, subscope); } - subcontext.add(new Bytecode.Label(continueLab), attributes(s)); - context.add(new Bytecode.Loop(new int[] {}, subcontext.blockIndex()), attributes(s)); - context.add(new Bytecode.Label(exitLab), attributes(s)); + subscope.add(new Bytecode.Label(continueLab), attributes(s)); + scope.add(new Bytecode.Loop(new int[] {}, subscope.blockIndex()), attributes(s)); + scope.add(new Bytecode.Label(exitLab), attributes(s)); } /** @@ -1193,17 +1057,11 @@ private void generate(Stmt.While s, GenerationContext context) { * * @param stmt * --- Statement to be translated. - * @param environment - * --- Mapping from variable names to block registers. - * @param block - * --- Code block into which this statement is to be translated. - * @param context - * --- Enclosing context of this statement (i.e. type, constant, - * function or method declaration). The context is used to aid - * with error reporting as it determines the enclosing file. + * @param scope + * --- Enclosing scope of this statement. * @return */ - private void generate(Stmt.DoWhile s, GenerationContext context) { + private void generate(Stmt.DoWhile s, EnclosingScope scope) { // A label marking where execution continues after the do-while // loop finishes. Used when the loop condition evaluates to false // or when a break statement is encountered. @@ -1212,22 +1070,22 @@ private void generate(Stmt.DoWhile s, GenerationContext context) { // by the continue statement. String continueLab = freshLabel(); - GenerationContext subcontext = context.createBlock(exitLab,continueLab); + EnclosingScope subscope = scope.createBlock(exitLab,continueLab); for (Stmt st : s.body) { - generate(st, subcontext.createBlock()); + generate(st, subscope); } for (Expr condition : s.invariants) { - int invariant = generateInvariantBlock(condition, subcontext); - subcontext.add(new Bytecode.Invariant(invariant), attributes(condition)); + int invariant = generateInvariantBlock(condition, subscope.createBlock()); + subscope.add(new Bytecode.Invariant(invariant), attributes(condition)); } - subcontext.add(new Bytecode.Label(continueLab), attributes(s)); - generateCondition(exitLab, invert(s.condition), subcontext); + subscope.add(new Bytecode.Label(continueLab), attributes(s)); + generateCondition(exitLab, invert(s.condition), subscope); - context.add(new Bytecode.Loop(new int[] {}, subcontext.blockIndex()), attributes(s)); - context.add(new Bytecode.Label(exitLab), attributes(s)); + scope.add(new Bytecode.Loop(new int[] {}, subscope.blockIndex()), attributes(s)); + scope.add(new Bytecode.Label(exitLab), attributes(s)); } // ========================================================================= @@ -1278,27 +1136,24 @@ private void generate(Stmt.DoWhile s, GenerationContext context) { * @param condition * --- Source-level condition to be translated into a sequence of * one or more conditional branches. - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * @return */ - public void generateCondition(String target, Expr condition, GenerationContext context) { + public void generateCondition(String target, Expr condition, EnclosingScope scope) { try { // First, we see whether or not we can employ a special handler for // translating this condition. if (condition instanceof Expr.Constant) { - generateCondition(target, (Expr.Constant) condition, context); + generateCondition(target, (Expr.Constant) condition, scope); } else if (condition instanceof Expr.UnOp) { - generateCondition(target, (Expr.UnOp) condition, context); + generateCondition(target, (Expr.UnOp) condition, scope); } else if (condition instanceof Expr.BinOp) { - generateCondition(target, (Expr.BinOp) condition, context); + generateCondition(target, (Expr.BinOp) condition, scope); } else if (condition instanceof Expr.Quantifier) { - generateCondition(target, (Expr.Quantifier) condition, context); + generateCondition(target, (Expr.Quantifier) condition, scope); } else if (condition instanceof Expr.ConstantAccess || condition instanceof Expr.LocalVariable || condition instanceof Expr.AbstractInvoke || condition instanceof Expr.AbstractIndirectInvoke || condition instanceof Expr.FieldAccess || condition instanceof Expr.IndexOf) { @@ -1308,17 +1163,17 @@ public void generateCondition(String target, Expr condition, GenerationContext c // true. In some cases, we could actually do better. For // example, !(x < 5) could be rewritten into x >= 5. - int result = generate(condition, context); - context.add(new Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); + int result = generate(condition, scope); + scope.add(new Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); } else { - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context.enclosingDeclaration(), condition); + syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), scope.getSourceContext(), condition); } } catch (SyntaxError se) { throw se; } catch (Exception ex) { - internalFailure(ex.getMessage(), context.enclosingDeclaration(), condition, ex); + internalFailure(ex.getMessage(), scope.getSourceContext(), condition, ex); } } @@ -1344,17 +1199,14 @@ public void generateCondition(String target, Expr condition, GenerationContext c * @param condition * --- Source-level condition to be translated into a sequence of * one or more conditional branches. - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * @return */ - private void generateCondition(String target, Expr.Constant c, GenerationContext context) { + private void generateCondition(String target, Expr.Constant c, EnclosingScope scope) { Constant.Bool b = (Constant.Bool) c.value; if (b.value()) { - context.add(new Bytecode.Goto(target)); + scope.add(new Bytecode.Goto(target)); } else { // do nout } @@ -1373,55 +1225,52 @@ private void generateCondition(String target, Expr.Constant c, GenerationContext * @param condition * --- Source-level condition to be translated into a sequence of * one or more conditional branches. - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * @return */ - private void generateCondition(String target, Expr.BinOp v, GenerationContext context) throws Exception { + private void generateCondition(String target, Expr.BinOp v, EnclosingScope scope) throws Exception { Expr.BOp bop = v.op; if (bop == Expr.BOp.OR) { - generateCondition(target, v.lhs, context); - generateCondition(target, v.rhs, context); + generateCondition(target, v.lhs, scope); + generateCondition(target, v.rhs, scope); } else if (bop == Expr.BOp.AND) { String exitLabel = freshLabel(); - generateCondition(exitLabel, invert(v.lhs), context); - generateCondition(target, v.rhs, context); - context.add(new Bytecode.Label(exitLabel)); + generateCondition(exitLabel, invert(v.lhs), scope); + generateCondition(target, v.rhs, scope); + scope.add(new Bytecode.Label(exitLabel)); } else if (bop == Expr.BOp.IS) { - generateTypeCondition(target, v, context); + generateTypeCondition(target, v, scope); } else { if (bop == Expr.BOp.EQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.Null) { // this is a simple rewrite to enable type inference. Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; - if (context.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context.enclosingDeclaration(), v.lhs); + if (scope.get(lhs.var) == null) { + syntaxError(errorMessage(UNKNOWN_VARIABLE), scope.getSourceContext(), v.lhs); } - int slot = context.get(lhs.var); - context.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); + int slot = scope.get(lhs.var); + scope.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); } else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.Null) { // this is a simple rewrite to enable type inference. String exitLabel = freshLabel(); Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; - if (context.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context.enclosingDeclaration(), v.lhs); + if (scope.get(lhs.var) == null) { + syntaxError(errorMessage(UNKNOWN_VARIABLE), scope.getSourceContext(), v.lhs); } - int slot = context.get(lhs.var); - context.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); - context.add(new Bytecode.Goto(target)); - context.add(new Bytecode.Label(exitLabel)); + int slot = scope.get(lhs.var); + scope.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); + scope.add(new Bytecode.Goto(target)); + scope.add(new Bytecode.Label(exitLabel)); } else { - int result = generate(v, context); - context.add(new Bytecode.If(v.srcType.raw(), result, target), attributes(v)); + int result = generate(v, scope); + scope.add(new Bytecode.If(v.srcType.raw(), result, target), attributes(v)); } } } @@ -1443,14 +1292,11 @@ private void generateCondition(String target, Expr.BinOp v, GenerationContext co * @param condition * --- Source-level binary condition to be translated into a * sequence of one or more conditional branches. - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * @return */ - private void generateTypeCondition(String target, Expr.BinOp condition, GenerationContext context) throws Exception { + private void generateTypeCondition(String target, Expr.BinOp condition, EnclosingScope scope) throws Exception { int leftOperand; if (condition.lhs instanceof Expr.LocalVariable) { @@ -1460,22 +1306,22 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Generati // variable (since, otherwise, we'll retype the temporary but not // the intended variable). Expr.LocalVariable lhs = (Expr.LocalVariable) condition.lhs; - if (context.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), context.enclosingDeclaration(), condition.lhs); + if (scope.get(lhs.var) == null) { + syntaxError(errorMessage(UNKNOWN_VARIABLE), scope.getSourceContext(), condition.lhs); } - leftOperand = context.get(lhs.var); + leftOperand = scope.get(lhs.var); } else { // This is the general case whether the lhs is an arbitrary variable // and, hence, retyping does not apply. Therefore, we can simply // evaluate the lhs into a temporary register as per usual. - leftOperand = generate(condition.lhs, context); + leftOperand = generate(condition.lhs, scope); } // Note, the type checker guarantees that the rhs is a type val, so the // following cast is always safe. Expr.TypeVal rhs = (Expr.TypeVal) condition.rhs; - context.add(new Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); + scope.add(new Bytecode.IfIs(condition.srcType.raw(), leftOperand, rhs.type.nominal(), target), attributes(condition)); } /** @@ -1494,14 +1340,11 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Generati * @param condition * --- Source-level condition to be translated into a sequence of * one or more conditional branches. - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * @return */ - private void generateCondition(String target, Expr.UnOp v, GenerationContext context) { + private void generateCondition(String target, Expr.UnOp v, EnclosingScope scope) { Expr.UOp uop = v.op; switch (uop) { case NOT: @@ -1510,13 +1353,13 @@ private void generateCondition(String target, Expr.UnOp v, GenerationContext con // through case we branch to our true destination. String label = freshLabel(); - generateCondition(label, v.mhs, context); - context.add(new Bytecode.Goto(target)); - context.add(new Bytecode.Label(label)); + generateCondition(label, v.mhs, scope); + scope.add(new Bytecode.Goto(target)); + scope.add(new Bytecode.Label(label)); return; default: // Nothing else is a valud boolean condition here. - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), context.enclosingDeclaration(), v); + syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), scope.getSourceContext(), v); } } @@ -1534,60 +1377,59 @@ private void generateCondition(String target, Expr.UnOp v, GenerationContext con * @param condition * --- Source-level condition to be translated into a sequence of * one or more conditional branches. - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * @return */ - private void generateCondition(String target, Expr.Quantifier e, GenerationContext context) { + private void generateCondition(String target, Expr.Quantifier e, EnclosingScope scope) { String exit = freshLabel(); - generate(e.sources.iterator(), target, exit, e, context); + // Note, we must clone the scope below at this point. This is to avoid + // the variable name percolating into the enclosing scope. + generate(e.sources.iterator(), target, exit, e, scope.clone()); switch (e.cop) { case NONE: - context.add(new Bytecode.Goto(target)); - context.add(new Bytecode.Label(exit)); + scope.add(new Bytecode.Goto(target)); + scope.add(new Bytecode.Label(exit)); break; case SOME: break; case ALL: - context.add(new Bytecode.Goto(target)); - context.add(new Bytecode.Label(exit)); + scope.add(new Bytecode.Goto(target)); + scope.add(new Bytecode.Label(exit)); break; } } private void generate(Iterator> srcIterator, String trueLabel, String falseLabel, - Expr.Quantifier e, GenerationContext context) { + Expr.Quantifier e, EnclosingScope scope) { if (srcIterator.hasNext()) { // This is the inductive case (i.e. an outer loop) Triple src = srcIterator.next(); // First, determine the src slot. - int varSlot = context.allocate(Type.T_INT, src.first()); - int startSlot = generate(src.second(), context); - int endSlot = generate(src.third(), context); + int varSlot = scope.allocate(Nominal.T_INT, src.first()); + int startSlot = generate(src.second(), scope); + int endSlot = generate(src.third(), scope); // Second, recursively generate remaining parts - GenerationContext subcontext = context.createBlock(); - generate(srcIterator, trueLabel, falseLabel, e, subcontext); + EnclosingScope subscope = scope.createBlock(); + generate(srcIterator, trueLabel, falseLabel, e, subscope); // Finally, create the forall loop bytecode - context.add(new Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], subcontext.blockIndex()), attributes(e)); + scope.add(new Bytecode.Quantify(startSlot, endSlot, varSlot, new int[0], subscope.blockIndex()), attributes(e)); } else { // This is the base case (i.e. the innermost loop) switch (e.cop) { case NONE: - generateCondition(falseLabel, e.condition, context); + generateCondition(falseLabel, e.condition, scope); break; case SOME: - generateCondition(trueLabel, e.condition, context); + generateCondition(trueLabel, e.condition, scope); break; case ALL: - generateCondition(falseLabel, invert(e.condition), context); + generateCondition(falseLabel, invert(e.condition), scope); break; } } @@ -1597,45 +1439,45 @@ private void generate(Iterator> srcIterator, String t // Multi-Expressions // ========================================================================= - public int[] generate(Expr.Multi expression, GenerationContext context) { + public int[] generate(Expr.Multi expression, EnclosingScope scope) { List returns = expression.returns(); int[] targets = new int[returns.size()]; for (int i = 0; i != targets.length; ++i) { - targets[i] = context.allocate(returns.get(i).raw()); + targets[i] = scope.allocate(returns.get(i)); } try { if (expression instanceof Expr.FunctionOrMethodCall) { Expr.FunctionOrMethodCall fmc = (Expr.FunctionOrMethodCall) expression; - generateStmt(fmc, context, targets); + generateStmt(fmc, scope, targets); } else if (expression instanceof Expr.IndirectFunctionOrMethodCall) { Expr.IndirectFunctionOrMethodCall fmc = (Expr.IndirectFunctionOrMethodCall) expression; - generateStmt(fmc, context, targets); + generateStmt(fmc, scope, targets); } else { // should be dead-code - internalFailure("unknown expression: " + expression.getClass().getName(), context.enclosingDeclaration(), expression); + internalFailure("unknown expression: " + expression.getClass().getName(), scope.getSourceContext(), expression); } } catch (ResolveError rex) { - syntaxError(rex.getMessage(), context.enclosingDeclaration(), expression, rex); + syntaxError(rex.getMessage(), scope.getSourceContext(), expression, rex); } catch (SyntaxError se) { throw se; } catch (Exception ex) { - internalFailure(ex.getMessage(), context.enclosingDeclaration(), expression, ex); + internalFailure(ex.getMessage(), scope.getSourceContext(), expression, ex); } // done return targets; } - public void generateStmt(Expr.FunctionOrMethodCall expr, GenerationContext context, int... targets) throws ResolveError { + public void generateStmt(Expr.FunctionOrMethodCall expr, EnclosingScope scope, int... targets) throws ResolveError { // - int[] operands = generate(expr.arguments, context); - context.add(new Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); + int[] operands = generate(expr.arguments, scope); + scope.add(new Bytecode.Invoke(expr.type().nominal(), targets, operands, expr.nid()), attributes(expr)); } - public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, GenerationContext context, int... targets) throws ResolveError { + public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, EnclosingScope scope, int... targets) throws ResolveError { // - int operand = generate(expr.src, context); - int[] operands = generate(expr.arguments, context); - context.add(new Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); + int operand = generate(expr.src, scope); + int[] operands = generate(expr.arguments, scope); + scope.add(new Bytecode.IndirectInvoke(expr.type().raw(), targets, operand, operands), attributes(expr)); } // ========================================================================= @@ -1649,146 +1491,110 @@ public void generateStmt(Expr.IndirectFunctionOrMethodCall expr, GenerationConte * * @param expression * --- Source-level expression to be translated - * @param environment - * --- Mapping from variable names to to slot numbers. - * @param block - * --- List of bytecodes onto which translation should be - * appended. + * @param scope + * --- Enclosing scope of the condition * * @return --- the register */ - public int generate(Expr expression, GenerationContext context) { + public int generate(Expr expression, EnclosingScope scope) { try { if (expression instanceof Expr.Constant) { - return generate((Expr.Constant) expression, context); + return generate((Expr.Constant) expression, scope); } else if (expression instanceof Expr.LocalVariable) { - return generate((Expr.LocalVariable) expression, context); + return generate((Expr.LocalVariable) expression, scope); } else if (expression instanceof Expr.ConstantAccess) { - return generate((Expr.ConstantAccess) expression, context); + return generate((Expr.ConstantAccess) expression, scope); } else if (expression instanceof Expr.ArrayInitialiser) { - return generate((Expr.ArrayInitialiser) expression, context); + return generate((Expr.ArrayInitialiser) expression, scope); } else if (expression instanceof Expr.ArrayGenerator) { - return generate((Expr.ArrayGenerator) expression, context); + return generate((Expr.ArrayGenerator) expression, scope); } else if (expression instanceof Expr.BinOp) { - return generate((Expr.BinOp) expression, context); + return generate((Expr.BinOp) expression, scope); } else if (expression instanceof Expr.Dereference) { - return generate((Expr.Dereference) expression, context); + return generate((Expr.Dereference) expression, scope); } else if (expression instanceof Expr.Cast) { - return generate((Expr.Cast) expression, context); + return generate((Expr.Cast) expression, scope); } else if (expression instanceof Expr.IndexOf) { - return generate((Expr.IndexOf) expression, context); + return generate((Expr.IndexOf) expression, scope); } else if (expression instanceof Expr.UnOp) { - return generate((Expr.UnOp) expression, context); + return generate((Expr.UnOp) expression, scope); } else if (expression instanceof Expr.FunctionOrMethodCall) { - return generate((Expr.FunctionOrMethodCall) expression, context); + return generate((Expr.FunctionOrMethodCall) expression, scope); } else if (expression instanceof Expr.IndirectFunctionCall) { - return generate((Expr.IndirectFunctionCall) expression, context); + return generate((Expr.IndirectFunctionCall) expression, scope); } else if (expression instanceof Expr.IndirectMethodCall) { - return generate((Expr.IndirectMethodCall) expression, context); + return generate((Expr.IndirectMethodCall) expression, scope); } else if (expression instanceof Expr.Quantifier) { - return generate((Expr.Quantifier) expression, context); + return generate((Expr.Quantifier) expression, scope); } else if (expression instanceof Expr.FieldAccess) { - return generate((Expr.FieldAccess) expression, context); + return generate((Expr.FieldAccess) expression, scope); } else if (expression instanceof Expr.Record) { - return generate((Expr.Record) expression, context); + return generate((Expr.Record) expression, scope); } else if (expression instanceof Expr.FunctionOrMethod) { - return generate((Expr.FunctionOrMethod) expression, context); + return generate((Expr.FunctionOrMethod) expression, scope); } else if (expression instanceof Expr.Lambda) { - return generate((Expr.Lambda) expression, context); + return generate((Expr.Lambda) expression, scope); } else if (expression instanceof Expr.New) { - return generate((Expr.New) expression, context); + return generate((Expr.New) expression, scope); } else { // should be dead-code - internalFailure("unknown expression: " + expression.getClass().getName(), context.enclosingDeclaration(), expression); + internalFailure("unknown expression: " + expression.getClass().getName(), scope.getSourceContext(), expression); } } catch (ResolveError rex) { - syntaxError(rex.getMessage(), context.enclosingDeclaration(), expression, rex); + syntaxError(rex.getMessage(), scope.getSourceContext(), expression, rex); } catch (SyntaxError se) { throw se; } catch (Exception ex) { - internalFailure(ex.getMessage(), context.enclosingDeclaration(), expression, ex); + internalFailure(ex.getMessage(), scope.getSourceContext(), expression, ex); } return -1; // deadcode } - public int generate(Expr.FunctionOrMethodCall expr, GenerationContext context) throws ResolveError { - int target = context.allocate(expr.result().raw()); - generateStmt(expr, context, target); + public int generate(Expr.FunctionOrMethodCall expr, EnclosingScope scope) throws ResolveError { + int target = scope.allocate(expr.result()); + generateStmt(expr, scope, target); return target; } - public int generate(Expr.IndirectFunctionOrMethodCall expr, GenerationContext context) throws ResolveError { - int target = context.allocate(expr.result().raw()); - generateStmt(expr, context, target); + public int generate(Expr.IndirectFunctionOrMethodCall expr, EnclosingScope scope) throws ResolveError { + int target = scope.allocate(expr.result()); + generateStmt(expr, scope, target); return target; } - private int generate(Expr.Constant expr, GenerationContext context) { + private int generate(Expr.Constant expr, EnclosingScope scope) { Constant val = expr.value; - int target = context.allocate(val.type()); - context.add(new Bytecode.Const(target, expr.value), attributes(expr)); + int target = scope.allocate(Nominal.construct(val.type(),val.type())); + scope.add(new Bytecode.Const(target, expr.value), attributes(expr)); return target; } - private int generate(Expr.FunctionOrMethod expr, GenerationContext context) { - Type.FunctionOrMethod rawType = expr.type.raw(); + private int generate(Expr.FunctionOrMethod expr, EnclosingScope scope) { Type.FunctionOrMethod nominalType = expr.type.nominal(); - int target = context.allocate(rawType); - context.add(new Bytecode.Lambda(nominalType, target, new int[0], expr.nid), attributes(expr)); + int target = scope.allocate(expr.type); + scope.add(new Bytecode.Lambda(nominalType, target, new int[0], expr.nid), attributes(expr)); return target; } - private int generate(Expr.Lambda expr, GenerationContext context) { - Type.FunctionOrMethod tfm = expr.type.raw(); - List tfm_params = tfm.params(); - List expr_params = expr.parameters; + private int generate(Expr.Lambda expr, EnclosingScope scope) { + Nominal.FunctionOrMethod lambdaType = expr.type; + Type.FunctionOrMethod rawLambdaType = lambdaType.raw(); - // Create environment for the lambda body. - ArrayList operands = new ArrayList(); - ArrayList paramTypes = new ArrayList(); - GenerationContext lambdaContext = new GenerationContext(context.enclosingDeclaration()); - List declarations = bodyForest.registers(); - - for (int i = 0; i != tfm_params.size(); ++i) { - Type type = tfm_params.get(i); - String name = expr_params.get(i).name; - lambdaContext.allocate(type, name); - paramTypes.add(type); - declarations.add(new BytecodeForest.Register(type, name)); - } - for (Pair v : Exprs.uses(expr.body, context.enclosingDeclaration())) { - if (lambdaContext.get(v.second()) == null) { - Type type = v.first(); - lambdaContext.allocate(type, v.second()); - paramTypes.add(type); - operands.add(context.get(v.second())); - declarations.add(new BytecodeForest.Register(type, v.second())); - } - } + // Variables contains the list of variables from the enclosing scope + // which are used in the lambda body + ArrayList variables = new ArrayList(); + // Create a new root scope for the lambda body + EnclosingScope lambdaScope = new EnclosingScope(scope.getSourceContext()).createRootBlock(); + Type.FunctionOrMethod concreteLambdaType = determineLambdaParametersAndOperands(expr,variables,lambdaScope); // Generate body based on current environment - - if (tfm.returns().isEmpty()) { - lambdaContext.add(new Bytecode.Return(), attributes(expr)); - } else { - int target = generate(expr.body, lambdaContext); - lambdaContext.add(new Bytecode.Return(tfm.returns().toArray(new Type[tfm.returns().size()]), target), - attributes(expr)); - } - - // Add type information for all temporary registers allocated - // during code generation. This complements the existing information - // about declared variables. - for (int i = declarations.size(); i != benv.size(); i = i + 1) { - Type t = benv.type(i); - declarations.add(new BytecodeForest.Register(t, null)); - } - // Create concrete type for private lambda function - Type.FunctionOrMethod cfm; - if (tfm instanceof Type.Function) { - cfm = Type.Function(tfm.returns(), paramTypes); + if (lambdaType.returns().isEmpty()) { + lambdaScope.add(new Bytecode.Return(), attributes(expr)); } else { - cfm = Type.Method(tfm.returns(), paramTypes); + int target = generate(expr.body, lambdaScope); + lambdaScope + .add(new Bytecode.Return(rawLambdaType.returns().toArray(new Type[rawLambdaType.returns().size()]), + target), attributes(expr)); } // Construct private lambda function using generated body @@ -1796,180 +1602,236 @@ private int generate(Expr.Lambda expr, GenerationContext context) { String name = "$lambda" + id; ArrayList modifiers = new ArrayList(); modifiers.add(Modifier.PRIVATE); - WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod(modifiers, name, cfm, lambdaContext.forest(), 0, 0, - attributes(expr)); + WyilFile.FunctionOrMethod lambda = new WyilFile.FunctionOrMethod(modifiers, name, concreteLambdaType, + lambdaScope.getForest(), 0, 0, attributes(expr)); lambdas.add(lambda); - Path.ID mid = context.file().module; + Path.ID mid = scope.getSourceContext().file().module; NameID nid = new NameID(mid, name); - + // Initialise the operands array + int[] operands = new int[variables.size()]; + for(int i=0;i!=operands.length;++i) { + operands[i] = scope.get(variables.get(i)); + } // Finally, create the lambda - int target = context.allocate(tfm); - context.add(new Bytecode.Lambda(cfm, target, toIntArray(operands), nid), attributes(expr)); + int target = scope.allocate(lambdaType); + scope.add(new Bytecode.Lambda(concreteLambdaType, target, operands, nid), attributes(expr)); return target; } - private int generate(Expr.ConstantAccess expr, GenerationContext context) throws ResolveError { + /** + * Determine the set of parameters for the lambda function itself. This + * includes the declared parameters in the lambda expression, but also any + * variables from the enclosing scope which are used within the lambda. For + * example: + * + *

+	 * type FunT is function(int)->int
+	 * 
+	 * method f(int x) -> FunT:
+	 *   return &(int y -> x + y)
+	 * 
+ * + * Here, the concrete lambda type is function(int,int)->int, where the first + * parameter is y and the second is x. + * + * @param expr + * @param operands + * @param scope + * @return + */ + private Type.FunctionOrMethod determineLambdaParametersAndOperands(Expr.Lambda expr, List operands, EnclosingScope scope) { + Nominal.FunctionOrMethod lambdaType = expr.type; + Type.FunctionOrMethod rawLambdaType = lambdaType.raw(); + List lambdaParameters = expr.parameters; + ArrayList paramTypes = new ArrayList(lambdaType.params()); + // First, add declared parameters + HashSet declaredVariables = new HashSet(); + for (int i = 0; i != lambdaParameters.size(); ++i) { + WhileyFile.Parameter parameter = lambdaParameters.get(i); + // allocate parameter to register in the current block + scope.allocate(paramTypes.get(i), parameter.name); + declaredVariables.add(parameter.name); + } + // Second add used variables (which are then parameters) + for (Pair v : Exprs.uses(expr.body, scope.getSourceContext())) { + if (!declaredVariables.contains(v.second())) { + scope.allocate(v.first(), v.second()); + paramTypes.add(v.first()); + operands.add(v.second()); + declaredVariables.add(v.second()); + } + } + // Convert all nominal parameters to raw parameters (ugly) + ArrayList rawParamTypes = new ArrayList(); + for(Nominal t : paramTypes) { rawParamTypes.add(t.raw()); } + // Finally, create the concrete lambda type + if(lambdaType instanceof Nominal.Function) { + return Type.Function(rawLambdaType.returns(),rawParamTypes); + } else { + return Type.Method(rawLambdaType.returns(),rawParamTypes); + } + } + + private int generate(Expr.ConstantAccess expr, EnclosingScope scope) throws ResolveError { Constant val = expr.value; - int target = context.allocate(val.type()); - context.add(new Bytecode.Const(target, val), attributes(expr)); + int target = scope.allocate(Nominal.construct(val.type(),val.type())); + scope.add(new Bytecode.Const(target, val), attributes(expr)); return target; } - private int generate(Expr.LocalVariable expr, GenerationContext context) throws ResolveError { - - if (context.get(expr.var) != null) { - int target = context.get(expr.var); - Type type = expr.result().raw(); + private int generate(Expr.LocalVariable expr, EnclosingScope scope) throws ResolveError { + if (scope.get(expr.var) != null) { + int target = scope.get(expr.var); return target; } else { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), context.enclosingDeclaration(), expr); + syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), scope.getSourceContext(), expr); return -1; } } - private int generate(Expr.UnOp expr, GenerationContext context) { - int[] operands = new int[] { generate(expr.mhs, context) }; - int[] targets = new int[] { context.allocate(expr.result().raw()) }; + private int generate(Expr.UnOp expr, EnclosingScope scope) { + int[] operands = new int[] { generate(expr.mhs, scope) }; + int[] targets = new int[] { scope.allocate(expr.result()) }; switch (expr.op) { case NEG: - context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), + scope.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NEG), attributes(expr)); break; case INVERT: - context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), + scope.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.BITWISEINVERT), attributes(expr)); break; case NOT: - context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NOT), + scope.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.NOT), attributes(expr)); break; case ARRAYLENGTH: - context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); + scope.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYLENGTH), attributes(expr)); break; default: // should be dead-code - internalFailure("unexpected unary operator encountered", context.enclosingDeclaration(), expr); + internalFailure("unexpected unary operator encountered", scope.getSourceContext(), expr); return -1; } return targets[0]; } - private int generate(Expr.Dereference expr, GenerationContext context) { - int[] operands = new int[] { generate(expr.src, context) }; - int[] targets = new int[] { context.allocate(expr.result().raw()) }; - context.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), + private int generate(Expr.Dereference expr, EnclosingScope scope) { + int[] operands = new int[] { generate(expr.src, scope) }; + int[] targets = new int[] { scope.allocate(expr.result()) }; + scope.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.DEREFERENCE), attributes(expr)); return targets[0]; } - private int generate(Expr.IndexOf expr, GenerationContext context) { - int[] operands = { generate(expr.src, context), - generate(expr.index, context) }; - int[] targets = new int[] { context.allocate(expr.result().raw()) }; - context.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); + private int generate(Expr.IndexOf expr, EnclosingScope scope) { + int[] operands = { generate(expr.src, scope), + generate(expr.index, scope) }; + int[] targets = new int[] { scope.allocate(expr.result()) }; + scope.add(new Bytecode.Operator(expr.srcType.raw(), targets, operands, Bytecode.OperatorKind.ARRAYINDEX), attributes(expr)); return targets[0]; } - private int generate(Expr.Cast expr, GenerationContext context) { - int operand = generate(expr.expr, context); - Type from = expr.expr.result().raw(); - Type to = expr.result().raw(); - int target = context.allocate(to); - context.add(new Bytecode.Convert(from, target, operand, to), attributes(expr)); + private int generate(Expr.Cast expr, EnclosingScope scope) { + int operand = generate(expr.expr, scope); + Nominal from = expr.expr.result(); + Nominal to = expr.result(); + int target = scope.allocate(to); + scope.add(new Bytecode.Convert(from.raw(), target, operand, to.raw()), attributes(expr)); return target; } - private int generate(Expr.BinOp v, GenerationContext context) throws Exception { + private int generate(Expr.BinOp v, EnclosingScope scope) throws Exception { // could probably use a range test for this somehow if(v.op == Expr.BOp.AND || v.op == Expr.BOp.OR) { String trueLabel = freshLabel(); String exitLabel = freshLabel(); - generateCondition(trueLabel, v, context); - int target = context.allocate(Type.T_BOOL); - context.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(v)); - context.add(new Bytecode.Goto(exitLabel)); - context.add(new Bytecode.Label(trueLabel)); - context.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(v)); - context.add(new Bytecode.Label(exitLabel)); + generateCondition(trueLabel, v, scope); + int target = scope.allocate(Nominal.T_BOOL); + scope.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(v)); + scope.add(new Bytecode.Goto(exitLabel)); + scope.add(new Bytecode.Label(trueLabel)); + scope.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(v)); + scope.add(new Bytecode.Label(exitLabel)); return target; } else { - Type result = v.result().raw(); - int[] targets = new int[] { context.allocate(result) }; + Nominal result = v.result(); + int[] targets = new int[] { scope.allocate(result) }; int[] operands = { - generate(v.lhs, context), - generate(v.rhs, context) + generate(v.lhs, scope), + generate(v.rhs, scope) }; - context.add(new Bytecode.Operator(result, targets, operands, OP2BOP(v.op, v, context.enclosingDeclaration())), attributes(v)); + scope.add(new Bytecode.Operator(result.raw(), targets, operands, OP2BOP(v.op, v, scope.getSourceContext())), attributes(v)); return targets[0]; } } - private int generate(Expr.ArrayInitialiser expr, GenerationContext context) { - int[] operands = generate(expr.arguments, context); - int[] targets = new int[] { context.allocate(expr.result().raw()) }; - context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), + private int generate(Expr.ArrayInitialiser expr, EnclosingScope scope) { + int[] operands = generate(expr.arguments, scope); + int[] targets = new int[] { scope.allocate(expr.result()) }; + scope.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYCONSTRUCTOR), attributes(expr)); return targets[0]; } - private int generate(Expr.ArrayGenerator expr, GenerationContext context) { - int[] operands = new int[] { generate(expr.element, context), - generate(expr.count, context) }; - int[] targets = new int[] { context.allocate(expr.result().raw()) }; - context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); + private int generate(Expr.ArrayGenerator expr, EnclosingScope scope) { + int[] operands = new int[] { generate(expr.element, scope), + generate(expr.count, scope) }; + int[] targets = new int[] { scope.allocate(expr.result()) }; + scope.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.ARRAYGENERATOR), attributes(expr)); return targets[0]; } - private int generate(Expr.Quantifier e, GenerationContext context) { + private int generate(Expr.Quantifier e, EnclosingScope scope) { String trueLabel = freshLabel(); String exitLabel = freshLabel(); - generateCondition(trueLabel, e, context); - int target = context.allocate(Type.T_BOOL); - context.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(e)); - context.add(new Bytecode.Goto(exitLabel)); - context.add(new Bytecode.Label(trueLabel)); - context.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(e)); - context.add(new Bytecode.Label(exitLabel)); + generateCondition(trueLabel, e, scope); + int target = scope.allocate(Nominal.T_BOOL); + scope.add(new Bytecode.Const(target, Constant.Bool(false)), attributes(e)); + scope.add(new Bytecode.Goto(exitLabel)); + scope.add(new Bytecode.Label(trueLabel)); + scope.add(new Bytecode.Const(target, Constant.Bool(true)), attributes(e)); + scope.add(new Bytecode.Label(exitLabel)); return target; } - private int generate(Expr.Record expr, GenerationContext context) { + private int generate(Expr.Record expr, EnclosingScope scope) { ArrayList keys = new ArrayList(expr.fields.keySet()); Collections.sort(keys); int[] operands = new int[expr.fields.size()]; for (int i = 0; i != operands.length; ++i) { String key = keys.get(i); Expr arg = expr.fields.get(key); - operands[i] = generate(arg, context); + operands[i] = generate(arg, scope); } - int[] targets = new int[] { context.allocate(expr.result().raw()) }; - context.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), + int[] targets = new int[] { scope.allocate(expr.result()) }; + scope.add(new Bytecode.Operator(expr.result().raw(), targets, operands, Bytecode.OperatorKind.RECORDCONSTRUCTOR), attributes(expr)); return targets[0]; } - private int generate(Expr.FieldAccess expr, GenerationContext context) { - int operand = generate(expr.src, context); - int target = context.allocate(expr.result().raw()); - context.add(new Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), + private int generate(Expr.FieldAccess expr, EnclosingScope scope) { + int operand = generate(expr.src, scope); + int target = scope.allocate(expr.result()); + scope.add(new Bytecode.FieldLoad((Type.EffectiveRecord) expr.srcType.raw(), target, operand, expr.name), attributes(expr)); return target; } - private int generate(Expr.New expr, GenerationContext context) throws ResolveError { - int[] operands = new int[] { generate(expr.expr, context) }; - int[] targets = new int[] { context.allocate(expr.result().raw()) }; - context.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); + private int generate(Expr.New expr, EnclosingScope scope) throws ResolveError { + int[] operands = new int[] { generate(expr.expr, scope) }; + int[] targets = new int[] { scope.allocate(expr.result()) }; + scope.add(new Bytecode.Operator(expr.type.raw(), targets, operands, Bytecode.OperatorKind.NEW)); return targets[0]; } - private int[] generate(List arguments, GenerationContext context) { + private int[] generate(List arguments, EnclosingScope scope) { int[] operands = new int[arguments.size()]; for (int i = 0; i != operands.length; ++i) { Expr arg = arguments.get(i); - operands[i] = generate(arg, context); + operands[i] = generate(arg, scope); } return operands; } @@ -1978,7 +1840,7 @@ private int[] generate(List arguments, GenerationContext context) { // Helpers // ========================================================================= - private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context context) { + private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Context scope) { switch (bop) { case ADD: return Bytecode.OperatorKind.ADD; @@ -2013,7 +1875,7 @@ private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Contex case RIGHTSHIFT: return Bytecode.OperatorKind.RIGHTSHIFT; default: - syntaxError(errorMessage(INVALID_BINARY_EXPRESSION), context, elem); + syntaxError(errorMessage(INVALID_BINARY_EXPRESSION), scope, elem); } // dead-code return null; @@ -2067,76 +1929,6 @@ private static Expr invert(Expr e) { return r; } - /** - * This resets the environment so that all variable names refer to those - * variables indicated in the given list of declared registers. This flushes - * out any unwanted assignments of variable names to temporary registers - * which can occur when we have multiple variables with the same name. - * - * @param environment - * @param declarations - * @return - */ - public Environment resetEnvironment(Environment environment, List declarations) { - Environment nenv = new Environment(); - for(int i=0;i!=declarations.size();++i) { - BytecodeForest.Register reg = declarations.get(i); - nenv.allocate(reg.type(),reg.name()); - } - for(int i=declarations.size();i block, List declarations, - Environment environment, WhileyFile.Context context) { - // - for (int i = 0; i != block.size(); ++i) { - buildVariableDeclarations(block.get(i), declarations, environment, context); - } - } - - public void buildVariableDeclarations(Stmt stmt, List declarations, Environment environment, - WhileyFile.Context context) { - if (stmt instanceof Assign || stmt instanceof Assert || stmt instanceof Assume || stmt instanceof Return - || stmt instanceof Debug || stmt instanceof Fail || stmt instanceof Break || stmt instanceof Continue - || stmt instanceof Expr.MethodCall || stmt instanceof Expr.IndirectMethodCall - || stmt instanceof Expr.FunctionCall || stmt instanceof Expr.IndirectFunctionCall - || stmt instanceof Expr.New || stmt instanceof Skip) { - // Don't need to do anything in these cases. - return; - } else if (stmt instanceof VariableDeclaration) { - VariableDeclaration d = (VariableDeclaration) stmt; - declarations.add(new BytecodeForest.Register(d.type.nominal(), d.parameter.name)); - environment.allocate(d.type.raw(), d.parameter.name); - } else if (stmt instanceof IfElse) { - IfElse s = (IfElse) stmt; - buildVariableDeclarations(s.trueBranch, declarations, environment, context); - buildVariableDeclarations(s.falseBranch, declarations, environment, context); - } else if (stmt instanceof Switch) { - Switch s = (Switch) stmt; - for (Stmt.Case c : s.cases) { - buildVariableDeclarations(c.stmts, declarations, environment, context); - } - } else if (stmt instanceof While) { - While s = (While) stmt; - buildVariableDeclarations(s.body, declarations, environment, context); - } else if (stmt instanceof DoWhile) { - DoWhile s = (DoWhile) stmt; - buildVariableDeclarations(s.body, declarations, environment, context); - } else { - // should be dead-code - WhileyFile.internalFailure("unknown statement: " + stmt.getClass().getName(), context, stmt); - } - } - /** * The attributes method extracts those attributes of relevance to WyIL, and * discards those which are only used for the wyc front end. @@ -2175,17 +1967,26 @@ public static String freshLabel() { return "blklab" + _idx++; } - private static final class GenerationContext { + /** + * Captures all useful information about the scope in which a statement or + * expression is being translated. For example, it determines which WyIL + * register all visible variables and parameters map to. Furthermore, it + * determines where break and continue statements will jump to. + * + * @author David J. Pearce + * + */ + private static final class EnclosingScope { /** * Maps variables to their WyIL register number and type. */ - private final Environment environment; + private final HashMap environment; /** * The outermost forest (needed for creating new subblocks). */ private final BytecodeForest forest; /** - * The enclosing source file context (needed for error reporting) + * The enclosing source file scope (needed for error reporting) */ private final WhileyFile.Context context; /** @@ -2195,32 +1996,31 @@ private static final class GenerationContext { /** * Get the index of the bytecode block into which bytecodes are being written */ - private final int blockIndex; - + private final int blockIndex; /** * Get the target for any continue statement encountered */ - private final String continueLabel; - + private final String continueLabel; /** * Get the target for any break statement encountered */ private final String breakLabel; - public GenerationContext(WhileyFile.Context context) { - this(new Environment(), new BytecodeForest(), context, -1); + public EnclosingScope(WhileyFile.Context context) { + this(new HashMap(), new BytecodeForest(), context, -1); } - private GenerationContext(Environment environment, BytecodeForest forest, WhileyFile.Context context, int blockIndex) { - this(environment,forest,context,blockIndex,null,null); + private EnclosingScope(Map environment, BytecodeForest forest, WhileyFile.Context context, + int blockIndex) { + this(environment, forest, context, blockIndex, null, null); } - private GenerationContext(Environment environment, BytecodeForest forest, WhileyFile.Context context, + private EnclosingScope(Map environment, BytecodeForest forest, WhileyFile.Context context, int blockIndex, String breakLabel, String continueLabel) { - this.environment = environment; + this.environment = new HashMap(environment); this.forest = forest; this.context = context; - this.blockIndex = blockIndex; + this.blockIndex = blockIndex; this.block = blockIndex == -1 ? null : forest.get(blockIndex); this.breakLabel = breakLabel; this.continueLabel = continueLabel; @@ -2230,11 +2030,15 @@ public int blockIndex() { return blockIndex; } - public BytecodeForest forest() { + public BytecodeForest getForest() { return forest; } - public WhileyFile.Context enclosingDeclaration() { + public BytecodeForest.Block getBlock() { + return block; + } + + public WhileyFile.Context getSourceContext() { return context; } @@ -2255,12 +2059,19 @@ public Integer get(String name) { return environment.get(name); } - public int allocate(Type t) { - return environment.allocate(t); + public int allocate(Nominal type) { + List registers = forest.registers(); + int index = registers.size(); + registers.add(new BytecodeForest.Register(type.nominal(), null)); + return index; } - public int allocate(Type t, String name) { - return environment.allocate(t,name); + public int allocate(Nominal type, String name) { + List registers = forest.registers(); + int index = registers.size(); + registers.add(new BytecodeForest.Register(type.nominal(), name)); + environment.put(name, index); + return index; } public void add(Bytecode b) { @@ -2270,96 +2081,31 @@ public void add(Bytecode b) { public void add(Bytecode b, List attributes) { block.add(b,attributes); } - - public GenerationContext createRootBlock() { + + public void add(int index, Bytecode b, List attributes) { + block.add(index, b,attributes); + } + + public EnclosingScope createRootBlock() { BytecodeForest.Block block = new BytecodeForest.Block(); int index = forest.addAsRoot(block); - return new GenerationContext(environment,forest,context,index); + return new EnclosingScope(environment,forest,context,index); } - public GenerationContext createBlock() { + public EnclosingScope createBlock() { BytecodeForest.Block block = new BytecodeForest.Block(); int index = forest.add(block); - return new GenerationContext(environment,forest,context,index); + return new EnclosingScope(environment,forest,context,index); } - public GenerationContext createBlock(String breakLabel, String continueLabel) { + public EnclosingScope createBlock(String breakLabel, String continueLabel) { BytecodeForest.Block block = new BytecodeForest.Block(); int index = forest.add(block); - return new GenerationContext(environment,forest,context,index,breakLabel,continueLabel); + return new EnclosingScope(environment,forest,context,index,breakLabel,continueLabel); } - } - - /** - * Maintains a mapping from Variable names to their allocated register slot, - * and their declared types. - * - * @author David J. Pearce - * - */ - public static final class Environment { - private final HashMap var2idx; - private final ArrayList idx2type; - - public Environment() { - var2idx = new HashMap(); - idx2type = new ArrayList(); - } - - public Environment(Environment env) { - var2idx = new HashMap(env.var2idx); - idx2type = new ArrayList(env.idx2type); - } - - public int allocate(Type t) { - int idx = idx2type.size(); - idx2type.add(t); - return idx; - } - - public int allocate(Type t, String v) { - int r = allocate(t); - var2idx.put(v, r); - return r; - } - - public int size() { - return idx2type.size(); - } - - public Integer get(String v) { - return var2idx.get(v); - } - - public String get(int idx) { - for (Map.Entry e : var2idx.entrySet()) { - int jdx = e.getValue(); - if (jdx == idx) { - return e.getKey(); - } - } - return null; - } - - public Type type(int idx) { - return idx2type.get(idx); - } - - public void put(int idx, String v) { - var2idx.put(v, idx); - } - - public ArrayList asRegisters() { - ArrayList registers = new ArrayList(); - for (int i = 0; i != idx2type.size(); ++i) { - Type t = idx2type.get(i); - registers.add(new BytecodeForest.Register(t, get(i))); - } - return registers; - } - - public String toString() { - return idx2type.toString() + "," + var2idx.toString(); + + public EnclosingScope clone() { + return new EnclosingScope(environment,forest,context,blockIndex,breakLabel,continueLabel); } } } diff --git a/modules/wyc/src/wyc/lang/Exprs.java b/modules/wyc/src/wyc/lang/Exprs.java index f4bf00b24f..73efd43025 100644 --- a/modules/wyc/src/wyc/lang/Exprs.java +++ b/modules/wyc/src/wyc/lang/Exprs.java @@ -18,19 +18,19 @@ public class Exprs { * @param context * @return */ - public static HashSet> uses(Expr expr, Context context) { - HashSet> r = new HashSet>(); + public static HashSet> uses(Expr expr, Context context) { + HashSet> r = new HashSet>(); uses(expr,context,r); return r; } - private static void uses(Expr expr, Context context, HashSet> uses) { + private static void uses(Expr expr, Context context, HashSet> uses) { try { if (expr instanceof Expr.Constant) { // do nout } else if (expr instanceof Expr.LocalVariable) { Expr.LocalVariable lv = (Expr.LocalVariable) expr; - uses.add(new Pair(lv.type.raw(),lv.var)); + uses.add(new Pair(lv.type,lv.var)); } else if (expr instanceof Expr.ConstantAccess) { // do nout diff --git a/modules/wyc/src/wyc/testing/AllValidVerificationTests.java b/modules/wyc/src/wyc/testing/AllValidVerificationTests.java index 37035d75a9..327be731d7 100644 --- a/modules/wyc/src/wyc/testing/AllValidVerificationTests.java +++ b/modules/wyc/src/wyc/testing/AllValidVerificationTests.java @@ -29,11 +29,8 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.junit.*; From 7f2e2a9265e453165b33bb3e6b0ae58b84aad01f Mon Sep 17 00:00:00 2001 From: DavePearce Date: Wed, 20 Apr 2016 19:38:04 +1200 Subject: [PATCH 31/43] WyC: Removed syntax checks from CodeGenerator Where possible I have removed syntax checks from CodeGenerator, as this is not the place where we should be doing this. In some cases, things have just disappeared. In other cases, I've left some internal failures instead. Unfortunately, the issue of checking duplicate case labels (E509) needs further thought. I've opened a separate issue for this #628 --- .../wyc/src/wyc/builder/CodeGenerator.java | 120 ++++++++++-------- modules/wyc/src/wyc/io/WhileyFileParser.java | 26 +++- tests/invalid/DefiniteAssign_Invalid_4.sysout | 6 +- tests/invalid/DefiniteAssign_Invalid_4.whiley | 2 +- tests/invalid/Parsing_Invalid_16.sysout | 2 +- 5 files changed, 94 insertions(+), 62 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 2bc5f794ef..738e2efd2b 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -28,7 +28,6 @@ import java.util.*; import static wyc.lang.WhileyFile.internalFailure; -import static wyc.lang.WhileyFile.syntaxError; import static wyil.util.ErrorMessages.*; import wyc.lang.*; import wyc.lang.Stmt.*; @@ -330,11 +329,11 @@ private void generate(Stmt stmt, EnclosingScope scope) { WhileyFile.internalFailure("unknown statement: " + stmt.getClass().getName(), scope.getSourceContext(), stmt); } } catch (ResolveError rex) { - WhileyFile.syntaxError(rex.getMessage(), scope.getSourceContext(), stmt, rex); + internalFailure(rex.getMessage(), scope.getSourceContext(), stmt, rex); } catch (SyntaxError sex) { throw sex; } catch (Exception ex) { - WhileyFile.internalFailure(ex.getMessage(), scope.getSourceContext(), stmt, ex); + internalFailure(ex.getMessage(), scope.getSourceContext(), stmt, ex); } } @@ -474,7 +473,7 @@ public void generateAssignment(Expr.LVal lval, int operand, Type type, Enclosing scope.add(new Bytecode.Update(lhs.type.raw(), target, toIntArray(operands), operand, lhs.afterType.raw(), fields), attributes(lval)); } else { - WhileyFile.syntaxError("invalid assignment", scope.getSourceContext(), lval); + internalFailure("invalid assignment", scope.getSourceContext(), lval); } } @@ -515,15 +514,12 @@ private Expr.AssignedVariable extractLVal(Expr e, ArrayList fields, Arra Expr.AssignedVariable l = extractLVal(la.src, fields, operands, scope); operands.add(operand); return l; - } else if (e instanceof Expr.FieldAccess) { + } else { Expr.FieldAccess ra = (Expr.FieldAccess) e; Expr.AssignedVariable r = extractLVal(ra.src, fields, operands, scope); fields.add(ra.name); return r; - } else { - WhileyFile.syntaxError(errorMessage(INVALID_LVAL_EXPRESSION), scope.getSourceContext(), e); - return null; // dead code - } + } } /** @@ -901,29 +897,29 @@ private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { String exitLab = freshLabel(); int operand = generate(s.expr, scope); String defaultTarget = exitLab; - HashSet values = new HashSet<>(); ArrayList> cases = new ArrayList<>(); int start = scope.getBlock().size(); + // FIXME: the following check should really occur earlier in the + // pipeline. However, it is difficult to do it earlier because it's only + // after FlowTypeChecker that we have determined the concrete values. + // See #6 + checkNoDuplicateLabels(s.cases,scope); + for (Stmt.Case c : s.cases) { if (c.expr.isEmpty()) { // A case with an empty match represents the default label. We // must check that we have not already seen a case with an empty // match (otherwise, we'd have two default labels ;) - if (defaultTarget != exitLab) { - WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), scope.getSourceContext(), c); - } else { - defaultTarget = freshLabel(); - scope.add(new Bytecode.Label(defaultTarget), attributes(c)); - // We need to clone the scope here to isolate variables - // declared in the default block from the enclosing scope - EnclosingScope defaultScope = scope.clone(); - for (Stmt st : c.stmts) { - generate(st, defaultScope); - } - scope.add(new Bytecode.Goto(exitLab), attributes(c)); + defaultTarget = freshLabel(); + scope.add(new Bytecode.Label(defaultTarget), attributes(c)); + // We need to clone the scope here to isolate variables + // declared in the default block from the enclosing scope + EnclosingScope defaultScope = scope.clone(); + for (Stmt st : c.stmts) { + generate(st, defaultScope); } - + scope.add(new Bytecode.Goto(exitLab), attributes(c)); } else if (defaultTarget == exitLab) { String target = freshLabel(); scope.add(new Bytecode.Label(target), attributes(c)); @@ -933,15 +929,8 @@ private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { // construct a mapping from that to a label indicating the start // of the case body. - for (Constant constant : c.constants) { - // Check whether this case constant has already been used as - // a case constant elsewhere. If so, then report an error. - if (values.contains(constant)) { - // FIXME: this should be located elsewhere - WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), scope.getSourceContext(), c); - } + for (Constant constant : c.constants) { cases.add(new Pair<>(constant, target)); - values.add(constant); } // We need to clone the scope here to isolate variables // declared in the case block from the enclosing scope @@ -955,7 +944,7 @@ private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { // This represents the case where we have another non-default // case after the default case. Such code cannot be executed, // and is therefore reported as an error. - WhileyFile.syntaxError(errorMessage(UNREACHABLE_CODE), scope.getSourceContext(), c); + internalFailure(errorMessage(UNREACHABLE_CODE), scope.getSourceContext(), c); } } @@ -963,6 +952,42 @@ private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { scope.add(new Bytecode.Label(exitLab), attributes(s)); } + /** + * Check that not two case statements have the same constant label + * + * @param cases + * @param indent + */ + private void checkNoDuplicateLabels(List cases, EnclosingScope scope) { + // The set of seen case labels captures those which have been seen + // already in some previous case block. Thus, if we see one again + // then we have a syntax error. + HashSet labels = new HashSet(); + // + for(int i=0;i!=cases.size();++i) { + Stmt.Case caseBlock = cases.get(i); + List caseLabels = caseBlock.constants; + if(caseLabels == null) { + // Default case + if (labels.contains(null)) { + WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), scope.getSourceContext(), caseBlock); + } else { + labels.add(null); + } + } else { + for (int j = 0; j != caseLabels.size(); ++j) { + Constant c = caseLabels.get(j); + if (labels.contains(c)) { + WhileyFile.syntaxError(errorMessage(DUPLICATE_CASE_LABEL), scope.getSourceContext(), caseBlock); + } else { + labels.add(c); + } + } + } + } + } + + /** * Translate a while loop into WyIL bytecodes. Consider the following use of * a while statement: @@ -1167,7 +1192,7 @@ public void generateCondition(String target, Expr condition, EnclosingScope scop scope.add(new Bytecode.If(Type.T_BOOL, result, target), attributes(condition)); } else { - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), scope.getSourceContext(), condition); + internalFailure(errorMessage(INVALID_BOOLEAN_EXPRESSION), scope.getSourceContext(), condition); } } catch (SyntaxError se) { @@ -1251,19 +1276,13 @@ private void generateCondition(String target, Expr.BinOp v, EnclosingScope scope && ((Expr.Constant) v.rhs).value == Constant.Null) { // this is a simple rewrite to enable type inference. Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; - if (scope.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), scope.getSourceContext(), v.lhs); - } int slot = scope.get(lhs.var); scope.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, target), attributes(v)); } else if (bop == Expr.BOp.NEQ && v.lhs instanceof Expr.LocalVariable && v.rhs instanceof Expr.Constant && ((Expr.Constant) v.rhs).value == Constant.Null) { // this is a simple rewrite to enable type inference. String exitLabel = freshLabel(); - Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; - if (scope.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), scope.getSourceContext(), v.lhs); - } + Expr.LocalVariable lhs = (Expr.LocalVariable) v.lhs; int slot = scope.get(lhs.var); scope.add(new Bytecode.IfIs(v.srcType.raw(), slot, Type.T_NULL, exitLabel), attributes(v)); scope.add(new Bytecode.Goto(target)); @@ -1305,10 +1324,7 @@ private void generateTypeCondition(String target, Expr.BinOp condition, Enclosin // on the original variable directly, rather than a temporary // variable (since, otherwise, we'll retype the temporary but not // the intended variable). - Expr.LocalVariable lhs = (Expr.LocalVariable) condition.lhs; - if (scope.get(lhs.var) == null) { - syntaxError(errorMessage(UNKNOWN_VARIABLE), scope.getSourceContext(), condition.lhs); - } + Expr.LocalVariable lhs = (Expr.LocalVariable) condition.lhs; leftOperand = scope.get(lhs.var); } else { // This is the general case whether the lhs is an arbitrary variable @@ -1359,7 +1375,7 @@ private void generateCondition(String target, Expr.UnOp v, EnclosingScope scope) return; default: // Nothing else is a valud boolean condition here. - syntaxError(errorMessage(INVALID_BOOLEAN_EXPRESSION), scope.getSourceContext(), v); + internalFailure(errorMessage(INVALID_BOOLEAN_EXPRESSION), scope.getSourceContext(), v); } } @@ -1457,7 +1473,7 @@ public int[] generate(Expr.Multi expression, EnclosingScope scope) { internalFailure("unknown expression: " + expression.getClass().getName(), scope.getSourceContext(), expression); } } catch (ResolveError rex) { - syntaxError(rex.getMessage(), scope.getSourceContext(), expression, rex); + internalFailure(rex.getMessage(), scope.getSourceContext(), expression, rex); } catch (SyntaxError se) { throw se; } catch (Exception ex) { @@ -1541,7 +1557,7 @@ public int generate(Expr expression, EnclosingScope scope) { internalFailure("unknown expression: " + expression.getClass().getName(), scope.getSourceContext(), expression); } } catch (ResolveError rex) { - syntaxError(rex.getMessage(), scope.getSourceContext(), expression, rex); + internalFailure(rex.getMessage(), scope.getSourceContext(), expression, rex); } catch (SyntaxError se) { throw se; } catch (Exception ex) { @@ -1680,13 +1696,7 @@ private int generate(Expr.ConstantAccess expr, EnclosingScope scope) throws Reso } private int generate(Expr.LocalVariable expr, EnclosingScope scope) throws ResolveError { - if (scope.get(expr.var) != null) { - int target = scope.get(expr.var); - return target; - } else { - syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), scope.getSourceContext(), expr); - return -1; - } + return scope.get(expr.var); } private int generate(Expr.UnOp expr, EnclosingScope scope) { @@ -1875,7 +1885,7 @@ private Bytecode.OperatorKind OP2BOP(Expr.BOp bop, SyntacticElement elem, Contex case RIGHTSHIFT: return Bytecode.OperatorKind.RIGHTSHIFT; default: - syntaxError(errorMessage(INVALID_BINARY_EXPRESSION), scope, elem); + internalFailure(errorMessage(INVALID_BINARY_EXPRESSION), scope, elem); } // dead-code return null; diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java index 178a8bab1c..9264f9e877 100644 --- a/modules/wyc/src/wyc/io/WhileyFileParser.java +++ b/modules/wyc/src/wyc/io/WhileyFileParser.java @@ -36,9 +36,12 @@ import java.util.List; import java.util.Set; +import com.sun.org.apache.bcel.internal.Constants; + import wyc.lang.*; import wyc.lang.Expr.ConstantAccess; import wyc.io.WhileyFileLexer.Token; +import static wyil.util.ErrorMessages.*; import static wyc.io.WhileyFileLexer.Token.Kind.*; import static wycc.lang.SyntaxError.*; import wyc.lang.WhileyFile.*; @@ -1241,6 +1244,7 @@ private List parseCaseBlock(WhileyFile wf, // with the appropriate level of indent. // ArrayList cases = new ArrayList(); + Indent nextIndent; while ((nextIndent = getIndent()) != null && indent.lessThanEq(nextIndent)) { @@ -1257,11 +1261,29 @@ private List parseCaseBlock(WhileyFile wf, // Second, parse the actual case statement at this point! cases.add(parseCaseStatement(wf, environment, indent)); } - + checkForDuplicateDefault(cases); return cases; } } + /** + * Check whether we have a duplicate default statement, or a case which + * occurs after a default statement (and, hence, is unreachable). + * + * @param cases + */ + private void checkForDuplicateDefault(List cases) { + boolean hasDefault = false; + for(Stmt.Case c: cases) { + if(c.expr.size() > 0 && hasDefault) { + syntaxError(errorMessage(UNREACHABLE_CODE), c); + } else if(c.expr.size() == 0 && hasDefault) { + syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), c); + } else { + hasDefault = c.expr.size() == 0; + } + } + } /** * Parse a case Statement, which has the form: * @@ -2779,7 +2801,7 @@ private Expr parseRecordExpression(WhileyFile wf, Token n = match(Identifier); // Check field name is unique if (keys.contains(n.text)) { - syntaxError("duplicate tuple key", n); + syntaxError("duplicate record key", n); } match(Colon); // Parse expression being assigned to field diff --git a/tests/invalid/DefiniteAssign_Invalid_4.sysout b/tests/invalid/DefiniteAssign_Invalid_4.sysout index 01884e94e9..fd0c7adff0 100644 --- a/tests/invalid/DefiniteAssign_Invalid_4.sysout +++ b/tests/invalid/DefiniteAssign_Invalid_4.sysout @@ -1,3 +1,3 @@ -../../tests/invalid/DefiniteAssign_Invalid_4.whiley:2: unknown variable - debug a - ^ +../../tests/invalid/DefiniteAssign_Invalid_4.whiley:5: unknown variable + f(x) + ^ diff --git a/tests/invalid/DefiniteAssign_Invalid_4.whiley b/tests/invalid/DefiniteAssign_Invalid_4.whiley index bc22099e66..3d24e46bd5 100644 --- a/tests/invalid/DefiniteAssign_Invalid_4.whiley +++ b/tests/invalid/DefiniteAssign_Invalid_4.whiley @@ -1,5 +1,5 @@ method f(any this) : - debug a + debug this method g() : f(x) diff --git a/tests/invalid/Parsing_Invalid_16.sysout b/tests/invalid/Parsing_Invalid_16.sysout index cca659a039..c1e7268bbf 100644 --- a/tests/invalid/Parsing_Invalid_16.sysout +++ b/tests/invalid/Parsing_Invalid_16.sysout @@ -1,3 +1,3 @@ -../../tests/invalid/Parsing_Invalid_16.whiley:2: duplicate tuple key +../../tests/invalid/Parsing_Invalid_16.whiley:2: duplicate record key return {x: 1, x: 2, y: 3} ^ From 5704dc79340472b833aa83bc95e44dec0cb472bd Mon Sep 17 00:00:00 2001 From: DavePearce Date: Wed, 20 Apr 2016 22:13:46 +1200 Subject: [PATCH 32/43] Fix for #622 (broken test cases) Several test cases where not passing as a result of changes made to the CodeGenerator. These are now resolved, though at least had to be done in a less than ideal fashion. --- modules/wyc/src/wyc/builder/CodeGenerator.java | 7 ++++++- modules/wyil/src/wyil/builders/VcExprGenerator.java | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 738e2efd2b..dc180ce2f5 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -1426,7 +1426,12 @@ private void generate(Iterator> srcIterator, String t Triple src = srcIterator.next(); // First, determine the src slot. - int varSlot = scope.allocate(Nominal.T_INT, src.first()); + int varSlot = scope.allocate(Nominal.T_INT); + // FIXME: the following line is a hack to deal with the relatively + // primitive way that VcGenerator determines the type of a variable. + // This should be removed when VcGenerator is reworked. + scope.environment.put(src.first(), varSlot); + // int startSlot = generate(src.second(), scope); int endSlot = generate(src.third(), scope); diff --git a/modules/wyil/src/wyil/builders/VcExprGenerator.java b/modules/wyil/src/wyil/builders/VcExprGenerator.java index 655925f7b4..0ea789de79 100644 --- a/modules/wyil/src/wyil/builders/VcExprGenerator.java +++ b/modules/wyil/src/wyil/builders/VcExprGenerator.java @@ -127,6 +127,7 @@ protected void transform(Bytecode.Operator code, BytecodeForest forest, VcBranch branch.write(code.target(i), branch.read(code.operand(i))); } break; + case NOT: case NEG: case ARRAYLENGTH: { Bytecode.Operator bc = (Bytecode.Operator) code; From 05df043d9d898a3f197a10321916865cf67e2cb4 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Thu, 21 Apr 2016 09:57:47 +1200 Subject: [PATCH 33/43] Moved checks for break/continue to Parser #621 Previously, the checks for determining whether a break or continue statement was contained within a loop were performed in CodeGenerator. This is not the right place to do these checks, and I have now moved them into the parser. In doing this, I refactored the parser a little which has improved it nicely. At this stage, the only remaining syntax check in CodeGenerator is for duplicate case labels. This is a little trickier to decide upon, and is left for #628 --- .../wyc/src/wyc/builder/CodeGenerator.java | 24 +- modules/wyc/src/wyc/io/WhileyFileParser.java | 1018 +++++++++-------- 2 files changed, 566 insertions(+), 476 deletions(-) diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index dc180ce2f5..925bc5b384 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -787,11 +787,7 @@ private void generate(Stmt.IfElse s, EnclosingScope scope) { * @return */ private void generate(Stmt.Break s, EnclosingScope scope) { - String breakLabel = scope.getBreakLabel(); - if (breakLabel == null) { - // FIXME: this should be located elsewhere - WhileyFile.syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP), scope.getSourceContext(), s); - } + String breakLabel = scope.getBreakLabel(); scope.add(new Bytecode.Goto(breakLabel)); } @@ -838,10 +834,7 @@ private void generate(Stmt.Break s, EnclosingScope scope) { * @return */ private void generate(Stmt.Continue s, EnclosingScope scope) { - String continueLabel = scope.getContinueLabel(); - if (continueLabel == null) { - WhileyFile.syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP), scope.getSourceContext(), s); - } + String continueLabel = scope.getContinueLabel(); scope.add(new Bytecode.Goto(continueLabel)); } @@ -903,7 +896,7 @@ private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { // FIXME: the following check should really occur earlier in the // pipeline. However, it is difficult to do it earlier because it's only // after FlowTypeChecker that we have determined the concrete values. - // See #6 + // See #628 checkNoDuplicateLabels(s.cases,scope); for (Stmt.Case c : s.cases) { @@ -953,7 +946,7 @@ private void generate(Stmt.Switch s, EnclosingScope scope) throws Exception { } /** - * Check that not two case statements have the same constant label + * Check that not two case statements have the same constant label. * * @param cases * @param indent @@ -967,14 +960,7 @@ private void checkNoDuplicateLabels(List cases, EnclosingScope scope) for(int i=0;i!=cases.size();++i) { Stmt.Case caseBlock = cases.get(i); List caseLabels = caseBlock.constants; - if(caseLabels == null) { - // Default case - if (labels.contains(null)) { - WhileyFile.syntaxError(errorMessage(DUPLICATE_DEFAULT_LABEL), scope.getSourceContext(), caseBlock); - } else { - labels.add(null); - } - } else { + if(caseLabels != null) { for (int j = 0; j != caseLabels.size(); ++j) { Constant c = caseLabels.get(j); if (labels.contains(c)) { diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java index 9264f9e877..bd2125b62f 100644 --- a/modules/wyc/src/wyc/io/WhileyFileParser.java +++ b/modules/wyc/src/wyc/io/WhileyFileParser.java @@ -286,9 +286,9 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, Token name = match(Identifier); - // Parse function or method parameters - HashSet environment = new HashSet(); - List parameters = parseParameters(wf,environment); + // Parse function or method parameters + EnclosingScope scope = new EnclosingScope(); + List parameters = parseParameters(wf,scope); // Parse (optional) return type List returns = Collections.EMPTY_LIST; @@ -298,7 +298,7 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, // environent and create a special one only for use within ensures // clauses, since these are the only expressions which may refer to // variables declared in the return type. - returns = parseOptionalParameters(wf,environment); + returns = parseOptionalParameters(wf,scope); } // Parse optional requires/ensures clauses @@ -310,13 +310,13 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, switch (lookahead.kind) { case Requires: // NOTE: expression terminated by ':' - requires.add(parseLogicalExpression(wf, environment, true)); + requires.add(parseLogicalExpression(wf, scope, true)); break; case Ensures: // Use the ensuresEnvironment here to get access to any // variables declared in the return type pattern. // NOTE: expression terminated by ':' - ensures.add(parseLogicalExpression(wf, environment, true)); + ensures.add(parseLogicalExpression(wf, scope, true)); break; } } @@ -334,7 +334,7 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, match(Colon); end = index; matchEndLine(); - stmts = parseBlock(wf, environment, ROOT_INDENT); + stmts = parseBlock(wf, scope, false); } WhileyFile.Declaration declaration; @@ -348,7 +348,7 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, wf.add(declaration); } - public List parseParameters(WhileyFile wf, HashSet environment) { + public List parseParameters(WhileyFile wf, EnclosingScope scope) { match(LeftBrace); ArrayList parameters = new ArrayList(); boolean firstTime = true; @@ -360,10 +360,10 @@ public List parseParameters(WhileyFile wf, HashSet environmen int pStart = index; Pair p = parseMixedType(); Token id = p.second(); - if (environment.contains(id.text)) { + if (scope.contains(id.text)) { syntaxError("parameter already declared", id); } else { - environment.add(id.text); + scope.add(id.text); } parameters.add(wf.new Parameter(p.first(), id.text, sourceAttr( pStart, index - 1))); @@ -372,19 +372,19 @@ public List parseParameters(WhileyFile wf, HashSet environmen } - public List parseOptionalParameters(WhileyFile wf, HashSet environment) { + public List parseOptionalParameters(WhileyFile wf, EnclosingScope scope) { int next = skipWhiteSpace(index); if(next < tokens.size() && tokens.get(next).kind == LeftBrace) { - return parseParameters(wf,environment); + return parseParameters(wf, scope); } else { - Parameter p = parseOptionalParameter(wf,environment); + Parameter p = parseOptionalParameter(wf, scope); ArrayList ps = new ArrayList(); ps.add(p); return ps; } } - public Parameter parseOptionalParameter(WhileyFile wf, HashSet environment) { + public Parameter parseOptionalParameter(WhileyFile wf, EnclosingScope scope) { int start = index; boolean braced = false; SyntacticType type; @@ -393,10 +393,10 @@ public Parameter parseOptionalParameter(WhileyFile wf, HashSet environme Pair p = parseMixedType(); type = p.first(); name = p.second().text; - if (environment.contains(name)) { + if (scope.contains(name)) { syntaxError("parameter already declared",p.second()); } else { - environment.add(name); + scope.add(name); } match(RightBrace); } else { @@ -445,15 +445,15 @@ public void parseTypeDeclaration(WhileyFile wf, List modifiers) { Token name = match(Identifier); match(Is); // Parse the type pattern - HashSet environment = new HashSet(); - Parameter p = parseOptionalParameter(wf,environment); + EnclosingScope scope = new EnclosingScope(); + Parameter p = parseOptionalParameter(wf, scope); ArrayList invariant = new ArrayList(); // Check whether or not there is an optional "where" clause. while (tryAndMatch(true, Where) != null) { // Yes, there is a "where" clause so parse the constraint. First, // construct the environment which will be used to identify the set // of declared variables in the current scope. - invariant.add(parseLogicalExpression(wf, environment, false)); + invariant.add(parseLogicalExpression(wf, scope, false)); } int end = index; matchEndLine(); @@ -498,7 +498,7 @@ private void parseConstantDeclaration(WhileyFile wf, // Token name = match(Identifier); match(Is); - Expr e = parseExpression(wf, new HashSet(), false); + Expr e = parseExpression(wf, new EnclosingScope(), false); int end = index; matchEndLine(); WhileyFile.Declaration declaration = wf.new Constant(modifiers, e, @@ -523,22 +523,25 @@ private void parseConstantDeclaration(WhileyFile wf, * The indentation level of the parent, for which all statements * in this block must have a greater indent. May not be * null. + * @param isLoop + * Indicates whether or not this block represents the body of a + * loop. This is important in order to setup the scope for this + * block appropriately. * @return */ - private List parseBlock(WhileyFile wf, HashSet environment, - Indent parentIndent) { - - // We must clone the environment here, in order to ensure variables - // declared within this block are properly scoped. - environment = new HashSet(environment); + private List parseBlock(WhileyFile wf, EnclosingScope scope, boolean isLoop) { // First, determine the initial indentation of this block based on the // first statement (or null if there is no statement). Indent indent = getIndent(); + // We must clone the environment here, in order to ensure variables + // declared within this block are properly scoped. + EnclosingScope blockScope = scope.newEnclosingScope(indent, isLoop); + // Second, check that this is indeed the initial indentation for this // block (i.e. that it is strictly greater than parent indent). - if (indent == null || indent.lessThanEq(parentIndent)) { + if (indent == null || indent.lessThanEq(scope.getIndent())) { // Initial indent either doesn't exist or is not strictly greater // than parent indent and,therefore, signals an empty block. // @@ -562,7 +565,7 @@ private List parseBlock(WhileyFile wf, HashSet environment, } // Second, parse the actual statement at this point! - stmts.add(parseStatement(wf, environment, indent)); + stmts.add(parseStatement(wf, blockScope)); } return stmts; @@ -601,20 +604,13 @@ private Indent getIndent() { * to construct some nested declarations (e.g. parameters for * lambdas) * - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. - * - * @param indent - * The indent level for the current statement. This is needed in - * order to constraint the indent level for any sub-blocks (e.g. - * for while or if statements). - * + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return */ - private Stmt parseStatement(WhileyFile wf, HashSet environment, - Indent indent) { + private Stmt parseStatement(WhileyFile wf, EnclosingScope scope) { checkNotEof(); Token lookahead = tokens.get(index); @@ -622,29 +618,29 @@ private Stmt parseStatement(WhileyFile wf, HashSet environment, switch (lookahead.kind) { case Assert: - return parseAssertStatement(wf, environment); + return parseAssertStatement(wf, scope); case Assume: - return parseAssumeStatement(wf, environment); + return parseAssumeStatement(wf, scope); case Break: - return parseBreakStatement(environment); + return parseBreakStatement(scope); case Continue: - return parseContinueStatement(environment); + return parseContinueStatement(scope); case Do: - return parseDoWhileStatement(wf, environment, indent); + return parseDoWhileStatement(wf, scope); case Debug: - return parseDebugStatement(wf, environment); + return parseDebugStatement(wf, scope); case Fail: - return parseFailStatement(environment); + return parseFailStatement(scope); case If: - return parseIfStatement(wf, environment, indent); + return parseIfStatement(wf, scope); case Return: - return parseReturnStatement(wf, environment); + return parseReturnStatement(wf, scope); case While: - return parseWhileStatement(wf, environment, indent); + return parseWhileStatement(wf, scope); case Skip: - return parseSkipStatement(environment); + return parseSkipStatement(scope); case Switch: - return parseSwitchStatement(wf, environment, indent); + return parseSwitchStatement(wf, scope); default: // fall through to the more difficult cases } @@ -657,7 +653,7 @@ private Stmt parseStatement(WhileyFile wf, HashSet environment, // be *any* of the three forms, but we definitely have an // expression-like thing at this point. Therefore, we parse that // expression and see what this gives and/or what follows... - return parseHeadlessStatement(wf,environment,indent); + return parseHeadlessStatement(wf, scope); } /** @@ -666,18 +662,19 @@ private Stmt parseStatement(WhileyFile wf, HashSet environment, * declarations. * * @param wf - * @param environment - * @param indent + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return */ - private Stmt parseHeadlessStatement(WhileyFile wf, HashSet environment, Indent indent) { - + private Stmt parseHeadlessStatement(WhileyFile wf, EnclosingScope scope) { int start = index; SyntacticType type = parseDefiniteType(); if (type == null) { // Can still be a variable declaration, assignment or invocation. - Expr e = parseExpression(wf, environment, false); + Expr e = parseExpression(wf, scope, false); if (e instanceof Expr.AbstractInvoke || e instanceof Expr.AbstractIndirectInvoke) { // Must be an invocation since these are neither valid // lvals (i.e. they cannot be assigned) nor types. @@ -690,12 +687,12 @@ private Stmt parseHeadlessStatement(WhileyFile wf, HashSet environment, // statement). index = start; // backtrack // - return parseAssignmentStatement(wf, environment); + return parseAssignmentStatement(wf, scope); } else if (tryAndMatch(true, Comma) != null) { // Must be an multi-assignment index = start; // backtrack // - return parseAssignmentStatement(wf, environment); + return parseAssignmentStatement(wf, scope); } else { // At this point, we must be left with a variable declaration. // Therefore, we backtrack and parse the expression again as a @@ -707,7 +704,7 @@ private Stmt parseHeadlessStatement(WhileyFile wf, HashSet environment, // Must be a variable declaration here. Token name = match(Identifier); WhileyFile.Parameter decl = wf.new Parameter(type, name.text, sourceAttr(start, index - 1)); - return parseVariableDeclaration(start, decl, wf, environment); + return parseVariableDeclaration(start, decl, wf, scope); } /** @@ -727,36 +724,39 @@ private Stmt parseHeadlessStatement(WhileyFile wf, HashSet environment, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.VariableDeclaration * * @return */ private Stmt.VariableDeclaration parseVariableDeclaration(int start, - Parameter parameter, WhileyFile wf, HashSet environment) { - HashSet originalEnvironment = (HashSet) environment.clone(); + Parameter parameter, WhileyFile wf, EnclosingScope scope) { // Ensure at least one variable is defined by this pattern. // Check that declared variables are not already defined. - if (environment.contains(parameter.name)) { + if (scope.contains(parameter.name)) { syntaxError("variable already declared", parameter); } else { - environment.add(parameter.name); + } // A variable declaration may optionally be assigned an initialiser // expression. Expr initialiser = null; if (tryAndMatch(true, Token.Kind.Equals) != null) { - initialiser = parseExpression(wf, originalEnvironment, false); + initialiser = parseExpression(wf, scope, false); } - // Finally, a new line indicates the end-of-statement + // Now, a new line indicates the end-of-statement int end = index; matchEndLine(); + // Finally, register the new variable in the enclosing scope. This + // should be done after parsing the initialiser expression to prevent it + // from referring to this variable. + scope.add(parameter.name); // Done. return new Stmt.VariableDeclaration(parameter, initialiser, sourceAttr( start, end - 1)); @@ -777,16 +777,16 @@ private Stmt.VariableDeclaration parseVariableDeclaration(int start, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Return * @return */ private Stmt.Return parseReturnStatement(WhileyFile wf, - HashSet environment) { + EnclosingScope scope) { int start = index; match(Return); @@ -799,7 +799,7 @@ private Stmt.Return parseReturnStatement(WhileyFile wf, // a potentially cryptic error message will be given. List returns = Collections.EMPTY_LIST; if (next < tokens.size() && tokens.get(next).kind != NewLine) { - returns = parseExpressions(wf,environment,false); + returns = parseExpressions(wf, scope,false); } // Finally, at this point we are expecting a new-line to signal the // end-of-statement. @@ -820,21 +820,21 @@ private Stmt.Return parseReturnStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Assert * @return */ private Stmt.Assert parseAssertStatement(WhileyFile wf, - HashSet environment) { + EnclosingScope scope) { int start = index; // Match the assert keyword match(Assert); // Parse the expression to be printed - Expr e = parseLogicalExpression(wf, environment, false); + Expr e = parseLogicalExpression(wf, scope, false); // Finally, at this point we are expecting a new-line to signal the // end-of-statement. int end = index; @@ -854,21 +854,21 @@ private Stmt.Assert parseAssertStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Assume * @return */ private Stmt.Assume parseAssumeStatement(WhileyFile wf, - HashSet environment) { + EnclosingScope scope) { int start = index; // Match the assume keyword match(Assume); // Parse the expression to be printed - Expr e = parseLogicalExpression(wf, environment, false); + Expr e = parseLogicalExpression(wf, scope, false); // Finally, at this point we are expecting a new-line to signal the // end-of-statement. int end = index; @@ -884,20 +884,24 @@ private Stmt.Assume parseAssumeStatement(WhileyFile wf, * BreakStmt ::= "break" *
* - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Break * @return */ - private Stmt.Break parseBreakStatement(HashSet environment) { + private Stmt.Break parseBreakStatement(EnclosingScope scope) { int start = index; // Match the break keyword - match(Break); + Token t = match(Break); int end = index; matchEndLine(); + // Check that break statement makes sense at this point. + if(!scope.isInLoop()) { + syntaxError(errorMessage(BREAK_OUTSIDE_SWITCH_OR_LOOP),t); + } // Done. return new Stmt.Break(sourceAttr(start, end - 1)); } @@ -909,20 +913,24 @@ private Stmt.Break parseBreakStatement(HashSet environment) { * ContinueStmt ::= "continue" *
* - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Continue * @return */ - private Stmt.Continue parseContinueStatement(HashSet environment) { + private Stmt.Continue parseContinueStatement(EnclosingScope scope) { int start = index; // Match the continue keyword - match(Continue); + Token t = match(Continue); int end = index; matchEndLine(); + // Check that continue statement makes sense at this point. + if(!scope.isInLoop()) { + syntaxError(errorMessage(CONTINUE_OUTSIDE_LOOP),t); + } // Done. return new Stmt.Continue(sourceAttr(start, end - 1)); } @@ -938,21 +946,20 @@ private Stmt.Continue parseContinueStatement(HashSet environment) { * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Debug * @return */ - private Stmt.Debug parseDebugStatement(WhileyFile wf, - HashSet environment) { + private Stmt.Debug parseDebugStatement(WhileyFile wf, EnclosingScope scope) { int start = index; // Match the debug keyword match(Debug); // Parse the expression to be printed - Expr e = parseExpression(wf, environment, false); + Expr e = parseExpression(wf, scope, false); // Finally, at this point we are expecting a new-line to signal the // end-of-statement. int end = index; @@ -974,33 +981,29 @@ private Stmt.Debug parseDebugStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this block. - * @param indent - * The indent level of this statement, which is needed to - * determine permissible indent level of child block(s). + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return * @author David J. Pearce * */ - private Stmt parseDoWhileStatement(WhileyFile wf, - HashSet environment, Indent indent) { + private Stmt parseDoWhileStatement(WhileyFile wf, EnclosingScope scope) { int start = index; match(Do); match(Colon); int end = index; matchEndLine(); // match the block - List blk = parseBlock(wf, environment, indent); + List blk = parseBlock(wf, scope, true); // match while and condition match(While); - Expr condition = parseLogicalExpression(wf, environment, false); + Expr condition = parseLogicalExpression(wf, scope, false); // Parse the loop invariants List invariants = new ArrayList(); while (tryAndMatch(true, Where) != null) { - invariants.add(parseLogicalExpression(wf, environment, false)); + invariants.add(parseLogicalExpression(wf, scope, false)); } matchEndLine(); return new Stmt.DoWhile(condition, invariants, blk, sourceAttr(start, @@ -1014,14 +1017,15 @@ private Stmt parseDoWhileStatement(WhileyFile wf, * FailStmt ::= "fail" * * - * @param environment - * The set of declared variables visible in the enclosing scope. - * The environment is not used by the fail statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Fail * @return */ - private Stmt.Fail parseFailStatement(HashSet environment) { + private Stmt.Fail parseFailStatement(EnclosingScope scope) { int start = index; // Match the fail keyword match(Fail); @@ -1048,44 +1052,40 @@ private Stmt.Fail parseFailStatement(HashSet environment) { * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. - * @param indent - * The indent level of this statement, which is needed to - * determine permissible indent level of child block(s). + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return */ - private Stmt.IfElse parseIfStatement(WhileyFile wf, - HashSet environment, Indent indent) { + private Stmt.IfElse parseIfStatement(WhileyFile wf, EnclosingScope scope) { int start = index; // An if statement begins with the keyword "if", followed by an // expression representing the condition. match(If); // NOTE: expression terminated by ':' - Expr c = parseLogicalExpression(wf, environment, true); + Expr c = parseLogicalExpression(wf, scope, true); // The a colon to signal the start of a block. match(Colon); matchEndLine(); int end = index; // First, parse the true branch, which is required - List tblk = parseBlock(wf, environment, indent); + List tblk = parseBlock(wf, scope, scope.isInLoop()); // Second, attempt to parse the false branch, which is optional. List fblk = Collections.emptyList(); - if (tryAndMatchAtIndent(true, indent, Else) != null) { + if (tryAndMatchAtIndent(true, scope.getIndent(), Else) != null) { int if_start = index; if (tryAndMatch(true, If) != null) { // This is an if-chain, so backtrack and parse a complete If index = if_start; fblk = new ArrayList(); - fblk.add(parseIfStatement(wf, environment, indent)); + fblk.add(parseIfStatement(wf, scope)); } else { match(Colon); matchEndLine(); - fblk = parseBlock(wf, environment, indent); + fblk = parseBlock(wf, scope, scope.isInLoop()); } } // Done! @@ -1105,33 +1105,29 @@ private Stmt.IfElse parseIfStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this block. - * @param indent - * The indent level of this statement, which is needed to - * determine permissible indent level of child block(s). + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return * @author David J. Pearce * */ - private Stmt parseWhileStatement(WhileyFile wf, - HashSet environment, Indent indent) { + private Stmt parseWhileStatement(WhileyFile wf, EnclosingScope scope) { int start = index; match(While); // NOTE: expression terminated by ':' - Expr condition = parseLogicalExpression(wf, environment, true); + Expr condition = parseLogicalExpression(wf, scope, true); // Parse the loop invariants List invariants = new ArrayList(); while (tryAndMatch(true, Where) != null) { // NOTE: expression terminated by ':' - invariants.add(parseLogicalExpression(wf, environment, true)); + invariants.add(parseLogicalExpression(wf, scope, true)); } match(Colon); int end = index; matchEndLine(); - List blk = parseBlock(wf, environment, indent); + List blk = parseBlock(wf, scope, true); return new Stmt.While(condition, invariants, blk, sourceAttr(start, end - 1)); } @@ -1143,15 +1139,15 @@ private Stmt parseWhileStatement(WhileyFile wf, * SkipStmt ::= "skip" * * - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this statement. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @see wyc.lang.Stmt.Skip * @return */ - private Stmt.Skip parseSkipStatement(HashSet environment) { + private Stmt.Skip parseSkipStatement(EnclosingScope scope) { int start = index; // Match the break keyword match(Skip); @@ -1176,28 +1172,24 @@ private Stmt.Skip parseSkipStatement(HashSet environment) { * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this block. - * @param indent - * The indent level of this statement, which is needed to - * determine permissible indent level of child block(s). + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return * @author David J. Pearce * */ - private Stmt parseSwitchStatement(WhileyFile wf, - HashSet environment, Indent indent) { + private Stmt parseSwitchStatement(WhileyFile wf, EnclosingScope scope) { int start = index; match(Switch); // NOTE: expression terminated by ':' - Expr condition = parseExpression(wf, environment, true); + Expr condition = parseExpression(wf, scope, true); match(Colon); int end = index; matchEndLine(); // Match case block - List cases = parseCaseBlock(wf, environment, indent); + List cases = parseCaseBlock(wf, scope); // Done return new Stmt.Switch(condition, cases, sourceAttr(start, end - 1)); } @@ -1215,26 +1207,26 @@ private Stmt parseSwitchStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param parentIndent - * The indentation level of the parent, for which all case - * statements in this block must have a greater indent. May not - * be null. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return */ - private List parseCaseBlock(WhileyFile wf, - HashSet environment, Indent parentIndent) { + private List parseCaseBlock(WhileyFile wf, EnclosingScope scope) { - // We must clone the environment here, in order to ensure variables - // declared within this block are properly scoped. - environment = new HashSet(environment); // First, determine the initial indentation of this block based on the // first statement (or null if there is no statement). Indent indent = getIndent(); - + + // We must create a new scope to ensure variables declared within this + // block are not visible in the enclosing scope. + EnclosingScope caseScope = scope.newEnclosingScope(indent); + // Second, check that this is indeed the initial indentation for this // block (i.e. that it is strictly greater than parent indent). - if (indent == null || indent.lessThanEq(parentIndent)) { + if (indent == null || indent.lessThanEq(scope.getIndent())) { // Initial indent either doesn't exist or is not strictly greater // than parent indent and,therefore, signals an empty block. // @@ -1259,7 +1251,7 @@ private List parseCaseBlock(WhileyFile wf, } // Second, parse the actual case statement at this point! - cases.add(parseCaseStatement(wf, environment, indent)); + cases.add(parseCaseStatement(wf, caseScope)); } checkForDuplicateDefault(cases); return cases; @@ -1295,17 +1287,13 @@ private void checkForDuplicateDefault(List cases) { * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this block. - * @param indent - * The indent level of this statement, which is needed to - * determine permissible indent level of child block(s). + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return */ - private Stmt.Case parseCaseStatement(WhileyFile wf, - HashSet environment, Indent indent) { + private Stmt.Case parseCaseStatement(WhileyFile wf, EnclosingScope scope) { int start = index; List values; if (tryAndMatch(true, Default) != null) { @@ -1316,13 +1304,13 @@ private Stmt.Case parseCaseStatement(WhileyFile wf, values = new ArrayList(); do { // NOTE: expression terminated by ':' - values.add(parseExpression(wf, environment, true)); + values.add(parseExpression(wf, scope, true)); } while (tryAndMatch(true, Comma) != null); } match(Colon); int end = index; matchEndLine(); - List stmts = parseBlock(wf, environment, indent); + List stmts = parseBlock(wf, scope, scope.isInLoop()); return new Stmt.Case(values, stmts, sourceAttr(start, end - 1)); } @@ -1355,19 +1343,18 @@ private Stmt.Case parseCaseStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within - * expressions used in this block. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @return */ - private Stmt parseAssignmentStatement(WhileyFile wf, - HashSet environment) { + private Stmt parseAssignmentStatement(WhileyFile wf, EnclosingScope scope) { int start = index; - List lvals = parseLVals(wf, environment); + List lvals = parseLVals(wf, scope); match(Equals); - List rvals = parseExpressions(wf, environment, false); + List rvals = parseExpressions(wf, scope, false); int end = index; matchEndLine(); return new Stmt.Assign(lvals, rvals, sourceAttr(start, end - 1)); @@ -1386,30 +1373,30 @@ private Stmt parseAssignmentStatement(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @return */ - private List parseLVals(WhileyFile wf, HashSet environment) { + private List parseLVals(WhileyFile wf, EnclosingScope scope) { int start = index; ArrayList elements = new ArrayList(); - elements.add(parseLVal(index, wf,environment)); + elements.add(parseLVal(index, wf, scope)); // Check whether we have a multiple lvals or not while (tryAndMatch(true, Comma) != null) { // Add all expressions separated by a comma - elements.add(parseLVal(index,wf, environment)); + elements.add(parseLVal(index,wf, scope)); // Done } return elements; } - private Expr.LVal parseLVal(int start, WhileyFile wf, HashSet environment) { - return parseAccessLVal(start, wf, environment); + private Expr.LVal parseLVal(int start, WhileyFile wf, EnclosingScope scope) { + return parseAccessLVal(start, wf, scope); } /** @@ -1427,15 +1414,15 @@ private Expr.LVal parseLVal(int start, WhileyFile wf, HashSet environmen * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @return */ - private Expr.LVal parseAccessLVal(int start, WhileyFile wf, HashSet environment) { - Expr.LVal lhs = parseLValTerm(start, wf, environment); + private Expr.LVal parseAccessLVal(int start, WhileyFile wf, EnclosingScope scope) { + Expr.LVal lhs = parseLValTerm(start, wf, scope); Token token; while ((token = tryAndMatchOnLine(LeftSquare)) != null @@ -1443,7 +1430,7 @@ private Expr.LVal parseAccessLVal(int start, WhileyFile wf, HashSet envi switch (token.kind) { case LeftSquare: // NOTE: expression is terminated by ']' - Expr rhs = parseAdditiveExpression(wf, environment, true); + Expr rhs = parseAdditiveExpression(wf, scope, true); match(RightSquare); lhs = new Expr.IndexOf(lhs, rhs, sourceAttr(start, index - 1)); break; @@ -1473,14 +1460,14 @@ private Expr.LVal parseAccessLVal(int start, WhileyFile wf, HashSet envi * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @return */ - private Expr.LVal parseLValTerm(int start, WhileyFile wf, HashSet environment) { + private Expr.LVal parseLValTerm(int start, WhileyFile wf, EnclosingScope scope) { checkNotEof(); // First, attempt to disambiguate the easy forms: Token lookahead = tokens.get(index); @@ -1491,13 +1478,13 @@ private Expr.LVal parseLValTerm(int start, WhileyFile wf, HashSet enviro index - 1)); case LeftBrace: { match(LeftBrace); - Expr.LVal lval = parseLVal(start, wf, environment); + Expr.LVal lval = parseLVal(start, wf, scope); match(RightBrace); return lval; } case Star: { match(Star); - Expr.LVal lval = parseLVal(start, wf, environment); + Expr.LVal lval = parseLVal(start, wf, scope); return new Expr.Dereference(lval, sourceAttr(start, index - 1)); } default: @@ -1511,12 +1498,27 @@ private Expr.LVal parseLValTerm(int start, WhileyFile wf, HashSet enviro * expressions separated by comma's * * @param wf - * @param environment + * The enclosing WhileyFile being constructed. This is necessary + * to construct some nested declarations (e.g. parameters for + * lambdas) + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated + * This indicates that the expression is known to be terminated + * (or not). An expression that's known to be terminated is one + * which is guaranteed to be followed by something. This is + * important because it means that we can ignore any newline + * characters encountered in parsing this expression, and that + * we'll never overrun the end of the expression (i.e. because + * there's guaranteed to be something which terminates this + * expression). A classic situation where terminated is true is + * when parsing an expression surrounded in braces. In such case, + * we know the right-brace will always terminate this expression. * @return */ - public List parseExpressions(WhileyFile wf, - HashSet environment, boolean terminated) { + public List parseExpressions(WhileyFile wf, EnclosingScope scope, boolean terminated) { ArrayList returns = new ArrayList(); // A return statement may optionally have a return expression. // Therefore, we first skip all whitespace on the given line. @@ -1525,9 +1527,9 @@ public List parseExpressions(WhileyFile wf, // then we assume what's remaining is the returned expression. This // means expressions must start on the same line as a return. Otherwise, // a potentially cryptic error message will be given. - returns.add(parseExpression(wf, environment, terminated)); + returns.add(parseExpression(wf, scope, terminated)); while(tryAndMatch(false,Comma) != null) { - returns.add(parseExpression(wf, environment, terminated)); + returns.add(parseExpression(wf, scope, terminated)); } return returns; } @@ -1557,10 +1559,10 @@ public List parseExpressions(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1574,9 +1576,8 @@ public List parseExpressions(WhileyFile wf, * we know the right-brace will always terminate this expression. * @return */ - private Expr parseExpression(WhileyFile wf, - HashSet environment, boolean terminated) { - return parseLogicalExpression(wf, environment, terminated); + private Expr parseExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { + return parseLogicalExpression(wf, scope, terminated); } /** @@ -1590,10 +1591,10 @@ private Expr parseExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1608,17 +1609,16 @@ private Expr parseExpression(WhileyFile wf, * * @return */ - private Expr parseLogicalExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + private Expr parseLogicalExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { checkNotEof(); int start = index; - Expr lhs = parseAndOrExpression(wf, environment, terminated); + Expr lhs = parseAndOrExpression(wf, scope, terminated); Token lookahead = tryAndMatch(terminated, LogicalImplication, LogicalIff); if (lookahead != null) { switch (lookahead.kind) { case LogicalImplication: { - Expr rhs = parseExpression(wf, environment, terminated); + Expr rhs = parseExpression(wf, scope, terminated); // FIXME: this is something of a hack, although it does work. It // would be nicer to have a binary expression kind for logical // implication. @@ -1629,7 +1629,7 @@ private Expr parseLogicalExpression(WhileyFile wf, index - 1)); } case LogicalIff: { - Expr rhs = parseExpression(wf, environment, terminated); + Expr rhs = parseExpression(wf, scope, terminated); // FIXME: this is something of a hack, although it does work. It // would be nicer to have a binary expression kind for logical // implication. @@ -1666,10 +1666,10 @@ private Expr parseLogicalExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1684,11 +1684,10 @@ private Expr parseLogicalExpression(WhileyFile wf, * * @return */ - private Expr parseAndOrExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + private Expr parseAndOrExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { checkNotEof(); int start = index; - Expr lhs = parseBitwiseOrExpression(wf, environment, terminated); + Expr lhs = parseBitwiseOrExpression(wf, scope, terminated); Token lookahead = tryAndMatch(terminated, LogicalAnd, LogicalOr); if (lookahead != null) { Expr.BOp bop; @@ -1702,7 +1701,7 @@ private Expr parseAndOrExpression(WhileyFile wf, default: throw new RuntimeException("deadcode"); // dead-code } - Expr rhs = parseExpression(wf, environment, terminated); + Expr rhs = parseExpression(wf, scope, terminated); return new Expr.BinOp(bop, lhs, rhs, sourceAttr(start, index - 1)); } @@ -1716,10 +1715,10 @@ private Expr parseAndOrExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1734,13 +1733,12 @@ private Expr parseAndOrExpression(WhileyFile wf, * * @return */ - private Expr parseBitwiseOrExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + private Expr parseBitwiseOrExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseBitwiseXorExpression(wf, environment, terminated); + Expr lhs = parseBitwiseXorExpression(wf, scope, terminated); if (tryAndMatch(terminated, VerticalBar) != null) { - Expr rhs = parseExpression(wf, environment, terminated); + Expr rhs = parseExpression(wf, scope, terminated); return new Expr.BinOp(Expr.BOp.BITWISEOR, lhs, rhs, sourceAttr( start, index - 1)); } @@ -1755,10 +1753,10 @@ private Expr parseBitwiseOrExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1773,13 +1771,12 @@ private Expr parseBitwiseOrExpression(WhileyFile wf, * * @return */ - private Expr parseBitwiseXorExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + private Expr parseBitwiseXorExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseBitwiseAndExpression(wf, environment, terminated); + Expr lhs = parseBitwiseAndExpression(wf, scope, terminated); if (tryAndMatch(terminated, Caret) != null) { - Expr rhs = parseExpression(wf, environment, terminated); + Expr rhs = parseExpression(wf, scope, terminated); return new Expr.BinOp(Expr.BOp.BITWISEXOR, lhs, rhs, sourceAttr( start, index - 1)); } @@ -1794,10 +1791,10 @@ private Expr parseBitwiseXorExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1813,12 +1810,12 @@ private Expr parseBitwiseXorExpression(WhileyFile wf, * @return */ private Expr parseBitwiseAndExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseConditionExpression(wf, environment, terminated); + Expr lhs = parseConditionExpression(wf, scope, terminated); if (tryAndMatch(terminated, Ampersand) != null) { - Expr rhs = parseExpression(wf, environment, terminated); + Expr rhs = parseExpression(wf, scope, terminated); return new Expr.BinOp(Expr.BOp.BITWISEAND, lhs, rhs, sourceAttr( start, index - 1)); } @@ -1833,10 +1830,10 @@ private Expr parseBitwiseAndExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1852,17 +1849,17 @@ private Expr parseBitwiseAndExpression(WhileyFile wf, * @return */ private Expr parseConditionExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; Token lookahead; // First, attempt to parse quantifiers (e.g. some, all, no, etc) if ((lookahead = tryAndMatch(terminated, Some, No, All)) != null) { - return parseQuantifierExpression(lookahead, wf, environment, + return parseQuantifierExpression(lookahead, wf, scope, terminated); } - Expr lhs = parseShiftExpression(wf, environment, terminated); + Expr lhs = parseShiftExpression(wf, scope, terminated); lookahead = tryAndMatch(terminated, LessEquals, LeftAngle, GreaterEquals, RightAngle, EqualsEquals, NotEquals, Is, @@ -1899,7 +1896,7 @@ private Expr parseConditionExpression(WhileyFile wf, throw new RuntimeException("deadcode"); // dead-code } - Expr rhs = parseShiftExpression(wf, environment, terminated); + Expr rhs = parseShiftExpression(wf, scope, terminated); return new Expr.BinOp(bop, lhs, rhs, sourceAttr(start, index - 1)); } @@ -1921,10 +1918,10 @@ private Expr parseConditionExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -1936,12 +1933,10 @@ private Expr parseConditionExpression(WhileyFile wf, * expression). A classic situation where terminated is true is * when parsing an expression surrounded in braces. In such case, * we know the right-brace will always terminate this expression. - * - * @param environment * @return */ private Expr parseQuantifierExpression(Token lookahead, WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index - 1; // Determine the quantifier operation @@ -1963,7 +1958,7 @@ private Expr parseQuantifierExpression(Token lookahead, WhileyFile wf, match(LeftCurly); // Parse one or more source variables / expressions - environment = new HashSet(environment); + scope = scope.newEnclosingScope(); List> srcs = new ArrayList>(); boolean firstTime = true; @@ -1973,20 +1968,20 @@ private Expr parseQuantifierExpression(Token lookahead, WhileyFile wf, } firstTime = false; Token id = match(Identifier); - if (environment.contains(id.text)) { + if (scope.contains(id.text)) { // It is already defined which is a syntax error syntaxError("variable already declared", id); } match(In); - Expr lhs = parseAdditiveExpression(wf, environment, terminated); + Expr lhs = parseAdditiveExpression(wf, scope, terminated); match(DotDot); - Expr rhs = parseAdditiveExpression(wf, environment, terminated); + Expr rhs = parseAdditiveExpression(wf, scope, terminated); srcs.add(new Triple(id.text, lhs, rhs)); - environment.add(id.text); + scope.add(id.text); } while (eventuallyMatch(VerticalBar) == null); // Parse condition over source variables - Expr condition = parseLogicalExpression(wf, environment, terminated); + Expr condition = parseLogicalExpression(wf, scope, terminated); match(RightCurly); @@ -2007,10 +2002,10 @@ private Expr parseQuantifierExpression(Token lookahead, WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2026,12 +2021,12 @@ private Expr parseQuantifierExpression(Token lookahead, WhileyFile wf, * @return */ private Expr parseRangeExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseShiftExpression(wf, environment, terminated); + Expr lhs = parseShiftExpression(wf, scope, terminated); if (tryAndMatch(terminated, DotDot) != null) { - Expr rhs = parseAdditiveExpression(wf, environment, terminated); + Expr rhs = parseAdditiveExpression(wf, scope, terminated); return new Expr.BinOp(Expr.BOp.RANGE, lhs, rhs, sourceAttr(start, index - 1)); } @@ -2050,10 +2045,10 @@ private Expr parseRangeExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2069,14 +2064,14 @@ private Expr parseRangeExpression(WhileyFile wf, * @return */ private Expr parseShiftExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseAdditiveExpression(wf, environment, terminated); + Expr lhs = parseAdditiveExpression(wf, scope, terminated); Token lookahead; while ((lookahead = tryAndMatch(terminated, LeftAngleLeftAngle, RightAngleRightAngle)) != null) { - Expr rhs = parseAdditiveExpression(wf, environment, terminated); + Expr rhs = parseAdditiveExpression(wf, scope, terminated); Expr.BOp bop = null; switch (lookahead.kind) { case LeftAngleLeftAngle: @@ -2099,10 +2094,10 @@ private Expr parseShiftExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2118,9 +2113,9 @@ private Expr parseShiftExpression(WhileyFile wf, * @return */ private Expr parseAdditiveExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseMultiplicativeExpression(wf, environment, terminated); + Expr lhs = parseMultiplicativeExpression(wf, scope, terminated); Token lookahead; while ((lookahead = tryAndMatch(terminated, Plus, Minus)) != null) { @@ -2136,7 +2131,7 @@ private Expr parseAdditiveExpression(WhileyFile wf, throw new RuntimeException("deadcode"); // dead-code } - Expr rhs = parseMultiplicativeExpression(wf, environment, + Expr rhs = parseMultiplicativeExpression(wf, scope, terminated); lhs = new Expr.BinOp(bop, lhs, rhs, sourceAttr(start, index - 1)); } @@ -2151,10 +2146,10 @@ private Expr parseAdditiveExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2170,9 +2165,9 @@ private Expr parseAdditiveExpression(WhileyFile wf, * @return */ private Expr parseMultiplicativeExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseAccessExpression(wf, environment, terminated); + Expr lhs = parseAccessExpression(wf, scope, terminated); Token lookahead = tryAndMatch(terminated, Star, RightSlash, Percent); if (lookahead != null) { @@ -2190,7 +2185,7 @@ private Expr parseMultiplicativeExpression(WhileyFile wf, default: throw new RuntimeException("deadcode"); // dead-code } - Expr rhs = parseAccessExpression(wf, environment, terminated); + Expr rhs = parseAccessExpression(wf, scope, terminated); return new Expr.BinOp(bop, lhs, rhs, sourceAttr(start, index - 1)); } @@ -2232,10 +2227,10 @@ private Expr parseMultiplicativeExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2251,9 +2246,9 @@ private Expr parseMultiplicativeExpression(WhileyFile wf, * @return */ private Expr parseAccessExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; - Expr lhs = parseTermExpression(wf, environment, terminated); + Expr lhs = parseTermExpression(wf, scope, terminated); Token token; while ((token = tryAndMatchOnLine(LeftSquare)) != null @@ -2261,7 +2256,7 @@ private Expr parseAccessExpression(WhileyFile wf, switch (token.kind) { case LeftSquare: // NOTE: expression guaranteed to be terminated by ']'. - Expr rhs = parseAdditiveExpression(wf, environment, true); + Expr rhs = parseAdditiveExpression(wf, scope, true); // This is a plain old array access expression match(RightSquare); lhs = new Expr.IndexOf(lhs, rhs, sourceAttr(start, @@ -2281,13 +2276,12 @@ private Expr parseAccessExpression(WhileyFile wf, // by examining what we have parsed already. A direct access or // invocation requires a sequence of identifiers where the first // is not a declared variable name. - Path.ID id = parsePossiblePathID(lhs, environment); + Path.ID id = parsePossiblePathID(lhs, scope); if (tryAndMatch(terminated, LeftBrace) != null) { // This indicates a direct or indirect invocation. First, // parse arguments to invocation - ArrayList arguments = parseInvocationArguments(wf, - environment); + ArrayList arguments = parseInvocationArguments(wf,scope); // Second, determine what kind of invocation we have. if(id == null) { // This indicates we have an indirect invocation @@ -2322,10 +2316,13 @@ private Expr parseAccessExpression(WhileyFile wf, * environment. * * @param src - * @param environment + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @return */ - private Path.ID parsePossiblePathID(Expr src, HashSet environment) { + private Path.ID parsePossiblePathID(Expr src, EnclosingScope scope) { if(src instanceof Expr.LocalVariable) { // this is a local variable, indicating that the we did not have // a module identifier. @@ -2335,7 +2332,7 @@ private Path.ID parsePossiblePathID(Expr src, HashSet environment) { return Trie.ROOT.append(ca.name); } else if(src instanceof Expr.FieldAccess) { Expr.FieldAccess ada = (Expr.FieldAccess) src; - Path.ID id = parsePossiblePathID(ada.src,environment); + Path.ID id = parsePossiblePathID(ada.src, scope); if(id != null) { return id.append(ada.name); } else { @@ -2352,10 +2349,10 @@ private Path.ID parsePossiblePathID(Expr src, HashSet environment) { * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2371,7 +2368,7 @@ private Path.ID parsePossiblePathID(Expr src, HashSet environment) { * @return */ private Expr parseTermExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { checkNotEof(); int start = index; @@ -2379,15 +2376,15 @@ private Expr parseTermExpression(WhileyFile wf, switch (token.kind) { case LeftBrace: - return parseBracketedExpression(wf, environment, terminated); + return parseBracketedExpression(wf, scope, terminated); case New: - return parseNewExpression(wf, environment, terminated); + return parseNewExpression(wf, scope, terminated); case Identifier: match(Identifier); if (tryAndMatch(terminated, LeftBrace) != null) { - return parseInvokeExpression(wf, environment, start, token, + return parseInvokeExpression(wf, scope, start, token, terminated); - } else if (environment.contains(token.text)) { + } else if (scope.contains(token.text)) { // Signals a local variable access return new Expr.LocalVariable(token.text, sourceAttr(start, index - 1)); @@ -2429,21 +2426,21 @@ private Expr parseTermExpression(WhileyFile wf, sourceAttr(start, index++)); } case Minus: - return parseNegationExpression(wf, environment, terminated); + return parseNegationExpression(wf, scope, terminated); case VerticalBar: - return parseLengthOfExpression(wf, environment, terminated); + return parseLengthOfExpression(wf, scope, terminated); case LeftSquare: - return parseArrayInitialiserOrGeneratorExpression(wf, environment, terminated); + return parseArrayInitialiserOrGeneratorExpression(wf, scope, terminated); case LeftCurly: - return parseRecordExpression(wf, environment, terminated); + return parseRecordExpression(wf, scope, terminated); case Shreak: - return parseLogicalNotExpression(wf, environment, terminated); + return parseLogicalNotExpression(wf, scope, terminated); case Star: - return parseDereferenceExpression(wf, environment, terminated); + return parseDereferenceExpression(wf, scope, terminated); case Tilde: - return parseBitwiseComplementExpression(wf, environment, terminated); + return parseBitwiseComplementExpression(wf, scope, terminated); case Ampersand: - return parseLambdaOrAddressExpression(wf, environment, terminated); + return parseLambdaOrAddressExpression(wf, scope, terminated); } syntaxError("unrecognised term", token); @@ -2506,10 +2503,10 @@ private Expr parseTermExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2525,7 +2522,7 @@ private Expr parseTermExpression(WhileyFile wf, * @return */ private Expr parseBracketedExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(LeftBrace); @@ -2547,7 +2544,7 @@ private Expr parseBracketedExpression(WhileyFile wf, // bracketed type. if (tryAndMatch(true, RightBrace) != null) { // Ok, finally, we are sure that it is definitely a cast. - Expr e = parseExpression(wf, environment, terminated); + Expr e = parseExpression(wf, scope, terminated); return new Expr.Cast(t, e, sourceAttr(start, index - 1)); } } @@ -2555,7 +2552,7 @@ private Expr parseBracketedExpression(WhileyFile wf, // cannot tell which yet. index = start; match(LeftBrace); - Expr e = parseExpression(wf, environment, true); + Expr e = parseExpression(wf, scope, true); match(RightBrace); // Now check whether this must be an expression, or could still be a @@ -2599,7 +2596,7 @@ private Expr parseBracketedExpression(WhileyFile wf, index = start; // backtrack SyntacticType type = parseType(); // Now, parse cast expression - e = parseExpression(wf, environment, terminated); + e = parseExpression(wf, scope, terminated); return new Expr.Cast(type, e, sourceAttr(start, index - 1)); } default: @@ -2624,10 +2621,10 @@ private Expr parseBracketedExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2643,19 +2640,19 @@ private Expr parseBracketedExpression(WhileyFile wf, * @return */ private Expr parseArrayInitialiserOrGeneratorExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(LeftSquare); - Expr expr = parseExpression(wf, environment, true); + Expr expr = parseExpression(wf, scope, true); // Finally, disambiguate if(tryAndMatch(true,SemiColon) != null) { // this is an array generator index = start; - return parseArrayGeneratorExpression(wf,environment,terminated); + return parseArrayGeneratorExpression(wf, scope,terminated); } else { // this is an array initialiser index = start; - return parseArrayInitialiserExpression(wf,environment,terminated); + return parseArrayInitialiserExpression(wf, scope,terminated); } } @@ -2670,10 +2667,10 @@ private Expr parseArrayInitialiserOrGeneratorExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2689,7 +2686,7 @@ private Expr parseArrayInitialiserOrGeneratorExpression(WhileyFile wf, * @return */ private Expr parseArrayInitialiserExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(LeftSquare); ArrayList exprs = new ArrayList(); @@ -2706,7 +2703,7 @@ private Expr parseArrayInitialiserExpression(WhileyFile wf, // list constructor expression is used ',' to distinguish elements. // Also, expression is guaranteed to be terminated, either by ']' or // ','. - exprs.add(parseExpression(wf, environment, true)); + exprs.add(parseExpression(wf, scope, true)); } while (eventuallyMatch(RightSquare) == null); return new Expr.ArrayInitialiser(exprs, sourceAttr(start, index - 1)); @@ -2723,10 +2720,10 @@ private Expr parseArrayInitialiserExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2742,12 +2739,12 @@ private Expr parseArrayInitialiserExpression(WhileyFile wf, * @return */ private Expr parseArrayGeneratorExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(LeftSquare); - Expr element = parseExpression(wf, environment, true); + Expr element = parseExpression(wf, scope, true); match(SemiColon); - Expr count = parseExpression(wf, environment, true); + Expr count = parseExpression(wf, scope, true); match(RightSquare); return new Expr.ArrayGenerator(element,count,sourceAttr(start, index - 1)); } @@ -2766,10 +2763,10 @@ private Expr parseArrayGeneratorExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2785,7 +2782,7 @@ private Expr parseArrayGeneratorExpression(WhileyFile wf, * @return */ private Expr parseRecordExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(LeftCurly); HashSet keys = new HashSet(); @@ -2811,7 +2808,7 @@ private Expr parseRecordExpression(WhileyFile wf, // record constructor expression is used ',' to distinguish fields. // Also, expression is guaranteed to be terminated, either by '}' or // ','. - Expr e = parseExpression(wf, environment, true); + Expr e = parseExpression(wf, scope, true); exprs.put(n.text, e); keys.add(n.text); } @@ -2831,10 +2828,10 @@ private Expr parseRecordExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2849,11 +2846,11 @@ private Expr parseRecordExpression(WhileyFile wf, * * @return */ - private Expr parseNewExpression(WhileyFile wf, HashSet environment, + private Expr parseNewExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { int start = index; match(New); - Expr e = parseExpression(wf, environment, terminated); + Expr e = parseExpression(wf, scope, terminated); return new Expr.New(e, sourceAttr(start, index - 1)); } @@ -2869,10 +2866,10 @@ private Expr parseNewExpression(WhileyFile wf, HashSet environment, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2888,7 +2885,7 @@ private Expr parseNewExpression(WhileyFile wf, HashSet environment, * @return */ private Expr parseLengthOfExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(VerticalBar); // We have to parse an Append Expression here, which is the most general @@ -2897,7 +2894,7 @@ private Expr parseLengthOfExpression(WhileyFile wf, // collections. Furthermore, the bitwise or expression could lead to // ambiguity and, hence, we bypass that an consider append expressions // only. However, the expression is guaranteed to be terminated by '|'. - Expr e = parseShiftExpression(wf, environment, true); + Expr e = parseShiftExpression(wf, scope, true); match(VerticalBar); return new Expr.UnOp(Expr.UOp.ARRAYLENGTH, e, sourceAttr(start, index - 1)); } @@ -2914,10 +2911,10 @@ private Expr parseLengthOfExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2933,10 +2930,10 @@ private Expr parseLengthOfExpression(WhileyFile wf, * @return */ private Expr parseNegationExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Minus); - Expr e = parseAccessExpression(wf, environment, terminated); + Expr e = parseAccessExpression(wf, scope, terminated); return new Expr.UnOp(Expr.UOp.NEG, e, sourceAttr(start, index - 1)); } @@ -2954,10 +2951,10 @@ private Expr parseNegationExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -2972,16 +2969,15 @@ private Expr parseNegationExpression(WhileyFile wf, * * @return */ - private Expr parseInvokeExpression(WhileyFile wf, - HashSet environment, int start, Token name, + private Expr parseInvokeExpression(WhileyFile wf, EnclosingScope scope, int start, Token name, boolean terminated) { // First, parse the arguments to this invocation. - ArrayList args = parseInvocationArguments(wf, environment); + ArrayList args = parseInvocationArguments(wf, scope); // Second, determine what kind of invocation we have. If the name of the // method is a local variable, then it must be an indirect invocation on // this variable. - if (environment.contains(name.text)) { + if (scope.contains(name.text)) { // indirect invocation on local variable Expr.LocalVariable lv = new Expr.LocalVariable(name.text, sourceAttr(start, start)); @@ -3009,10 +3005,10 @@ private Expr parseInvokeExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -3028,7 +3024,7 @@ private Expr parseInvokeExpression(WhileyFile wf, * @return */ private ArrayList parseInvocationArguments(WhileyFile wf, - HashSet environment) { + EnclosingScope scope) { boolean firstTime = true; ArrayList args = new ArrayList(); while (eventuallyMatch(RightBrace) == null) { @@ -3043,7 +3039,7 @@ private ArrayList parseInvocationArguments(WhileyFile wf, // invocation expression is used ',' to distinguish arguments. // However, expression is guaranteed to be terminated either by ')' // or by ','. - Expr e = parseExpression(wf, environment, true); + Expr e = parseExpression(wf, scope, true); args.add(e); } @@ -3062,10 +3058,10 @@ private ArrayList parseInvocationArguments(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -3081,13 +3077,13 @@ private ArrayList parseInvocationArguments(WhileyFile wf, * @return */ private Expr parseLogicalNotExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Shreak); // Note: cannot parse unit expression here, because that messes up the // precedence. For example, !result ==> other should be parsed as // (!result) ==> other, not !(result ==> other). - Expr expression = parseConditionExpression(wf, environment, terminated); + Expr expression = parseConditionExpression(wf, scope, terminated); return new Expr.UnOp(Expr.UOp.NOT, expression, sourceAttr(start, index - 1)); } @@ -3104,18 +3100,18 @@ private Expr parseLogicalNotExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * * @return */ private Expr parseDereferenceExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Star); - Expr expression = parseExpression(wf, environment, terminated); + Expr expression = parseExpression(wf, scope, terminated); return new Expr.Dereference(expression, sourceAttr(start, index - 1)); } @@ -3135,10 +3131,10 @@ private Expr parseDereferenceExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -3154,15 +3150,15 @@ private Expr parseDereferenceExpression(WhileyFile wf, * @return */ private Expr parseLambdaOrAddressExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Ampersand); if (tryAndMatch(terminated, LeftBrace) != null) { index = start; // backtrack - return parseLambdaExpression(wf, environment, terminated); + return parseLambdaExpression(wf, scope, terminated); } else { index = start; // backtrack - return parseAddressExpression(wf, environment, terminated); + return parseAddressExpression(wf, scope, terminated); } } @@ -3178,10 +3174,10 @@ private Expr parseLambdaOrAddressExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -3197,14 +3193,14 @@ private Expr parseLambdaOrAddressExpression(WhileyFile wf, * @return */ private Expr parseLambdaExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Ampersand); match(LeftBrace); ArrayList parameters = new ArrayList(); // Clone the environment so we can update it with those declared // parameters. - environment = new HashSet(environment); + scope = scope.newEnclosingScope(); boolean firstTime = true; while (eventuallyMatch(MinusGreater) == null) { int p_start = index; @@ -3214,16 +3210,16 @@ private Expr parseLambdaExpression(WhileyFile wf, firstTime = false; SyntacticType type = parseType(); Token id = match(Identifier); - if (environment.contains(id.text)) { + if (scope.contains(id.text)) { syntaxError("duplicate variable or parameter name", id); } - environment.add(id.text); + scope.add(id.text); parameters.add(wf.new Parameter(type, id.text, sourceAttr(p_start, index - 1))); } // NOTE: expression guanrateed to be terminated by ')' - Expr body = parseExpression(wf, environment, true); + Expr body = parseExpression(wf, scope, true); match(RightBrace); return new Expr.Lambda(parameters, body, sourceAttr(start, index - 1)); @@ -3241,10 +3237,10 @@ private Expr parseLambdaExpression(WhileyFile wf, * The enclosing WhileyFile being constructed. This is necessary * to construct some nested declarations (e.g. parameters for * lambdas) - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -3260,7 +3256,7 @@ private Expr parseLambdaExpression(WhileyFile wf, * @return */ private Expr parseAddressExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Ampersand); @@ -3297,10 +3293,10 @@ private Expr parseAddressExpression(WhileyFile wf, * | '~' Expr// bitwise complement * * - * @param environment - * The set of declared variables visible in the enclosing scope. - * This is necessary to identify local variables within this - * expression. + * @param scope + * The enclosing scope for this statement, which determines the + * set of visible (i.e. declared) variables and also the current + * indentation level. * @param terminated * This indicates that the expression is known to be terminated * (or not). An expression that's known to be terminated is one @@ -3317,10 +3313,10 @@ private Expr parseAddressExpression(WhileyFile wf, */ private Expr parseBitwiseComplementExpression(WhileyFile wf, - HashSet environment, boolean terminated) { + EnclosingScope scope, boolean terminated) { int start = index; match(Tilde); - Expr expression = parseExpression(wf, environment, terminated); + Expr expression = parseExpression(wf, scope, terminated); return new Expr.UnOp(Expr.UOp.INVERT, expression, sourceAttr(start, index - 1)); } @@ -4402,4 +4398,112 @@ public boolean equivalent(Indent other) { * code for parsing indentation. */ private static final Indent ROOT_INDENT = new Indent("", 0); + + /** + * The enclosing scope provides contextual information about the enclosing + * scope for the given statement or expression being parsed. + * + * @author David J. Pearce + * + */ + private static class EnclosingScope { + /** + * The indent level of the enclosing scope. + */ + private final Indent indent; + + /** + * The set of declared variables in the enclosing scope. + */ + private final HashSet environment; + + /** + * A simple flag that tells us whether or not we are currently within a + * loop. This is necessary to stop break or continue statements which + * are written outside of a loop. + */ + private final boolean inLoop; + + public EnclosingScope() { + this.indent = ROOT_INDENT; + this.environment = new HashSet(); + this.inLoop = false; + } + + private EnclosingScope(Indent indent, Set environment, boolean inLoop) { + this.indent = indent; + this.environment = new HashSet(environment); + this.inLoop = inLoop; + } + + public Indent getIndent() { + return indent; + } + + public boolean isInLoop() { + return inLoop; + } + + /** + * Check whether a given name corresponds to a declared variable in this + * scope. + * + * @param name + * @return + */ + public boolean contains(String name) { + return environment.contains(name); + } + + /** + * Declare a new variable in this scope. + * + * @param name + */ + public void add(String name) { + environment.add(name); + } + + /** + * Create a new enclosing scope in which variables can be declared which + * are remain invisible to this enclosing scope. All variables declared + * in this enclosing scope remain declared in the new enclosing scope. + * + * @param indent + * the indent level for the new scope + * + * @return + */ + public EnclosingScope newEnclosingScope() { + return new EnclosingScope(indent,environment,inLoop); + } + + /** + * Create a new enclosing scope in which variables can be declared which + * are remain invisible to this enclosing scope. All variables declared + * in this enclosing scope remain declared in the new enclosing scope. + * + * @param indent + * the indent level for the new scope + * + * @return + */ + public EnclosingScope newEnclosingScope(Indent indent) { + return new EnclosingScope(indent,environment,inLoop); + } + + /** + * Create a new enclosing scope in which variables can be declared which + * are remain invisible to this enclosing scope. All variables declared + * in this enclosing scope remain declared in the new enclosing scope. + * + * @param indent + * the indent level for the new scope + * + * @return + */ + public EnclosingScope newEnclosingScope(Indent indent, boolean inLoop) { + return new EnclosingScope(indent,environment,inLoop); + } + } } From afbb424a318a48432faba23b245f700e09d0d92e Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Thu, 21 Apr 2016 14:34:20 +1200 Subject: [PATCH 34/43] fix #632 function subtyping --- modules/wyil/src/wyil/util/type/SubtypeOperator.java | 4 ++-- tests/invalid/Function_Invalid_11.sysout | 3 +++ tests/invalid/Function_Invalid_11.whiley | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/invalid/Function_Invalid_11.sysout create mode 100644 tests/invalid/Function_Invalid_11.whiley diff --git a/modules/wyil/src/wyil/util/type/SubtypeOperator.java b/modules/wyil/src/wyil/util/type/SubtypeOperator.java index d1ac93d8d1..d0b6b04535 100644 --- a/modules/wyil/src/wyil/util/type/SubtypeOperator.java +++ b/modules/wyil/src/wyil/util/type/SubtypeOperator.java @@ -240,12 +240,12 @@ protected boolean isIntersectionInner(int fromIndex, boolean fromSign, int toInd int[] fromChildren = fromState.children; int[] toChildren = toState.children; if(fromChildren.length != toChildren.length){ - return false; + return !fromSign || !toSign; } int fromNumParams = (Integer) fromState.data; int toNumParams = (Integer) toState.data; if(fromNumParams != toNumParams){ - return false; + return !fromSign || !toSign; } boolean andChildren = true; boolean orChildren = false; diff --git a/tests/invalid/Function_Invalid_11.sysout b/tests/invalid/Function_Invalid_11.sysout new file mode 100644 index 0000000000..273275f529 --- /dev/null +++ b/tests/invalid/Function_Invalid_11.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Function_Invalid_11.whiley:4: expected type Function_Invalid_11:fun, found function(int,int)->(int) + fun f = &(int x, int y -> y) + ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Function_Invalid_11.whiley b/tests/invalid/Function_Invalid_11.whiley new file mode 100644 index 0000000000..5112022e33 --- /dev/null +++ b/tests/invalid/Function_Invalid_11.whiley @@ -0,0 +1,5 @@ +type fun is function(int)->(int) + +function foo(): + fun f = &(int x, int y -> y) + From 799a4cab0b31bcd90b03a409a386267c80af83e8 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Thu, 21 Apr 2016 14:50:53 +1200 Subject: [PATCH 35/43] fix whiley.io.File --- modules/wyrt/src/whiley/io/File.whiley | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/wyrt/src/whiley/io/File.whiley b/modules/wyrt/src/whiley/io/File.whiley index 55576a7675..909bbfff89 100755 --- a/modules/wyrt/src/whiley/io/File.whiley +++ b/modules/wyrt/src/whiley/io/File.whiley @@ -107,4 +107,4 @@ private native method read(NativeFile f, int max) -> byte[] private native method read(NativeFile f) -> byte[] // write entire contents of native file -private native method write(NativeFile f, byte[] data) +private native method write(NativeFile f, byte[] data) -> uint From ab0d9a6aa2ad63d8320c6a216bb5fd4cc265f454 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Sun, 24 Apr 2016 02:40:58 +1200 Subject: [PATCH 36/43] recognize function and method types as definite types (closes #634) --- modules/wyc/src/wyc/io/WhileyFileParser.java | 11 ++--------- tests/valid/Lambda_Valid_11.whiley | 4 ++++ 2 files changed, 6 insertions(+), 9 deletions(-) create mode 100644 tests/valid/Lambda_Valid_11.whiley diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java index bd2125b62f..be2b94c3f4 100644 --- a/modules/wyc/src/wyc/io/WhileyFileParser.java +++ b/modules/wyc/src/wyc/io/WhileyFileParser.java @@ -3375,15 +3375,8 @@ private boolean mustParseAsType(SyntacticType type) { // valid expression. return true; } else if (type instanceof SyntacticType.FunctionOrMethod) { - SyntacticType.FunctionOrMethod tt = (SyntacticType.FunctionOrMethod) type; - boolean result = false; - for (SyntacticType element : tt.paramTypes) { - result |= mustParseAsType(element); - } - for (SyntacticType element : tt.returnTypes) { - result |= mustParseAsType(element); - } - return result; + // "function" and "method" are keywords, cannot parse as expression. + return true; } else if (type instanceof SyntacticType.Intersection) { SyntacticType.Intersection tt = (SyntacticType.Intersection) type; boolean result = false; diff --git a/tests/valid/Lambda_Valid_11.whiley b/tests/valid/Lambda_Valid_11.whiley new file mode 100644 index 0000000000..6aa20ebaac --- /dev/null +++ b/tests/valid/Lambda_Valid_11.whiley @@ -0,0 +1,4 @@ +type myint is int + +public export method test(): + method()->(&myint) m = &(-> new 1) From d33ef9432c793ac02815269a75d733932f4b4c3a Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Sun, 24 Apr 2016 21:50:39 +1200 Subject: [PATCH 37/43] refactor method type intersection, closes #636 --- .../src/wyil/util/type/SubtypeOperator.java | 64 +++++++++++-------- tests/invalid/NegationType_Invalid_4.sysout | 3 + tests/invalid/NegationType_Invalid_4.whiley | 3 + 3 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 tests/invalid/NegationType_Invalid_4.sysout create mode 100644 tests/invalid/NegationType_Invalid_4.whiley diff --git a/modules/wyil/src/wyil/util/type/SubtypeOperator.java b/modules/wyil/src/wyil/util/type/SubtypeOperator.java index d0b6b04535..01611853dd 100644 --- a/modules/wyil/src/wyil/util/type/SubtypeOperator.java +++ b/modules/wyil/src/wyil/util/type/SubtypeOperator.java @@ -239,36 +239,50 @@ protected boolean isIntersectionInner(int fromIndex, boolean fromSign, int toInd // nary nodes int[] fromChildren = fromState.children; int[] toChildren = toState.children; - if(fromChildren.length != toChildren.length){ - return !fromSign || !toSign; - } int fromNumParams = (Integer) fromState.data; int toNumParams = (Integer) toState.data; - if(fromNumParams != toNumParams){ - return !fromSign || !toSign; - } - boolean andChildren = true; - boolean orChildren = false; - - for(int i=0;i= fromNumParams) { - // return type(s) are co-variant - v = isIntersection(fromChildren[i], fromSign, - toChildren[i], toSign); - } else { - // parameter type(s) are contra-variant - v = isIntersection(fromChildren[i], !fromSign, - toChildren[i], !toSign); + + if (fromSign && toSign) { + // Two intersecting method types must have the same number + // of parameters and returns. + if (fromChildren.length != toChildren.length || fromNumParams != toNumParams) { + return false; } - andChildren &= v; - orChildren |= v; + + // Two intersecting method types must have intersecting return types. + // Parameter types can always be chosen as "any". + for (int i = fromNumParams; i < fromChildren.length; ++i) { + if (!isIntersection(fromChildren[i], true, toChildren[i], true)) { + return false; + } + } + return true; } - if(!fromSign || !toSign) { - return orChildren; - } else { - return andChildren; + + // Now we have one of the following: + // fromSign == true: negation of "is fromType a subtype of toType?" + // toSign == true: negation of "is toType a subtype of fromType?" + + // Two method types can only be subtypes of each other if they have + // the same number of parameters and returns. + if (fromChildren.length != toChildren.length || fromNumParams != toNumParams) { + return true; // true means "not subtype" } + + // Parameter types are contra-variant + for (int i = 0; i < fromNumParams; ++i) { + if (isIntersection(fromChildren[i], !fromSign, toChildren[i], !toSign)) { + return true; + } + } + + // Return types are co-variant + for (int i = fromNumParams; i < fromChildren.length; ++i) { + if (isIntersection(fromChildren[i], fromSign, toChildren[i], toSign)) { + return true; + } + } + return false; } return true; default: diff --git a/tests/invalid/NegationType_Invalid_4.sysout b/tests/invalid/NegationType_Invalid_4.sysout new file mode 100644 index 0000000000..0170bbc966 --- /dev/null +++ b/tests/invalid/NegationType_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/NegationType_Invalid_4.whiley:3: expected type !NegationType_Invalid_4:t, found method(any)->() + !t m = &test + ^^^^^ diff --git a/tests/invalid/NegationType_Invalid_4.whiley b/tests/invalid/NegationType_Invalid_4.whiley new file mode 100644 index 0000000000..0a734d42c5 --- /dev/null +++ b/tests/invalid/NegationType_Invalid_4.whiley @@ -0,0 +1,3 @@ +type t is method(any)->() +public export method test(any x): + !t m = &test From bfb56758e6c709cf50610f3c0a03a9e32fc52d4e Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Sun, 24 Apr 2016 23:32:59 +1200 Subject: [PATCH 38/43] fix purity checker, closes #613 --- modules/wyc/src/wyc/lang/Exprs.java | 8 ++++---- tests/valid/Lambda_Valid_12.whiley | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 tests/valid/Lambda_Valid_12.whiley diff --git a/modules/wyc/src/wyc/lang/Exprs.java b/modules/wyc/src/wyc/lang/Exprs.java index 73efd43025..e867927ddf 100644 --- a/modules/wyc/src/wyc/lang/Exprs.java +++ b/modules/wyc/src/wyc/lang/Exprs.java @@ -138,7 +138,6 @@ public static boolean isPure(Expr expr, Context context) { if (expr instanceof Expr.Constant) { return true; } else if (expr instanceof Expr.LocalVariable) { - Expr.LocalVariable lv = (Expr.LocalVariable) expr; return true; } else if (expr instanceof Expr.ConstantAccess) { return true; @@ -156,8 +155,7 @@ public static boolean isPure(Expr expr, Context context) { return isPure(e.lhs, context) && isPure(e.rhs, context); } else if (expr instanceof Expr.Dereference) { - Expr.Dereference e = (Expr.Dereference) expr; - return isPure(e.src, context); + return false; } else if (expr instanceof Expr.Cast) { Expr.Cast e = (Expr.Cast) expr; @@ -185,7 +183,9 @@ public static boolean isPure(Expr expr, Context context) { } else if (expr instanceof Expr.IndirectFunctionCall) { Expr.IndirectFunctionCall e = (Expr.IndirectFunctionCall) expr; - + if (!isPure(e.src, context)) { + return false; + } for(Expr p : e.arguments) { if(!isPure(p, context)) { return false; diff --git a/tests/valid/Lambda_Valid_12.whiley b/tests/valid/Lambda_Valid_12.whiley new file mode 100644 index 0000000000..eed51c47bf --- /dev/null +++ b/tests/valid/Lambda_Valid_12.whiley @@ -0,0 +1,7 @@ +type mymethod is method()->(int) + +public export method test(): + &int x = new 3 + mymethod m = &(->(*x)) + int y = m() + assume y == 3 From 2b1dcbf929cd490230b18e342fc8e405edc1225e Mon Sep 17 00:00:00 2001 From: DavePearce Date: Mon, 25 Apr 2016 11:43:42 +1200 Subject: [PATCH 39/43] Fix for #639 This was a simple problem related to the way that VirtualRool was handling outputstreams for its contained Entry's. --- modules/wybs/src/wyfs/util/VirtualRoot.java | 26 ++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/modules/wybs/src/wyfs/util/VirtualRoot.java b/modules/wybs/src/wyfs/util/VirtualRoot.java index 9cc5b94db8..784892dd33 100644 --- a/modules/wybs/src/wyfs/util/VirtualRoot.java +++ b/modules/wybs/src/wyfs/util/VirtualRoot.java @@ -72,6 +72,11 @@ public static final class Entry extends AbstractEntry implements Path.Entr */ private byte[] data; + /** + * Number of bytes in data actually used. + */ + private int length; + /** * The last modified date. This is a time stamp used to determine when * the file was last modified in order to calculate which dependents @@ -98,22 +103,31 @@ public long lastModified() { } public InputStream inputStream() { - return new ByteArrayInputStream(data); + return new ByteArrayInputStream(data,0,length); } public OutputStream outputStream() { lastModified = System.currentTimeMillis(); data = new byte[0]; + length = 0; // create an output stream which will automatically resize the given // array. - return new OutputStream() { - private int pos = 0; - + return new OutputStream() { + public void write(byte[] bytes) { + data = new byte[bytes.length]; + System.arraycopy(bytes, 0, data, 0, bytes.length); + length = data.length; + } + public void write(byte[] bytes, int off, int len) { + data = new byte[len]; + System.arraycopy(bytes, off, data, 0, len); + length = data.length; + } public void write(int b) { - if (pos >= data.length) { + if (length >= data.length) { data = Arrays.copyOf(data, (data.length + 1) * 2); } - data[pos++] = (byte) b; + data[length++] = (byte) b; } }; } From f490c1ee64f61f846b49cbab6782a2265bf21a12 Mon Sep 17 00:00:00 2001 From: Rich Dougherty Date: Wed, 13 Apr 2016 13:40:00 +1200 Subject: [PATCH 40/43] Prune some uninhabitable types Modify the simplification algorithm so that it propagates inhabitation information through compound types like arrays, unions and negation. This allows some uninhabitable types to be reduced to void and pruned from the type. Fixes #631, fixes #406. --- .../wyc/src/wyc/testing/AllValidTests.java | 1 - modules/wyil/src/wyil/lang/Type.java | 2 +- .../src/wyil/util/type/TypeAlgorithms.java | 427 ++++++++++++++++-- .../src/wyjc/testing/RuntimeValidTests.java | 1 - tests/invalid/RecursiveType_Invalid_11.sysout | 3 + tests/invalid/RecursiveType_Invalid_11.whiley | 7 + tests/valid/Coercion_Valid_8.whiley | 12 +- 7 files changed, 395 insertions(+), 58 deletions(-) create mode 100644 tests/invalid/RecursiveType_Invalid_11.sysout create mode 100644 tests/invalid/RecursiveType_Invalid_11.whiley diff --git a/modules/wyc/src/wyc/testing/AllValidTests.java b/modules/wyc/src/wyc/testing/AllValidTests.java index 326d8368b9..a9d4ca4e3b 100644 --- a/modules/wyc/src/wyc/testing/AllValidTests.java +++ b/modules/wyc/src/wyc/testing/AllValidTests.java @@ -77,7 +77,6 @@ public class AllValidTests { public final static Map IGNORED = new HashMap(); static { - IGNORED.put("Coercion_Valid_8", "#406"); IGNORED.put("Complex_Valid_3", "Issue ???"); IGNORED.put("ConstrainedIntersection_Valid_1", "unknown"); IGNORED.put("ConstrainedNegation_Valid_1", "#342"); diff --git a/modules/wyil/src/wyil/lang/Type.java b/modules/wyil/src/wyil/lang/Type.java index 298f0b1867..9d50edb152 100755 --- a/modules/wyil/src/wyil/lang/Type.java +++ b/modules/wyil/src/wyil/lang/Type.java @@ -101,7 +101,7 @@ public static final Type.Reference Reference(Type element) { return (Type.Reference) r; } else { throw new IllegalArgumentException( - "invalid arguments for Type.Reference()"); + "invalid arguments for Type.Reference(): " + r); } } diff --git a/modules/wyil/src/wyil/util/type/TypeAlgorithms.java b/modules/wyil/src/wyil/util/type/TypeAlgorithms.java index 828d97b8bb..9b37301301 100755 --- a/modules/wyil/src/wyil/util/type/TypeAlgorithms.java +++ b/modules/wyil/src/wyil/util/type/TypeAlgorithms.java @@ -225,18 +225,251 @@ private static boolean isContractive(int index, BitSet contractives, *

* */ + /** + * Type inhabitation labels used by the simplification algorithm. + */ + private static enum Inhabitation { + /** + * Indicates that the simplification algorithm has not yet labeled a state + * with a value. + */ + UNLABELED, + /** + * Type is uninhabited. Equivalent to void. + */ + NONE, + /** + * May have some values inhabiting the type. Since we don't precisely + * track inhabitation, a type that has this label does not necessarily + * have any values, but we may be unable to prove it is uninhabited so we + * may choose to assume that it is inhabited to be safe. + */ + SOME, + /** + * Type is inhabited by all values. Equivalent to any. + */ + ALL + } + + /** + * Get the inhabitation of a state during simplification. Some states have a + * fixed, known inhabitation. Others are either UNLABELED or SOME, depending + * on the value of the corresponding bit in the set of flags. + * + * @param index + * --- index of state being worked on. + * @param automaton + * --- automaton containing state being worked on. + * @param inhabitationFlags + * --- flags tracking inhabitation for some of the states + * @return The inhabitation of the state. + */ + private static Inhabitation getStateInhabitation(int index, Automaton automaton, BitSet inhabitationFlags) { + Automaton.State state = automaton.states[index]; + switch (state.kind) { + // Some states have known, fixed inhabitation. + case Type.K_VOID: + return Inhabitation.NONE; + case Type.K_ANY: + return Inhabitation.ALL; + case Type.K_NULL: + case Type.K_BOOL: + case Type.K_BYTE: + case Type.K_CHAR: + case Type.K_INT: + case Type.K_RATIONAL: + case Type.K_STRING: + case Type.K_FUNCTION: + case Type.K_NOMINAL: + return Inhabitation.SOME; + // Other states have their inhabitation labels stored as a flag. + case Type.K_NEGATION: + case Type.K_UNION : + case Type.K_LIST: + case Type.K_REFERENCE: + case Type.K_SET: + case Type.K_RECORD: + case Type.K_TUPLE: + case Type.K_METHOD: + if (inhabitationFlags.get(index)) { + return Inhabitation.SOME; + } else { + return Inhabitation.UNLABELED; + } + default: + throw new IllegalArgumentException("Unknown kind: " + state.kind); + } + } + + /** + * Set the inhabitation of a state during simplification. This method might + * do several things: + * + * 1. If the inhabitation is set to its existing value, then nothing happens + * and false is returned. + * 2. If the inhabitation is set to NONE or ALL then the state's kind will be + * changed to K_VOID or K_ANY and true is returned. + * 3. If the inhabitation is set to SOME or UNLABELED, and the state's kind + * supports it, then a flag will be set or clared in the inhabitationFlags + * parameter. True will be returned. + * 4. If none of these actions are possible then a state is being set to + * an unsupported inhabitation label (e.g. K_INT set to K_UNLABELED) and + * an IllegalArgumentExceptionw will be thrown. + * + * @param index + * --- index of state being worked on. + * @param automaton + * --- automaton containing state being worked on. + * @param inhabitationFlags + * --- flags tracking inhabitation for some of the states + * @param newValue + * --- the new inhabitation value to set the state to + * @return Whether or not the value was changed. + */ + private static boolean setStateInhabitation(int index, Automaton automaton, BitSet inhabitationFlags, Inhabitation newValue) { + Inhabitation existingValue = getStateInhabitation(index, automaton, inhabitationFlags); + if (newValue == existingValue) { + return false; + } + if (newValue == Inhabitation.NONE) { + automaton.states[index] = new Automaton.State(Type.K_VOID); + } else if (newValue == Inhabitation.ALL) { + automaton.states[index] = new Automaton.State(Type.K_ANY); + } else if (newValue == Inhabitation.SOME || newValue == Inhabitation.UNLABELED) { + int existingKind = automaton.states[index].kind; + switch (existingKind) { + case Type.K_NEGATION: + case Type.K_UNION : + case Type.K_LIST: + case Type.K_REFERENCE: + case Type.K_SET: + case Type.K_RECORD: + case Type.K_TUPLE: + case Type.K_FUNCTION: + case Type.K_METHOD: + inhabitationFlags.set(index, newValue == Inhabitation.SOME); + break; + default: + throw new IllegalArgumentException("Cannot label state with kind " + existingKind + " with inhabitation label " + newValue); + } + } else { + throw new IllegalArgumentException("Unknown inhabitation label " + newValue); + } + return true; + } + + /** + * Analyse an automaton and try to produce a version that is simpler but + * still equivalent. + * + * @param automaton + * --- automaton to simplify. + */ public static void simplify(Automaton automaton) { - boolean changed = true; - while(changed) { - changed = false; - // NOTE: the following is commented out because it's broken. - // changed |= simplifyContractives(automaton); + // Use a BitSet to track the inhabitation label of some of the states in + // the automaton. A bit is available for each state but it is ignored for + // states whose kind has an implicit inhabitation label, e.g. K_VOID has + // a label of NONE, K_ANY has a label of ALL, K_INT has a label of SOME, + // etc, but K_RECORD needs a bit to track whether it is UNLABELED or has + // a label of SOME. + // + // A clear bit indicates a state is UNLABELED, a set bit indicates it + // has SOME inhabitants. This bit has no meaning for states that already + // have implicit inhabitation information. + BitSet lastInhabitationFlags = null; + BitSet inhabitationFlags = null; + + while (true) { + // Swap inhabitation flags. + { + BitSet temp = inhabitationFlags; + lastInhabitationFlags = inhabitationFlags; + lastInhabitationFlags = temp; + if (inhabitationFlags == null) { + inhabitationFlags = new BitSet(automaton.size()); + } else { + inhabitationFlags.clear(); + } + } + + // Don't assume any prior knowledge about inhabitation. Previous simplifications + // may have assumed states had SOME inhabitants but these could be NONE after + // further simplification, i.e. because !!SOME => SOME using our rules, but this + // is only because we're imprecise in propagating inhabitation through negation. + inhabitationFlags.clear(); + + // Perform an initial simplification on the automaton. + boolean simplified = simplifyInner(automaton, inhabitationFlags); + // Break if not simplified or inhabitaitonFlags non-null and equal + if (!simplified && lastInhabitationFlags != null && lastInhabitationFlags.equals(inhabitationFlags)) { + // No simplification and inhabitation flags unchanged: breaking out of simplification loop + break; + } + // Found simplification and/or inhabitation flags changes: continuing with simplification loop + + // Now that simplification has occurred once, all possibly inhabited states should + // have a label. Anything unlabeled can be considered uninhabited. + boolean labelingChanged = markUnlabeledAsUninhabited(automaton, inhabitationFlags); + if (!labelingChanged && lastInhabitationFlags != null) { + // No uninhabited labeling changes: breaking out of simplification loop + break; + } + // Found labeling changes: continuing with simplification loop + } + } + + /** + * Simplifies each state in an automaton until a fixpoint is reached. + * + * @param automaton + * --- automaton being simplified. + * @param inhabitationFlags + * --- flags tracking inhabitation for some of the states + */ + private static boolean simplifyInner(Automaton automaton, BitSet inhabitationFlags) { + // Flag indicating whether any simplifications have been performed by this + // method. + boolean anySimplificationPerformed = false; + // Flag indicating whether or not to loop again. This will be false when + // no simplifications are made to any state. + boolean loopAgain = true; + while (loopAgain) { + loopAgain = false; + // Try to simplify each state and track whether or not any simplifications + // have occurred. for(int i=0;i!=automaton.size();++i) { - changed |= simplify(i,automaton); + boolean stateSimplified = simplifyState(i,automaton, inhabitationFlags); + loopAgain |= stateSimplified; + anySimplificationPerformed |= stateSimplified; + } + } + return anySimplificationPerformed; + } + + /** + * This method is called after an initial simplification pass. Any states + * that have an UNLABELED inhabitation must not be inhabited, so can be set + * to NONE. + * + * @param automaton + * --- automaton being simplified. + * @param inhabitationFlags + * --- flags tracking inhabitation for some of the states + * @return True if the labels were changed by this method, false otherwise. + */ + private static boolean markUnlabeledAsUninhabited(Automaton automaton, BitSet inhabitationFlags) { + boolean labelingChanged = false; + for(int i=0;i!=automaton.size();++i) { + if (getStateInhabitation(i, automaton, inhabitationFlags) == Inhabitation.UNLABELED) { + // Mark unlabeled state as uninhabited + setStateInhabitation(i, automaton, inhabitationFlags, Inhabitation.NONE); + labelingChanged = true; } } + return labelingChanged; } + // FIXME: This method is not called from anywhere. private static boolean simplifyContractives(Automaton automaton) { BitSet contractives = new BitSet(automaton.size()); // initially all nodes are considered contractive. @@ -252,16 +485,39 @@ private static boolean simplifyContractives(Automaton automaton) { return changed; } - private static boolean simplify(int index, Automaton automaton) { + /** + * Attempts to simplify a single state in automaton. + * + * @param index + * --- the index of the state being worked on. + * @param automaton + * --- automaton being simplified. + * @param inhabitationFlags + * --- flags tracking inhabitation for some of the states + * @return True if this method modified the state, false if the state is + * still the same. + */ + private static boolean simplifyState(int index, Automaton automaton, BitSet inhabitationFlags) { Automaton.State state = automaton.states[index]; - boolean changed=false; switch (state.kind) { + case Type.K_VOID: + case Type.K_ANY: + case Type.K_NULL: + case Type.K_BOOL: + case Type.K_BYTE: + case Type.K_CHAR: + case Type.K_INT: + case Type.K_RATIONAL: + case Type.K_STRING: + case Type.K_NOMINAL: + case Type.K_FUNCTION: + return false; case Type.K_NEGATION: - changed = simplifyNegation(index, state, automaton); - break; + return simplifyNegation(index, state, automaton, inhabitationFlags); case Type.K_UNION : - changed = simplifyUnion(index, state, automaton); - break; + return simplifyUnion(index, state, automaton, inhabitationFlags); + case Type.K_REFERENCE: + return simplifyReference(index, state, automaton, inhabitationFlags); case Type.K_LIST: case Type.K_SET: // for list and set types, we want to simplify the following cases: @@ -269,46 +525,92 @@ private static boolean simplify(int index, Automaton automaton) { // {void+} => void boolean nonEmpty = (Boolean) state.data; if(!nonEmpty) { - // type of form [T], so ignore - break; + // type of form [T], so no further simplication needed + // All non-empty lists and sets contain the empty list or set + return setStateInhabitation(index, automaton, inhabitationFlags, Inhabitation.SOME); } - // type of form [T+] so simplify like other compounds. + // list/set type of form [T+] so fall through to simplify like other compounds. case Type.K_RECORD: case Type.K_TUPLE: - case Type.K_FUNCTION: case Type.K_METHOD: - changed = simplifyCompound(index, state, automaton); - break; + return simplifyCompound(index, state, automaton, inhabitationFlags); + default: + throw new IllegalArgumentException("Can't simplify state with kind: " + state.kind); } - return changed; } - private static boolean simplifyNegation(int index, Automaton.State state, Automaton automaton) { + private static boolean simplifyNegation(int index, Automaton.State state, Automaton automaton, BitSet inhabitationFlags) { + // Rewrite !!X => X Automaton.State child = automaton.states[state.children[0]]; if(child.kind == Type.K_NEGATION) { // bypass node - Automaton.State childchild = automaton.states[child.children[0]]; + int childchildIndex = child.children[0]; + Inhabitation childchildInhabitation = getStateInhabitation(childchildIndex, automaton, inhabitationFlags); + Automaton.State childchild = automaton.states[childchildIndex]; + // Replace double negation of X with just X automaton.states[index] = new Automaton.State(childchild); + setStateInhabitation(index, automaton, inhabitationFlags, childchildInhabitation); return true; } - return false; + + // Set inhabitation label based on child's label + Inhabitation childInhabitation = getStateInhabitation(state.children[0], automaton, inhabitationFlags); + Inhabitation inhabitation; + if (childInhabitation == Inhabitation.ALL) { + inhabitation = Inhabitation.NONE; // Negation of ALL is NONE + } else if (childInhabitation == Inhabitation.NONE) { + inhabitation = Inhabitation.ALL; // Negation of ALL is SOME + } else { + // Approximate negation of SOME is SOME, leave UNLABELED if child is UNLABELED + inhabitation = childInhabitation; + } + return setStateInhabitation(index, automaton, inhabitationFlags, inhabitation); } - private static boolean simplifyCompound(int index, Automaton.State state, Automaton automaton) { + private static boolean simplifyReference(int index, Automaton.State state, Automaton automaton, BitSet inhabitationFlags) { + // Look at child inhabitation to work out reference inhabitation + Inhabitation childInhabitation = getStateInhabitation(state.children[0], automaton, inhabitationFlags); + if (childInhabitation == Inhabitation.ALL || childInhabitation == Inhabitation.SOME) { + // Use SOME for the reference even if the child is ALL. This is necessary to prevent + // the constant Reference.T_REF_ANY from be normalised directly into any, losing its + // reference-ness. We could possibly use ALL if we added a way to construct a reference + // without normalising it completely. + return setStateInhabitation(index, automaton, inhabitationFlags, Inhabitation.SOME); + } else if (childInhabitation == Inhabitation.NONE) { + return setStateInhabitation(index, automaton, inhabitationFlags, Inhabitation.NONE); + } else { + return false; + } + } + + private static boolean simplifyCompound(int index, Automaton.State state, Automaton automaton, BitSet inhabitationFlags) { int kind = state.kind; int[] children = state.children; - boolean isOpenRecord = false; - if(kind == Type.K_RECORD) { - Type.Record.State data = (Type.Record.State) state.data; - isOpenRecord = data.isOpen; + + // Skip some children if the compound is a function + int numChildrenToCheck = children.length; + if (state.kind == Type.K_FUNCTION) { + // Only check function parameters for now + // TODO: Work out how to handle function return types properly + numChildrenToCheck = (Integer) state.data; } - for(int i=0;i j)) { + // Found a child that's a subtype of another child; it can be + // subsumed into that other child. subsumed = true; } } if (subsumed) { + // Remove the subsumed child. children = removeIndex(i--, children); state.children = children; changed = true; @@ -472,6 +798,11 @@ public boolean equals(Object o) { return false; } + @Override + public String toString() { + return "(" + (fromSign ? '+' : '-') + fromIndex + "&" + (toSign ? '+' : '-') + fromIndex + ")"; + } + public int hashCode() { return fromIndex + toIndex; } diff --git a/modules/wyjc/src/wyjc/testing/RuntimeValidTests.java b/modules/wyjc/src/wyjc/testing/RuntimeValidTests.java index a552785929..a46a97cb0e 100644 --- a/modules/wyjc/src/wyjc/testing/RuntimeValidTests.java +++ b/modules/wyjc/src/wyjc/testing/RuntimeValidTests.java @@ -76,7 +76,6 @@ public class RuntimeValidTests { public final static Map IGNORED = new HashMap(); static { - IGNORED.put("Coercion_Valid_8", "#406"); IGNORED.put("Complex_Valid_3", "Issue ???"); IGNORED.put("ConstrainedIntersection_Valid_1", "unknown"); IGNORED.put("ConstrainedNegation_Valid_1", "#342"); diff --git a/tests/invalid/RecursiveType_Invalid_11.sysout b/tests/invalid/RecursiveType_Invalid_11.sysout new file mode 100644 index 0000000000..c1b72c0cf6 --- /dev/null +++ b/tests/invalid/RecursiveType_Invalid_11.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/RecursiveType_Invalid_11.whiley:1: empty type encountered +type T is {T t} +^^^^^^^^^^^^^^^ diff --git a/tests/invalid/RecursiveType_Invalid_11.whiley b/tests/invalid/RecursiveType_Invalid_11.whiley new file mode 100644 index 0000000000..59656c2356 --- /dev/null +++ b/tests/invalid/RecursiveType_Invalid_11.whiley @@ -0,0 +1,7 @@ +type T is {T t} + +function f(int|T x) -> int: + return x + +public export method test() : + assume f(1) == 1 diff --git a/tests/valid/Coercion_Valid_8.whiley b/tests/valid/Coercion_Valid_8.whiley index 344a6020d9..8ceec20ebd 100644 --- a/tests/valid/Coercion_Valid_8.whiley +++ b/tests/valid/Coercion_Valid_8.whiley @@ -1,13 +1,11 @@ +type Expr is int | Expr[] - -type Expr is real | Expr[] - -function f(Expr x) -> real: +function f(Expr x) -> int: if x is Expr[]: - return (real) |x| + return |x| else: return x public export method test() : - assume f([1.0, 2.0, 3.0]) == 3.0 - assume f(1.234) == 1.234 + assume f([1, 2, 3]) == 3 + assume f(1) == 1 From 217e0c31c9a5121fa86be723ee85f4e5a28a8ae2 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Mon, 25 Apr 2016 01:41:50 +1200 Subject: [PATCH 41/43] implement lifetimes --- .../wyc/src/wyc/builder/CodeGenerator.java | 14 +- .../wyc/src/wyc/builder/FlowTypeChecker.java | 860 ++++++++++++++---- modules/wyc/src/wyc/io/WhileyFileLexer.java | 4 + modules/wyc/src/wyc/io/WhileyFileParser.java | 676 +++++++++++--- modules/wyc/src/wyc/io/WhileyFilePrinter.java | 94 +- modules/wyc/src/wyc/lang/Expr.java | 115 ++- modules/wyc/src/wyc/lang/Nominal.java | 6 +- modules/wyc/src/wyc/lang/Stmt.java | 53 ++ modules/wyc/src/wyc/lang/SyntacticType.java | 40 +- modules/wyc/src/wyc/lang/WhileyFile.java | 14 +- .../wyc/src/wyc/testing/AllInvalidTests.java | 2 +- .../testing/AllValidVerificationTests.java | 1 + modules/wyc/src/wyc/testing/TestUtils.java | 3 +- modules/wyil/src/wyil/Main.java | 3 +- modules/wyil/src/wyil/lang/Type.java | 299 ++++-- modules/wyil/src/wyil/util/TypeExpander.java | 2 +- .../wyil/util/interpreter/Interpreter.java | 11 +- .../util/type/ExplicitCoercionOperator.java | 4 +- .../src/wyil/util/type/LifetimeRelation.java | 140 +++ .../wyil/util/type/LifetimeSubstitution.java | 262 ++++++ .../src/wyil/util/type/SubtypeOperator.java | 191 +++- .../src/wyil/util/type/TypeAlgorithms.java | 9 +- .../wyil/src/wyil/util/type/TypeTester.java | 3 +- modules/wyjc/src/wyjc/Wyil2JavaBuilder.java | 3 +- modules/wyrt/src/whiley/io/File.whiley | 20 +- modules/wyrt/src/whiley/lang/Stack.whiley | 22 +- .../ConstrainedRecord_Invalid_1.whiley | 2 +- tests/invalid/DefiniteAssign_Invalid_4.whiley | 4 +- tests/invalid/FunctionRef_Invalid_5.whiley | 2 +- tests/invalid/FunctionRef_Invalid_7.sysout | 4 +- tests/invalid/FunctionRef_Invalid_7.whiley | 4 +- tests/invalid/Lifetime_Invalid_1.sysout | 3 + tests/invalid/Lifetime_Invalid_1.whiley | 2 + tests/invalid/Lifetime_Invalid_2.sysout | 3 + tests/invalid/Lifetime_Invalid_2.whiley | 4 + tests/invalid/Lifetime_Invalid_3.sysout | 3 + tests/invalid/Lifetime_Invalid_3.whiley | 5 + tests/invalid/Lifetime_Invalid_4.sysout | 3 + tests/invalid/Lifetime_Invalid_4.whiley | 5 + tests/invalid/Lifetime_Invalid_5.sysout | 6 + tests/invalid/Lifetime_Invalid_5.whiley | 16 + tests/invalid/Lifetime_Invalid_6.sysout | 5 + tests/invalid/Lifetime_Invalid_6.whiley | 12 + tests/invalid/Lifetime_Invalid_7.sysout | 5 + tests/invalid/Lifetime_Invalid_7.whiley | 16 + tests/invalid/Lifetime_Invalid_8.sysout | 5 + tests/invalid/Lifetime_Invalid_8.whiley | 10 + .../invalid/Lifetime_Lambda_Invalid_1.sysout | 3 + .../invalid/Lifetime_Lambda_Invalid_1.whiley | 4 + .../invalid/Lifetime_Lambda_Invalid_2.sysout | 3 + .../invalid/Lifetime_Lambda_Invalid_2.whiley | 4 + .../invalid/Lifetime_Lambda_Invalid_3.sysout | 3 + .../invalid/Lifetime_Lambda_Invalid_3.whiley | 4 + .../invalid/Lifetime_Lambda_Invalid_4.sysout | 3 + .../invalid/Lifetime_Lambda_Invalid_4.whiley | 4 + .../invalid/Lifetime_Lambda_Invalid_5.sysout | 3 + .../invalid/Lifetime_Lambda_Invalid_5.whiley | 7 + .../invalid/Lifetime_Lambda_Invalid_6.sysout | 3 + .../invalid/Lifetime_Lambda_Invalid_6.whiley | 7 + tests/invalid/MethodCall_Invalid_1.sysout | 4 +- tests/invalid/MethodCall_Invalid_1.whiley | 4 +- tests/invalid/MethodCall_Invalid_2.sysout | 4 +- tests/invalid/MethodCall_Invalid_2.whiley | 6 +- tests/invalid/MethodCall_Invalid_3.sysout | 4 +- tests/invalid/MethodCall_Invalid_3.whiley | 4 +- tests/invalid/Parameter_Invalid_1.sysout | 2 +- tests/invalid/Parsing_Invalid_5.sysout | 2 +- tests/invalid/Parsing_Invalid_6.sysout | 2 +- tests/invalid/ProcessAccess_Invalid_1.sysout | 4 +- tests/invalid/ProcessAccess_Invalid_1.whiley | 6 +- tests/invalid/ProcessAccess_Invalid_2.sysout | 4 +- tests/invalid/ProcessAccess_Invalid_2.whiley | 4 +- tests/invalid/ProcessAccess_Invalid_3.sysout | 2 +- tests/invalid/ProcessAccess_Invalid_3.whiley | 4 +- tests/invalid/Process_Invalid_1.sysout | 4 +- tests/invalid/Process_Invalid_1.whiley | 8 +- tests/invalid/Subtype_Invalid_6.whiley | 2 +- tests/invalid/TupleAssign_Invalid_1.whiley | 2 +- tests/invalid/TupleAssign_Invalid_2.whiley | 4 +- tests/invalid/TupleAssign_Invalid_3.whiley | 2 +- tests/invalid/TupleDefine_Invalid_2.whiley | 2 +- tests/invalid/UnionType_Invalid_7.whiley | 2 +- tests/valid/ConstrainedList_Valid_11.whiley | 4 +- tests/valid/FunctionRef_Valid_12.whiley | 2 +- tests/valid/FunctionRef_Valid_13.whiley | 6 +- tests/valid/FunctionRef_Valid_7.whiley | 4 +- tests/valid/FunctionRef_Valid_9.whiley | 6 +- tests/valid/Lambda_Valid_3.whiley | 4 +- tests/valid/Lambda_Valid_4.whiley | 6 +- tests/valid/Lifetime_Lambda_Valid_1.whiley | 5 + tests/valid/Lifetime_Lambda_Valid_2.whiley | 9 + tests/valid/Lifetime_Lambda_Valid_3.whiley | 12 + tests/valid/Lifetime_Lambda_Valid_4.whiley | 16 + tests/valid/Lifetime_Lambda_Valid_5.whiley | 6 + tests/valid/Lifetime_Lambda_Valid_6.whiley | 11 + tests/valid/Lifetime_Lambda_Valid_7.whiley | 26 + tests/valid/Lifetime_Valid_1.whiley | 45 + tests/valid/Lifetime_Valid_10.whiley | 12 + tests/valid/Lifetime_Valid_2.whiley | 22 + tests/valid/Lifetime_Valid_3.whiley | 61 ++ tests/valid/Lifetime_Valid_4.whiley | 19 + tests/valid/Lifetime_Valid_5.whiley | 34 + tests/valid/Lifetime_Valid_6.whiley | 33 + tests/valid/Lifetime_Valid_7.whiley | 45 + tests/valid/Lifetime_Valid_8.whiley | 28 + tests/valid/Lifetime_Valid_9.whiley | 14 + tests/valid/MessageRef_Valid_1.whiley | 2 +- tests/valid/MessageRef_Valid_2.whiley | 4 +- tests/valid/MessageSend_Valid_1.whiley | 2 +- tests/valid/MessageSend_Valid_2.whiley | 4 +- tests/valid/MessageSend_Valid_3.whiley | 4 +- tests/valid/MessageSend_Valid_4.whiley | 4 +- tests/valid/MessageSend_Valid_5.whiley | 10 +- tests/valid/MethodCall_Valid_4.whiley | 10 +- tests/valid/ProcessAccess_Valid_1.whiley | 8 +- tests/valid/ProcessAccess_Valid_2.whiley | 6 +- tests/valid/Process_Valid_1.whiley | 8 +- tests/valid/Process_Valid_10.whiley | 16 +- tests/valid/Process_Valid_11.whiley | 4 +- tests/valid/Process_Valid_12.whiley | 4 +- tests/valid/Process_Valid_4.whiley | 4 +- tests/valid/Process_Valid_5.whiley | 4 +- tests/valid/Process_Valid_6.whiley | 4 +- tests/valid/Process_Valid_7.whiley | 8 +- tests/valid/Process_Valid_8.whiley | 8 +- tests/valid/Process_Valid_9.whiley | 16 +- tests/valid/RecordAccess_Valid_1.whiley | 6 +- tests/valid/RecordAssign_Valid_1.whiley | 2 +- tests/valid/RecordAssign_Valid_4.whiley | 4 +- tests/valid/RecursiveType_Valid_11.whiley | 4 +- tests/valid/RecursiveType_Valid_12.whiley | 4 +- tests/valid/RecursiveType_Valid_14.whiley | 4 +- tests/valid/RecursiveType_Valid_22.whiley | 4 +- tests/valid/UnionType_Valid_18.whiley | 2 +- 134 files changed, 3081 insertions(+), 609 deletions(-) create mode 100644 modules/wyil/src/wyil/util/type/LifetimeRelation.java create mode 100644 modules/wyil/src/wyil/util/type/LifetimeSubstitution.java create mode 100644 tests/invalid/Lifetime_Invalid_1.sysout create mode 100644 tests/invalid/Lifetime_Invalid_1.whiley create mode 100644 tests/invalid/Lifetime_Invalid_2.sysout create mode 100644 tests/invalid/Lifetime_Invalid_2.whiley create mode 100644 tests/invalid/Lifetime_Invalid_3.sysout create mode 100644 tests/invalid/Lifetime_Invalid_3.whiley create mode 100644 tests/invalid/Lifetime_Invalid_4.sysout create mode 100644 tests/invalid/Lifetime_Invalid_4.whiley create mode 100644 tests/invalid/Lifetime_Invalid_5.sysout create mode 100644 tests/invalid/Lifetime_Invalid_5.whiley create mode 100644 tests/invalid/Lifetime_Invalid_6.sysout create mode 100644 tests/invalid/Lifetime_Invalid_6.whiley create mode 100644 tests/invalid/Lifetime_Invalid_7.sysout create mode 100644 tests/invalid/Lifetime_Invalid_7.whiley create mode 100644 tests/invalid/Lifetime_Invalid_8.sysout create mode 100644 tests/invalid/Lifetime_Invalid_8.whiley create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_1.sysout create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_1.whiley create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_2.sysout create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_2.whiley create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_3.sysout create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_3.whiley create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_4.sysout create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_4.whiley create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_5.sysout create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_5.whiley create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_6.sysout create mode 100644 tests/invalid/Lifetime_Lambda_Invalid_6.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_1.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_2.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_3.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_4.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_5.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_6.whiley create mode 100644 tests/valid/Lifetime_Lambda_Valid_7.whiley create mode 100644 tests/valid/Lifetime_Valid_1.whiley create mode 100644 tests/valid/Lifetime_Valid_10.whiley create mode 100644 tests/valid/Lifetime_Valid_2.whiley create mode 100644 tests/valid/Lifetime_Valid_3.whiley create mode 100644 tests/valid/Lifetime_Valid_4.whiley create mode 100644 tests/valid/Lifetime_Valid_5.whiley create mode 100644 tests/valid/Lifetime_Valid_6.whiley create mode 100644 tests/valid/Lifetime_Valid_7.whiley create mode 100644 tests/valid/Lifetime_Valid_8.whiley create mode 100644 tests/valid/Lifetime_Valid_9.whiley diff --git a/modules/wyc/src/wyc/builder/CodeGenerator.java b/modules/wyc/src/wyc/builder/CodeGenerator.java index 925bc5b384..08ff915da5 100644 --- a/modules/wyc/src/wyc/builder/CodeGenerator.java +++ b/modules/wyc/src/wyc/builder/CodeGenerator.java @@ -312,6 +312,8 @@ private void generate(Stmt stmt, EnclosingScope scope) { generate((Break) stmt, scope); } else if (stmt instanceof Continue) { generate((Continue) stmt, scope); + } else if (stmt instanceof NamedBlock) { + generate((NamedBlock) stmt, scope); } else if (stmt instanceof While) { generate((While) stmt, scope); } else if (stmt instanceof DoWhile) { @@ -974,6 +976,15 @@ private void checkNoDuplicateLabels(List cases, EnclosingScope scope) } + /** + * Translate a named block into WyIL bytecodes. + */ + private void generate(Stmt.NamedBlock s, EnclosingScope scope) { + for (Stmt st : s.body) { + generate(st, scope); + } + } + /** * Translate a while loop into WyIL bytecodes. Consider the following use of * a while statement: @@ -1675,7 +1686,8 @@ private Type.FunctionOrMethod determineLambdaParametersAndOperands(Expr.Lambda e if(lambdaType instanceof Nominal.Function) { return Type.Function(rawLambdaType.returns(),rawParamTypes); } else { - return Type.Method(rawLambdaType.returns(),rawParamTypes); + return Type.Method(rawLambdaType.returns(),rawLambdaType.contextLifetimes(), + rawLambdaType.lifetimeParams(),rawParamTypes); } } diff --git a/modules/wyc/src/wyc/builder/FlowTypeChecker.java b/modules/wyc/src/wyc/builder/FlowTypeChecker.java index d7f420c644..c3e77d7c7f 100644 --- a/modules/wyc/src/wyc/builder/FlowTypeChecker.java +++ b/modules/wyc/src/wyc/builder/FlowTypeChecker.java @@ -54,6 +54,8 @@ import wyil.lang.Type; import wyil.lang.WyilFile; import wyil.util.TypeExpander; +import wyil.util.type.LifetimeRelation; +import wyil.util.type.LifetimeSubstitution; /** * Propagates type information in a flow-sensitive fashion from declared @@ -246,7 +248,8 @@ public void propagate(WhileyFile.Constant cd) throws IOException, ResolveError { public void propagate(WhileyFile.FunctionOrMethod d) throws IOException { // Resolve the types of all parameters and construct an appropriate // environment for use in the flow-sensitive type propagation. - Environment environment = addDeclaredParameters(d.parameters, new Environment(), d); + Environment environment = new Environment().declareLifetimeParameters(d.lifetimeParameters); + environment = addDeclaredParameters(d.parameters, environment, d); environment = addDeclaredParameters(d.returns, environment, d); // Resolve types for any preconditions (i.e. requires clauses) provided. propagateConditions(d.requires, environment, d); @@ -262,6 +265,9 @@ public void propagate(WhileyFile.FunctionOrMethod d) throws IOException { m.resolvedType = resolveAsType(m.unresolvedType(), d); } + // Add the "this" lifetime + environment = environment.startNamedBlock("this"); + // Finally, propagate type information throughout all statements in the // function / method body. Environment last = propagate(d.statements, environment, d); @@ -362,6 +368,8 @@ private Environment propagate(Stmt stmt, Environment environment, Context contex return propagate((Stmt.Return) stmt, environment, context); } else if (stmt instanceof Stmt.IfElse) { return propagate((Stmt.IfElse) stmt, environment, context); + } else if (stmt instanceof Stmt.NamedBlock) { + return propagate((Stmt.NamedBlock) stmt, environment, context); } else if (stmt instanceof Stmt.While) { return propagate((Stmt.While) stmt, environment, context); } else if (stmt instanceof Stmt.Switch) { @@ -410,7 +418,7 @@ private Environment propagate(Stmt stmt, Environment environment, Context contex */ private Environment propagate(Stmt.Assert stmt, Environment environment, Context context) { stmt.expr = propagate(stmt.expr, environment, context); - checkIsSubtype(Type.T_BOOL, stmt.expr); + checkIsSubtype(Type.T_BOOL, stmt.expr, environment); return environment; } @@ -427,7 +435,7 @@ private Environment propagate(Stmt.Assert stmt, Environment environment, Context */ private Environment propagate(Stmt.Assume stmt, Environment environment, Context context) { stmt.expr = propagate(stmt.expr, environment, context); - checkIsSubtype(Type.T_BOOL, stmt.expr); + checkIsSubtype(Type.T_BOOL, stmt.expr, environment); return environment; } @@ -472,7 +480,7 @@ private Environment propagate(Stmt.VariableDeclaration stmt, Environment environ // to the newly declared variable. if (stmt.expr != null) { stmt.expr = propagate(stmt.expr, environment, context); - checkIsSubtype(stmt.type, stmt.expr); + checkIsSubtype(stmt.type, stmt.expr, environment); } // Third, update environment accordingly. Observe that we can safely @@ -527,15 +535,15 @@ private Environment propagate(Stmt.Assign stmt, Environment environment, Context for (int i = 0; i != valuesProduced.size(); ++i) { Expr.LVal lval = stmt.lvals.get(i); Nominal rval = valuesProduced.get(i).second(); - Expr.AssignedVariable av = inferAfterType(lval, rval); - checkIsSubtype(environment.getDeclaredType(av.var), av.afterType, av); + Expr.AssignedVariable av = inferAfterType(lval, rval, environment); + checkIsSubtype(environment.getDeclaredType(av.var), av.afterType, av, environment); environment = environment.update(av.var, av.afterType); } return environment; } - private Expr.AssignedVariable inferAfterType(Expr.LVal lv, Nominal afterType) { + private Expr.AssignedVariable inferAfterType(Expr.LVal lv, Nominal afterType, Environment environment) { if (lv instanceof Expr.AssignedVariable) { Expr.AssignedVariable v = (Expr.AssignedVariable) lv; v.afterType = afterType; @@ -544,20 +552,20 @@ private Expr.AssignedVariable inferAfterType(Expr.LVal lv, Nominal afterType) { Expr.Dereference pa = (Expr.Dereference) lv; // The before and after types are the same since an assignment // through a reference does not change its type. - checkIsSubtype(pa.srcType.element(), afterType, lv); - return inferAfterType((Expr.LVal) pa.src, pa.srcType); + checkIsSubtype(pa.srcType.element(), afterType, lv, environment); + return inferAfterType((Expr.LVal) pa.src, pa.srcType, environment); } else if (lv instanceof Expr.IndexOf) { Expr.IndexOf la = (Expr.IndexOf) lv; Nominal.Array srcType = la.srcType; afterType = (Nominal) srcType.update(la.index.result(), afterType); - return inferAfterType((Expr.LVal) la.src, afterType); + return inferAfterType((Expr.LVal) la.src, afterType, environment); } else if (lv instanceof Expr.FieldAccess) { Expr.FieldAccess la = (Expr.FieldAccess) lv; Nominal.Record srcType = la.srcType; // I know I can modify this hash map, since it's created fresh // in Nominal.Record.fields(). afterType = (Nominal) srcType.update(la.name, afterType); - return inferAfterType((Expr.LVal) la.src, afterType); + return inferAfterType((Expr.LVal) la.src, afterType, environment); } else { internalFailure("unknown lval: " + lv.getClass().getName(), filename, lv); return null; // deadcode @@ -611,7 +619,7 @@ private Environment propagate(Stmt.Continue stmt, Environment environment, Conte */ private Environment propagate(Stmt.Debug stmt, Environment environment, Context context) { stmt.expr = propagate(stmt.expr, environment, context); - checkIsSubtype(Type.Array(Type.T_INT, false), stmt.expr); + checkIsSubtype(Type.Array(Type.T_INT, false), stmt.expr, environment); return environment; } @@ -636,7 +644,7 @@ private Environment propagate(Stmt.DoWhile stmt, Environment environment, Contex Expr invariant = stmt_invariants.get(i); invariant = propagate(invariant, environment, context); stmt_invariants.set(i, invariant); - checkIsSubtype(Type.T_BOOL, invariant); + checkIsSubtype(Type.T_BOOL, invariant, environment); } // Type condition assuming its false to represent the terminated loop. @@ -756,7 +764,7 @@ private Environment propagate(Stmt.Return stmt, Environment environment, Context for(int i=0;i!=current_returns.size();++i) { Pair p = stmt_types.get(i); Nominal t = current_returns.get(i); - checkIsSubtype(t, p.second(), p.first()); + checkIsSubtype(t, p.second(), p.first(), environment); } environment.free(); @@ -873,6 +881,22 @@ private Environment propagate(Stmt.Switch stmt, Environment environment, Context return finalEnv; } + /** + * Type check a NamedBlock statement. + * + * @param stmt + * Statement to type check + * @param environment + * Determines the type of all variables immediately going into + * this block + * @return + */ + private Environment propagate(Stmt.NamedBlock stmt, Environment environment, Context context) { + environment = environment.startNamedBlock(stmt.name); + environment = propagate(stmt.body, environment, context); + return environment.endNamedBlock(stmt.name); + } + /** * Type check a whiley statement. * @@ -894,7 +918,7 @@ private Environment propagate(Stmt.While stmt, Environment environment, Context Expr invariant = stmt_invariants.get(i); invariant = propagate(invariant, environment, context); stmt_invariants.set(i, invariant); - checkIsSubtype(Type.T_BOOL, invariant); + checkIsSubtype(Type.T_BOOL, invariant, environment); } // Type condition assuming its false to represent the terminated loop. @@ -1020,7 +1044,7 @@ public Pair propagateCondition(Expr expr, boolean sign, Envir // For non-compound forms, can just default back to the base rules // for general expressions. expr = propagate(expr, environment, context); - checkIsSubtype(Type.T_BOOL, expr, context); + checkIsSubtype(Type.T_BOOL, expr, context, environment); return new Pair(expr, environment); } } @@ -1054,7 +1078,7 @@ private Pair propagateCondition(Expr.UnOp expr, boolean sign, if (uop.op == Expr.UOp.NOT) { Pair p = propagateCondition(uop.mhs, !sign, environment, context); uop.mhs = p.first(); - checkIsSubtype(Type.T_BOOL, uop.mhs, context); + checkIsSubtype(Type.T_BOOL, uop.mhs, context, environment); uop.type = Nominal.T_BOOL; return new Pair(uop, p.second()); } else { @@ -1173,8 +1197,8 @@ private Pair resolveNonLeafCondition(Expr.BinOp bop, boolean environment = local.merge(local.keySet(), p.second()); } - checkIsSubtype(Type.T_BOOL, bop.lhs, context); - checkIsSubtype(Type.T_BOOL, bop.rhs, context); + checkIsSubtype(Type.T_BOOL, bop.lhs, context, environment); + checkIsSubtype(Type.T_BOOL, bop.rhs, context, environment); bop.srcType = Nominal.T_BOOL; return new Pair(bop, environment); @@ -1273,7 +1297,7 @@ private Pair resolveLeafCondition(Expr.BinOp bop, boolean sig // we don't know anything about the rhs. It may be possible // to support bounds here in order to do that, but frankly // that's future work :) - checkIsSubtype(Type.T_META, rhs, context); + checkIsSubtype(Type.T_META, rhs, context, environment); } bop.srcType = lhs.result(); @@ -1282,8 +1306,8 @@ private Pair resolveLeafCondition(Expr.BinOp bop, boolean sig case LTEQ: case GTEQ: case GT: - checkSuptypes(lhs, context, Nominal.T_INT); - checkSuptypes(rhs, context, Nominal.T_INT); + checkSuptypes(lhs, context, environment, Nominal.T_INT); + checkSuptypes(rhs, context, environment, Nominal.T_INT); // if (!lhsRawType.equals(rhsRawType)) { syntaxError(errorMessage(INCOMPARABLE_OPERANDS, lhsRawType, rhsRawType), filename, bop); @@ -1318,9 +1342,9 @@ private Pair resolveLeafCondition(Expr.BinOp bop, boolean sig environment = environment.update(lv.var, newType); } else { // handle general case - if (Type.isSubtype(lhsRawType, rhsRawType)) { + if (Type.isSubtype(lhsRawType, rhsRawType, environment.getLifetimeRelation())) { bop.srcType = lhs.result(); - } else if (Type.isSubtype(rhsRawType, lhsRawType)) { + } else if (Type.isSubtype(rhsRawType, lhsRawType, environment.getLifetimeRelation())) { bop.srcType = rhs.result(); } else { syntaxError(errorMessage(INCOMPARABLE_OPERANDS, lhsRawType, rhsRawType), context, bop); @@ -1444,30 +1468,30 @@ private Expr propagate(Expr.BinOp expr, Environment environment, Context context case BITWISEAND: case BITWISEOR: case BITWISEXOR: - checkIsSubtype(Type.T_BYTE, lhs, context); - checkIsSubtype(Type.T_BYTE, rhs, context); + checkIsSubtype(Type.T_BYTE, lhs, context, environment); + checkIsSubtype(Type.T_BYTE, rhs, context, environment); srcType = Type.T_BYTE; break; case LEFTSHIFT: case RIGHTSHIFT: - checkIsSubtype(Type.T_BYTE, lhs, context); - checkIsSubtype(Type.T_INT, rhs, context); + checkIsSubtype(Type.T_BYTE, lhs, context, environment); + checkIsSubtype(Type.T_INT, rhs, context, environment); srcType = Type.T_BYTE; break; case RANGE: - checkIsSubtype(Type.T_INT, lhs, context); - checkIsSubtype(Type.T_INT, rhs, context); + checkIsSubtype(Type.T_INT, lhs, context, environment); + checkIsSubtype(Type.T_INT, rhs, context, environment); srcType = Type.Array(Type.T_INT, false); break; case REM: - checkIsSubtype(Type.T_INT, lhs, context); - checkIsSubtype(Type.T_INT, rhs, context); + checkIsSubtype(Type.T_INT, lhs, context, environment); + checkIsSubtype(Type.T_INT, rhs, context, environment); srcType = Type.T_INT; break; default: // all other operations go through here - checkSuptypes(lhs, context, Nominal.T_INT); - checkSuptypes(rhs, context, Nominal.T_INT); + checkSuptypes(lhs, context, environment, Nominal.T_INT); + checkSuptypes(rhs, context, environment, Nominal.T_INT); // if (!lhsRawType.equals(rhsRawType)) { syntaxError(errorMessage(INCOMPARABLE_OPERANDS, lhsRawType, rhsRawType), filename, expr); @@ -1495,10 +1519,10 @@ private Expr propagate(Expr.UnOp expr, Environment environment, Context context) switch (expr.op) { case NEG: - checkSuptypes(src, context, Nominal.T_INT); + checkSuptypes(src, context, environment, Nominal.T_INT); break; case INVERT: - checkIsSubtype(Type.T_BYTE, src, context); + checkIsSubtype(Type.T_BYTE, src, context, environment); break; case ARRAYLENGTH: { expr.type = expandAsEffectiveArray(expr.mhs, context); @@ -1523,7 +1547,7 @@ private Expr propagate(Expr.Quantifier expr, Environment environment, Context co Expr start = propagate(p.second(), local, context); Expr end = propagate(p.third(), local, context); sources.set(i, new Triple(p.first(), start, end)); - checkIsSubtype(Type.T_INT, start, context); + checkIsSubtype(Type.T_INT, start, context, environment); local = local.declare(p.first(), Nominal.T_INT, Nominal.T_INT); } @@ -1547,7 +1571,7 @@ private Expr propagate(Expr.Cast c, Environment environment, Context context) th c.type = resolveAsType(c.unresolvedType, context); Type from = c.expr.result().raw(); Type to = c.type.raw(); - if (!Type.isExplicitCoerciveSubtype(to, from)) { + if (!Type.isExplicitCoerciveSubtype(to, from, environment.getLifetimeRelation())) { syntaxError(errorMessage(SUBTYPE_ERROR, to, from), context, c); } return c; @@ -1560,7 +1584,7 @@ private Expr propagate(Expr.AbstractFunctionOrMethod expr, Environment environme return expr; } - Pair p; + Triple> p; if (expr.paramTypes != null) { ArrayList paramTypes = new ArrayList(); @@ -1568,18 +1592,19 @@ private Expr propagate(Expr.AbstractFunctionOrMethod expr, Environment environme paramTypes.add(resolveAsType(t, context)); } // FIXME: clearly a bug here in the case of message reference - p = (Pair) resolveAsFunctionOrMethod(expr.name, paramTypes, context); + p = (Triple>) resolveAsFunctionOrMethod(expr.name, paramTypes, + expr.lifetimeParameters, context, environment); } else { - p = resolveAsFunctionOrMethod(expr.name, context); + p = resolveAsFunctionOrMethod(expr.name, context, environment); } - expr = new Expr.FunctionOrMethod(p.first(), expr.paramTypes, expr.attributes()); + expr = new Expr.FunctionOrMethod(p.first(), expr.paramTypes, p.third, expr.attributes()); expr.type = p.second(); return expr; } private Expr propagate(Expr.Lambda expr, Environment environment, Context context) throws IOException { - environment = environment.clone(); + environment = environment.startLambda(expr.contextLifetimes, expr.lifetimeParameters); Type.FunctionOrMethod rawResultType; Type.FunctionOrMethod nomResultType; ArrayList rawParameterTypes = new ArrayList(); @@ -1618,8 +1643,8 @@ private Expr propagate(Expr.Lambda expr, Environment environment, Context contex rawResultType = Type.Function(rawReturnTypes, rawParameterTypes); nomResultType = Type.Function(nomReturnTypes, nomParameterTypes); } else { - rawResultType = Type.Method(rawReturnTypes, rawParameterTypes); - nomResultType = Type.Method(nomReturnTypes, nomParameterTypes); + rawResultType = Type.Method(rawReturnTypes, expr.contextLifetimes, expr.lifetimeParameters, rawParameterTypes); + nomResultType = Type.Method(nomReturnTypes, expr.contextLifetimes, expr.lifetimeParameters, nomParameterTypes); } expr.type = (Nominal.FunctionOrMethod) Nominal.construct(nomResultType, rawResultType); @@ -1629,25 +1654,96 @@ private Expr propagate(Expr.Lambda expr, Environment environment, Context contex private Expr propagate(Expr.AbstractIndirectInvoke expr, Environment environment, Context context) throws IOException, ResolveError { + // We can only invoke functions and methods expr.src = propagate(expr.src, environment, context); Nominal.FunctionOrMethod funType = expandAsFunctionOrMethod(expr.src.result()); if (funType == null) { syntaxError("function or method type expected", context, expr.src); } + // Do we have matching argument count? List paramTypes = funType.params(); ArrayList exprArgs = expr.arguments; - if (paramTypes.size() != exprArgs.size()) { syntaxError("insufficient arguments for function or method invocation", context, expr.src); } + // resolve through arguments + ArrayList argTypes = new ArrayList(); for (int i = 0; i != exprArgs.size(); ++i) { - Nominal pt = paramTypes.get(i); Expr arg = propagate(exprArgs.get(i), environment, context); - checkIsSubtype(pt, arg, context); exprArgs.set(i, arg); + argTypes.add(arg.result()); + } + // Handle lifetime arguments + List lifetimeParameters = funType.raw().lifetimeParams(); + List lifetimeArguments = expr.lifetimeArguments; + if (lifetimeArguments == null) { + // First consider the case where no lifetime arguments are specified. + if (lifetimeParameters.isEmpty()) { + // No lifetime arguments needed! + lifetimeArguments = Collections.emptyList(); + } else { + // We have to guess proper lifetime arguments. + List rawArgTypes = stripNominal(argTypes); + List validCandidates = new ArrayList(); + guessLifetimeArguments( + extractLifetimesFromArguments(rawArgTypes), + lifetimeParameters, + funType.raw().params(), + rawArgTypes, + null, // don't need a name id + funType, + validCandidates, + environment); + + if (validCandidates.isEmpty()) { + // We were not able to guess lifetime arguments + syntaxError("no lifetime arguments specified and unable to infer them", context, expr.src); + } + if (validCandidates.size() == 1) { + // All right, we found proper lifetime arguments. + // Note that at this point we indeed have a method + // (not a function), because functions don't have + // lifetime parameters. + Expr.IndirectMethodCall imc = new Expr.IndirectMethodCall( + expr.src, exprArgs, + validCandidates.get(0).lifetimeArguments, + expr.attributes()); + imc.methodType = (Nominal.Method) funType; + return imc; + } + + // Arriving here means we have more than one possible solution. + // That is an ambiguity error, but we're nice and also print all + // solutions. + StringBuilder msg = new StringBuilder( + "no lifetime arguments specified and unable to infer a unique solution"); + List solutions = new ArrayList(validCandidates.size()); + for (ValidCandidate vc : validCandidates) { + solutions.add(vc.lifetimeArguments.toString()); + } + Collections.sort(solutions); // make error message deterministic! + for (String s : solutions) { + msg.append("\nfound solution: "); + msg.append(s); + } + syntaxError(msg.toString(), context, expr.src); + } + } + if (lifetimeParameters.size() != lifetimeArguments.size()) { + // Lifetime arguments specified, but number doesn't match + syntaxError("insufficient lifetime arguments for method invocation", context, expr.src); + } + + // Check argument types with respect to specified lifetime arguments + Map substitution = buildSubstitution(lifetimeParameters, lifetimeArguments); + for (int i = 0; i != exprArgs.size(); ++i) { + Nominal pt = paramTypes.get(i); + Expr arg = propagate(exprArgs.get(i), environment, context); + checkIsSubtype(applySubstitution(substitution, pt), arg, context, environment); + exprArgs.set(i, arg); } if (funType instanceof Nominal.Function) { @@ -1655,11 +1751,10 @@ private Expr propagate(Expr.AbstractIndirectInvoke expr, Environment environment ifc.functionType = (Nominal.Function) funType; return ifc; } else { - Expr.IndirectMethodCall imc = new Expr.IndirectMethodCall(expr.src, exprArgs, expr.attributes()); + Expr.IndirectMethodCall imc = new Expr.IndirectMethodCall(expr.src, exprArgs, lifetimeArguments, expr.attributes()); imc.methodType = (Nominal.Method) funType; return imc; } - } private Expr propagate(Expr.AbstractInvoke expr, Environment environment, Context context) @@ -1669,6 +1764,7 @@ private Expr propagate(Expr.AbstractInvoke expr, Environment environment, Contex Path.ID qualification = expr.qualification; ArrayList exprArgs = expr.arguments; + ArrayList lifetimeArgs = expr.lifetimeArguments; ArrayList paramTypes = new ArrayList(); for (int i = 0; i != exprArgs.size(); ++i) { Expr arg = propagate(exprArgs.get(i), environment, context); @@ -1689,14 +1785,14 @@ private Expr propagate(Expr.AbstractInvoke expr, Environment environment, Contex // third, lookup the appropriate function or method based on the name // and given parameter types. - Nominal.FunctionOrMethod funType = resolveAsFunctionOrMethod(name, paramTypes, context); - if (funType instanceof Nominal.Function) { + Triple> triple = resolveAsFunctionOrMethod(name, paramTypes, lifetimeArgs, context, environment); + if (triple.second() instanceof Nominal.Function) { Expr.FunctionCall r = new Expr.FunctionCall(name, qualification, exprArgs, expr.attributes()); - r.functionType = (Nominal.Function) funType; + r.functionType = (Nominal.Function) triple.second(); return r; } else { - Expr.MethodCall r = new Expr.MethodCall(name, qualification, exprArgs, expr.attributes()); - r.methodType = (Nominal.Method) funType; + Expr.MethodCall r = new Expr.MethodCall(name, qualification, exprArgs, triple.third(), expr.attributes()); + r.methodType = (Nominal.Method) triple.second(); return r; } } @@ -1713,7 +1809,7 @@ private Expr propagate(Expr.IndexOf expr, Environment environment, Context conte expr.srcType = srcType; } - checkIsSubtype(srcType.key(), expr.index, context); + checkIsSubtype(srcType.key(), expr.index, context, environment); return expr; } @@ -1744,7 +1840,7 @@ private Expr propagate(Expr.ArrayGenerator expr, Environment environment, Contex expr.element = propagate(expr.element, environment, context); expr.count = propagate(expr.count, environment, context); expr.type = Nominal.Array(expr.element.result(), true); - checkIsSubtype(Type.T_INT, expr.count); + checkIsSubtype(Type.T_INT, expr.count, environment); return expr; } @@ -1813,13 +1909,17 @@ private Expr propagate(Expr.Dereference expr, Environment environment, Context c if (srcType == null) { syntaxError("invalid reference expression", context, src); } + String lifetime = srcType.raw().lifetime(); + if (!environment.canDereferenceLifetime(lifetime)) { + syntaxError("lifetime '" + lifetime + "' cannot be dereferenced here", context, expr); + } expr.srcType = srcType; return expr; } private Expr propagate(Expr.New expr, Environment environment, Context context) { expr.expr = propagate(expr.expr, environment, context); - expr.type = Nominal.Reference(expr.expr.result()); + expr.type = Nominal.Reference(expr.expr.result(), expr.lifetime); return expr; } @@ -1954,13 +2054,17 @@ private Environment computeFixedPoint(Environment environment, ArrayList b * * @param nid * @param parameters - * @return + * @param lifetimeArgs + * --- lifetime arguments passed on method invocation, + * or null if none are passed and the compiler has to figure it out + * @return nameid, type, given/inferred lifetime arguments * @throws IOException */ - public Nominal.FunctionOrMethod resolveAsFunctionOrMethod(NameID nid, List parameters, Context context) + public Triple> resolveAsFunctionOrMethod(NameID nid, + List parameters, List lifetimeArgs, Context context, Environment environment) throws IOException, ResolveError { - // Thet set of candidate names and types for this function or method. + // The set of candidate names and types for this function or method. HashSet> candidates = new HashSet>(); // First, add all valid candidates to the list without considering which @@ -1969,7 +2073,7 @@ public Nominal.FunctionOrMethod resolveAsFunctionOrMethod(NameID nid, List resolveAsFunctionOrMethod(String name, Context context) + public Triple> resolveAsFunctionOrMethod(String name, + Context context, Environment environment) throws IOException, ResolveError { - return resolveAsFunctionOrMethod(name, null, context); + return resolveAsFunctionOrMethod(name, null, null, context, environment); } /** @@ -1999,13 +2104,16 @@ public Pair resolveAsFunctionOrMethod(String n * --- name of function or method whose type to determine. * @param parameters * --- required parameter types for the function or method. + * @param lifetimeArgs + * --- lifetime arguments passed on method invocation, + * or null if none are passed and the compiler has to figure it out * @param context * --- context in which to resolve this name. - * @return + * @return nameid, type, given/inferred lifetime arguments * @throws IOException */ - public Pair resolveAsFunctionOrMethod(String name, List parameters, - Context context) throws IOException, ResolveError { + public Triple> resolveAsFunctionOrMethod(String name, List parameters, + List lifetimeArgs, Context context, Environment environment) throws IOException, ResolveError { HashSet> candidates = new HashSet>(); // first, try to find the matching message @@ -2026,35 +2134,22 @@ public Pair resolveAsFunctionOrMethod(String n } } - return selectCandidateFunctionOrMethod(name, parameters, candidates, context); + return selectCandidateFunctionOrMethod(name, parameters, lifetimeArgs, candidates, context, environment); } - private boolean paramSubtypes(Type.FunctionOrMethod f1, Type.FunctionOrMethod f2) { - List f1_params = f1.params(); - List f2_params = f2.params(); - if (f1_params.size() == f2_params.size()) { - for (int i = 0; i != f1_params.size(); ++i) { - Type f1_param = f1_params.get(i); - Type f2_param = f2_params.get(i); - if (!Type.isSubtype(f1_param, f2_param)) { - return false; - } - } - - return true; - } - return false; - } - - private boolean paramStrictSubtypes(Type.FunctionOrMethod f1, Type.FunctionOrMethod f2) { - List f1_params = f1.params(); - List f2_params = f2.params(); + /** + * @param f1_params + * @param f2_params + * @param environment + * @return whether f2_params are strict subtypes of f1_params + */ + private boolean paramStrictSubtypes(List f1_params, List f2_params, Environment environment) { if (f1_params.size() == f2_params.size()) { boolean allEqual = true; for (int i = 0; i != f1_params.size(); ++i) { Type f1_param = f1_params.get(i); Type f2_param = f2_params.get(i); - if (!Type.isSubtype(f1_param, f2_param)) { + if (!Type.isSubtype(f1_param, f2_param, environment.getLifetimeRelation())) { return false; } allEqual &= f1_param.equals(f2_param); @@ -2085,88 +2180,343 @@ private String parameterString(List paramTypes) { return paramStr + ")"; } - private Pair selectCandidateFunctionOrMethod(String name, - List parameters, Collection> candidates, Context context) - throws IOException, ResolveError { + private String foundCandidatesString(Collection> candidates) { + ArrayList candidateStrings = new ArrayList(); + for (Pair c : candidates) { + candidateStrings.add(c.first() + " : " + c.second().nominal()); + } + Collections.sort(candidateStrings); // make error message deterministic! + StringBuilder msg = new StringBuilder(); + for (String s : candidateStrings) { + msg.append("\n\tfound: "); + msg.append(s); + } + return msg.toString(); + } - List rawParameters; - Type.Function target; + /** + * Extract all lifetime names from the types in the given list. + * + * We just walk through the type automata and collect the lifetime for each + * encountered reference. + * + * The result set will always contain the default lifetime "*". + * + * @param types + * the types to get the lifetimes from + * @return a set of all extracted lifetime names, without "*" + */ + private List extractLifetimesFromArguments(Iterable types) { + Set result = new HashSet(); + for (Type t : types) { + Automaton automaton = Type.destruct(t); + for (Automaton.State s : automaton.states) { + if (s.kind == Type.K_REFERENCE) { + String lifetime = (String) s.data; + if (!lifetime.equals("*")) { + result.add(lifetime); + } + } + } + } + result.add("*"); + return new ArrayList(result); + } - if (parameters != null) { - // FIXME: this seems to be fundamentally broken in that the number - // of expected return values is completely unknown. - ArrayList rawReturns = new ArrayList(); - rawReturns.add(Type.T_ANY); - rawParameters = stripNominal(parameters); - target = (Type.Function) Type.Function(rawReturns, rawParameters); - } else { - rawParameters = null; - target = null; + /** + * Container for a function/method candidate during method resolution. + */ + private static class ValidCandidate { + private final NameID id; + private final Nominal.FunctionOrMethod type; + + // Either given (lifetimeArgs) or inferred + private final List lifetimeArguments; + + // Lifetime parameters substituted with (inferred) arguments + private final List parameterTypesSubstituted; + + private ValidCandidate(NameID id, Nominal.FunctionOrMethod type, List lifetimeArguments, List parameterTypesSubstituted) { + this.id = id; + this.type = type; + this.lifetimeArguments = lifetimeArguments; + this.parameterTypesSubstituted = parameterTypesSubstituted; } + } - NameID candidateID = null; - Nominal.FunctionOrMethod candidateType = null; - for (Pair p : candidates) { - Nominal.FunctionOrMethod nft = p.second(); - Type.FunctionOrMethod ft = nft.raw(); - if (parameters == null || paramSubtypes(ft, target)) { - // this is now a genuine candidate - if (candidateType == null || paramStrictSubtypes(candidateType.raw(), ft)) { - candidateType = nft; - candidateID = p.first(); - } else if (!paramStrictSubtypes(ft, candidateType.raw())) { - // this is an ambiguous error - StringBuilder msg = new StringBuilder(name + parameterString(parameters) + " is ambiguous"); - // FIXME: should report all ambiguous matches here - List candidateStrings = new ArrayList(); - candidateStrings.add(candidateID + " : " + candidateType.nominal()); - candidateStrings.add(p.first() + " : " + p.second().nominal()); - Collections.sort(candidateStrings);// make error message deterministic! - for (String s : candidateStrings) { - msg.append("\n\tfound: "); - msg.append(s); + /** + * Highly optimized method to validate a function/method candidate. + * + * @param candidateId + * @param candidateType + * @param candidateParameterTypes + * @param targetParameterTypes + * @param lifetimeParameters + * @param lifetimeArguments + * @param environment + * @return + */ + private static ValidCandidate validateCandidate(NameID candidateId, Nominal.FunctionOrMethod candidateType, + List candidateParameterTypes, List targetParameterTypes, + List lifetimeParameters, List lifetimeArguments, Environment environment) { + if (!lifetimeParameters.isEmpty()) { + // Here we *might* need a substitution + Map substitution = buildSubstitution(lifetimeParameters, lifetimeArguments); + if (!substitution.isEmpty()) { + // OK, substitution is necessary. + Iterator itC = candidateParameterTypes.iterator(); + Iterator itT = targetParameterTypes.iterator(); + List parameterTypesSubstituted = new ArrayList(candidateParameterTypes.size()); + while (itC.hasNext()) { + Type c = itC.next(); + Type t = itT.next(); + c = applySubstitution(substitution, c); + if (!Type.isSubtype(c, t, environment.getLifetimeRelation())) { + return null; } - throw new ResolveError(msg.toString()); + parameterTypesSubstituted.add(c); } + return new ValidCandidate(candidateId, candidateType, lifetimeArguments, parameterTypesSubstituted); + } + } + + // No substitution necessary, just do the check + Iterator itC = candidateParameterTypes.iterator(); + Iterator itT = targetParameterTypes.iterator(); + while (itC.hasNext()) { + Type c = itC.next(); + Type t = itT.next(); + if (!Type.isSubtype(c, t, environment.getLifetimeRelation())) { + return null; } } + return new ValidCandidate(candidateId, candidateType, Collections. emptyList(), candidateParameterTypes); + } - if (candidateType == null) { - // second, didn't find matching message so generate error message - String msg = "no match for " + name + parameterString(parameters); + private Triple> selectCandidateFunctionOrMethod(String name, + List parameters, List lifetimeArgs, + Collection> candidates, + Context context, Environment environment) + throws IOException, ResolveError { - for (Pair p : candidates) { - msg += "\n\tfound: " + p.first() + " : " + p.second().nominal(); + // We cannot do anything here without candidates + if (candidates.isEmpty()) { + throw new ResolveError("no match for " + name + parameterString(parameters)); + } + + // If we don't match parameters, then we don't need to bother about + // lifetimes. Handle it separately to avoid null checks in further + // logic. + if (parameters == null) { + if (candidates.size() == 1) { + Pair p = candidates.iterator().next(); + return new Triple<>(p.first(), p.second(), null); } - throw new ResolveError(msg); - } else { + // More than one candidate and all will match. Clearly ambiguous! + throw new ResolveError(name + parameterString(parameters) + " is ambiguous" + + foundCandidatesString(candidates)); + } + + // We chose a method based only on the parameter types, as return + // type(s) are unknown. + List targetParameterTypes = stripNominal(parameters); + + // In case we don't get lifetime arguments, we have to pick a possible + // substitution by guessing. To do so, we need all lifetime names that + // occur in the passed argument types. We will cache it here once we + // compute it (only compute it if needed. + List lifetimesUsedInArguments = null; + + // Check each candidate to see if it is valid. + List validCandidates = new LinkedList(); + for (Pair p : candidates) { + Nominal.FunctionOrMethod candidateType = p.second(); + List candidateParameterTypes = candidateType.raw().params(); + + // We need a matching parameter count + if (candidateParameterTypes.size() != targetParameterTypes.size()) { + continue; + } + + // If we got lifetime arguments: Lifetime parameter count must match + List candidateLifetimeParams = candidateType.raw().lifetimeParams(); + if (lifetimeArgs != null && candidateLifetimeParams.size() != lifetimeArgs.size()) { + continue; + } + + if (candidateLifetimeParams.isEmpty()) { + // We don't need lifetime arguments, so just provide an empty list. + ValidCandidate vc = validateCandidate(p.first(), + candidateType, candidateParameterTypes, + targetParameterTypes, candidateLifetimeParams, + Collections. emptyList(), + environment); + if (vc != null) { + validCandidates.add(vc); + } + } else if (lifetimeArgs != null) { + // We got some lifetime arguments. Just check it with them. + ValidCandidate vc = validateCandidate(p.first(), + candidateType, candidateParameterTypes, + targetParameterTypes, candidateLifetimeParams, + lifetimeArgs, + environment); + if (vc != null) { + validCandidates.add(vc); + } + } else { + // Here it is a bit tricky: + // We need to "guess" suitable lifetime arguments. + + // Make sure we know all lifetime names from our arguments, and + // cache the result for the next candidate. + if (lifetimesUsedInArguments == null) { + lifetimesUsedInArguments = extractLifetimesFromArguments(targetParameterTypes); + } + + // Guess the lifetime arguments. + guessLifetimeArguments(lifetimesUsedInArguments, candidateLifetimeParams, + candidateParameterTypes, targetParameterTypes, + p.first(), candidateType, validCandidates, environment); + } + } + + // See if we have valid candidates + if (validCandidates.isEmpty()) { + // No valid candidates + throw new ResolveError("no match for " + name + parameterString(parameters) + + foundCandidatesString(candidates)); + } + + // More than one candidate + if (validCandidates.size() != 1) { + // Idea: We iterate through the list and delete a valid candidate, + // if there is another one that is a strict subtype. + // The outer iterator is used to actually modify the list by + // removing candidates. + ListIterator it = validCandidates.listIterator(); + + // we know that the list is not empty, so do-while is perfectly fine + // here + do { + ValidCandidate c1 = it.next(); + + // Let the inner iterator start at the next entry. Note that the + // list initially had > 1 elements and the outer do-while also + // checks that there is one more element left, so we can again + // use do-while here. + for (ValidCandidate c2 : validCandidates) { + if (c1 != c2 && paramStrictSubtypes(c1.parameterTypesSubstituted, c2.parameterTypesSubstituted, environment)) { + it.remove(); + break; + } + } + } while (it.hasNext()); + } + + if (validCandidates.size() == 1) { // now check protection modifier - WhileyFile wf = builder.getSourceFile(candidateID.module()); + ValidCandidate winner = validCandidates.get(0); + NameID winnerId = winner.id; + Nominal.FunctionOrMethod winnerType = winner.type; + WhileyFile wf = builder.getSourceFile(winnerId.module()); if (wf != null) { if (wf != context.file()) { - for (WhileyFile.FunctionOrMethod d : wf.declarations(WhileyFile.FunctionOrMethod.class, - candidateID.name())) { - if (d.parameters.equals(candidateType.params())) { + for (WhileyFile.FunctionOrMethod d : wf.declarations(WhileyFile.FunctionOrMethod.class, winnerId.name())) { + if (d.parameters.equals(winnerType.params())) { if (!d.hasModifier(Modifier.PUBLIC)) { - String msg = candidateID.module() + "." + name + parameterString(parameters) - + " is not visible"; + String msg = winnerId.module() + "." + name + parameterString(parameters) + " is not visible"; throw new ResolveError(msg); } } } } } else { - WyilFile m = builder.getModule(candidateID.module()); - WyilFile.FunctionOrMethod d = m.functionOrMethod(candidateID.name(), candidateType.nominal()); + WyilFile m = builder.getModule(winnerId.module()); + WyilFile.FunctionOrMethod d = m.functionOrMethod(winnerId.name(), winnerType.nominal()); if (!d.hasModifier(Modifier.PUBLIC)) { - String msg = candidateID.module() + "." + name + parameterString(parameters) + " is not visible"; + String msg = winnerId.module() + "." + name + parameterString(parameters) + " is not visible"; throw new ResolveError(msg); } } + + return new Triple>(winnerId, winnerType, winner.lifetimeArguments); } - return new Pair(candidateID, candidateType); + // this is an ambiguous error + StringBuilder msg = new StringBuilder(name + parameterString(parameters) + " is ambiguous"); + ArrayList candidateStrings = new ArrayList(); + for (ValidCandidate c : validCandidates) { + String s = c.id + " : " + c.type.nominal(); + if (!c.lifetimeArguments.isEmpty()) { + s += " instantiated with <"; + boolean first = true; + for (String lifetime : c.lifetimeArguments) { + if (!first) { + s += ", "; + } else { + first = false; + } + s += lifetime; + } + s += ">"; + } + candidateStrings.add(s); + } + Collections.sort(candidateStrings); // make error message deterministic! + for (String s : candidateStrings) { + msg.append("\n\tfound: "); + msg.append(s); + } + throw new ResolveError(msg.toString()); + } + + /** + * Guess lifetime arguments for a method call. + * + * @param lifetimesUsedInArguments + * possible choices for lifetimes to be used as argument + * @param candidateLifetimeParams + * lifetime parameters to be assigned with an argument + * @param candidateParameterTypes + * parameter types of the actual declared method + * @param targetParameterTypes + * parameter types as needed (extracted from caller arguments) + * @param candidateName + * @param candidateType + * @param validCandidates + * the set where we can put valid substitutions + * @param environment + */ + private static void guessLifetimeArguments( + List lifetimesUsedInArguments, List candidateLifetimeParams, + List candidateParameterTypes, List targetParameterTypes, + NameID candidateName, Nominal.FunctionOrMethod candidateType, + List validCandidates, Environment environment) { + // Assume we have "exp" lifetime parameters to be filled and + // "base" choices for each one. + // That makes base^exp possibilities! + int base = lifetimesUsedInArguments.size(); + int exp = candidateLifetimeParams.size(); + long count = (long) Math.pow(base, exp); + for (long guessNumber = 0; guessNumber < count; guessNumber++) { + // Here we generate a guessed list of lifetime arguments. + // Basically it is the algorithm to transform guessNumber to + // base size(lifetimesUsedInArguments). + List guessedLifetimeArgs = new ArrayList(candidateLifetimeParams.size()); + for (int i = 0; i < exp; i++) { + int guessed = (int) ((guessNumber / (long) Math.pow(base, i)) % base); + guessedLifetimeArgs.add(lifetimesUsedInArguments.get(guessed)); + } + + // Now we can check the candidate with our guess + ValidCandidate vc = validateCandidate(candidateName, candidateType, candidateParameterTypes, + targetParameterTypes, candidateLifetimeParams, guessedLifetimeArgs, environment); + if (vc != null) { + validCandidates.add(vc); + } + } } /** @@ -2238,6 +2588,56 @@ private static List stripNominal(List types) { return r; } + /** + * Apply a lifetime substitution: Substitute all parameters in original by + * their arguments. + * + * @param lifetimeParameters + * @param lifetimeArguments + * @param original + * @return + */ + public static Nominal applySubstitution(List lifetimeParameters, List lifetimeArguments, Nominal original) { + if (lifetimeParameters.size() != lifetimeArguments.size()) { + throw new IllegalArgumentException("lifetime parameter/argument size mismatch!" + lifetimeParameters + " vs. " + lifetimeArguments); + } + Map substitution = buildSubstitution(lifetimeParameters, lifetimeArguments); + if (substitution.isEmpty()) { + return original; + } + return applySubstitution(substitution, original); + } + + private static Type applySubstitution(Map substitution, Type original) { + if (substitution.isEmpty()) { + return original; + } + return new LifetimeSubstitution(original, substitution).getType(); + } + + private static Nominal applySubstitution(Map substitution, Nominal original) { + if (substitution.isEmpty()) { + return original; + } + Type nominal = new LifetimeSubstitution(original.nominal(), substitution).getType(); + Type raw = new LifetimeSubstitution(original.raw(), substitution).getType(); + return Nominal.construct(nominal, raw); + } + + private static Map buildSubstitution(List lifetimeParameters, List lifetimeArguments) { + Map substitution = new HashMap(); + Iterator itP = lifetimeParameters.iterator(); + Iterator itA = lifetimeArguments.iterator(); + while (itP.hasNext()) { + String param = itP.next(); + String arg = itA.next(); + if (!arg.equals(param)) { + substitution.put(param, arg); + } + } + return substitution; + } + // ========================================================================= // ResolveAsName // ========================================================================= @@ -2595,12 +2995,15 @@ private int resolveAsType(SyntacticType type, Context context, ArrayList utParamTypes = ut.paramTypes; ArrayList utReturnTypes = ut.returnTypes; + ArrayList utContextLifetimes = ut.contextLifetimes; + ArrayList utLifetimeParameters = ut.lifetimeParameters; if (ut instanceof SyntacticType.Method) { myKind = Type.K_METHOD; @@ -2619,7 +3022,7 @@ private int resolveAsType(SyntacticType type, Context context, ArrayList(utContextLifetimes), utLifetimeParameters); } states.set(myIndex, new Automaton.State(myKind, myData, myDeterministic, myChildren)); @@ -3247,22 +3650,22 @@ private Environment addDeclaredParameter(WhileyFile.Parameter parameter, Environ // ========================================================================= // Check t1 :> t2 - private void checkIsSubtype(Nominal t1, Nominal t2, SyntacticElement elem) { - if (!Type.isSubtype(t1.raw(), t2.raw())) { + private void checkIsSubtype(Nominal t1, Nominal t2, SyntacticElement elem, Environment environment) { + if (!Type.isSubtype(t1.raw(), t2.raw(), environment.getLifetimeRelation())) { syntaxError(errorMessage(SUBTYPE_ERROR, t1.nominal(), t2.nominal()), filename, elem); } } - private void checkIsSubtype(Nominal t1, Expr t2) { - if (!Type.isSubtype(t1.raw(), t2.result().raw())) { + private void checkIsSubtype(Nominal t1, Expr t2, Environment environment) { + if (!Type.isSubtype(t1.raw(), t2.result().raw(), environment.getLifetimeRelation())) { // We use the nominal type for error reporting, since this includes // more helpful names. syntaxError(errorMessage(SUBTYPE_ERROR, t1.nominal(), t2.result().nominal()), filename, t2); } } - private void checkIsSubtype(Type t1, Expr t2) { - if (!Type.isSubtype(t1, t2.result().raw())) { + private void checkIsSubtype(Type t1, Expr t2, Environment environment) { + if (!Type.isSubtype(t1, t2.result().raw(), environment.getLifetimeRelation())) { // We use the nominal type for error reporting, since this includes // more helpful names. syntaxError(errorMessage(SUBTYPE_ERROR, t1, t2.result().nominal()), filename, t2); @@ -3270,22 +3673,22 @@ private void checkIsSubtype(Type t1, Expr t2) { } // Check t1 :> t2 - private void checkIsSubtype(Nominal t1, Nominal t2, SyntacticElement elem, Context context) { - if (!Type.isSubtype(t1.raw(), t2.raw())) { + private void checkIsSubtype(Nominal t1, Nominal t2, SyntacticElement elem, Context context, Environment environment) { + if (!Type.isSubtype(t1.raw(), t2.raw(), environment.getLifetimeRelation())) { syntaxError(errorMessage(SUBTYPE_ERROR, t1.nominal(), t2.nominal()), context, elem); } } - private void checkIsSubtype(Nominal t1, Expr t2, Context context) { - if (!Type.isSubtype(t1.raw(), t2.result().raw())) { + private void checkIsSubtype(Nominal t1, Expr t2, Context context, Environment environment) { + if (!Type.isSubtype(t1.raw(), t2.result().raw(), environment.getLifetimeRelation())) { // We use the nominal type for error reporting, since this includes // more helpful names. syntaxError(errorMessage(SUBTYPE_ERROR, t1.nominal(), t2.result().nominal()), context, t2); } } - private void checkIsSubtype(Type t1, Expr t2, Context context) { - if (!Type.isSubtype(t1, t2.result().raw())) { + private void checkIsSubtype(Type t1, Expr t2, Context context, Environment environment) { + if (!Type.isSubtype(t1, t2.result().raw(), environment.getLifetimeRelation())) { // We use the nominal type for error reporting, since this includes // more helpful names. syntaxError(errorMessage(SUBTYPE_ERROR, t1, t2.result().nominal()), context, t2); @@ -3293,10 +3696,10 @@ private void checkIsSubtype(Type t1, Expr t2, Context context) { } // Check t1 <: t2 or t1 <: t3 ... - private void checkSuptypes(Expr e, Context context, Nominal... types) { + private void checkSuptypes(Expr e, Context context, Environment environment, Nominal... types) { Nominal t1 = e.result(); for (Nominal t : types) { - if (Type.isSubtype(t.raw(), t1.raw())) { + if (Type.isSubtype(t.raw(), t1.raw(), environment.getLifetimeRelation())) { return; // OK } } @@ -3357,6 +3760,23 @@ private static final class Environment { */ private int count; // refCount + /** + * Whether we are currently inside a Lambda body + */ + private boolean inLambda; + + /** + * The lifetimes that are allowed to be dereferenced in a lambda body. + * These are lifetime parameters to the lambda expression and declared context lifetimes. + */ + private final HashSet lambdaLifetimes; + + /** + * The lifetime relation remembers how lifetimes are ordered (they are + * in a partial order). + */ + private final LifetimeRelation lifetimeRelation; + /** * Construct an empty environment. Initially the reference count is 1. */ @@ -3364,6 +3784,9 @@ public Environment() { count = 1; currentTypes = new HashMap(); declaredTypes = new HashMap(); + inLambda = false; + lambdaLifetimes = new HashSet(); + lifetimeRelation = new LifetimeRelation(); } /** @@ -3374,6 +3797,9 @@ private Environment(Environment environment) { count = 1; this.currentTypes = (HashMap) environment.currentTypes.clone(); this.declaredTypes = (HashMap) environment.declaredTypes.clone(); + inLambda = environment.inLambda; + lambdaLifetimes = (HashSet) environment.lambdaLifetimes.clone(); + lifetimeRelation = new LifetimeRelation(environment.lifetimeRelation); } /** @@ -3437,6 +3863,7 @@ public Set keySet() { * association. */ public Environment declare(String variable, Nominal declared, Nominal initial) { + // TODO: check that lifetimes and variables are disjoint if (declaredTypes.containsKey(variable)) { throw new RuntimeException("Variable already declared - " + variable); } @@ -3453,6 +3880,77 @@ public Environment declare(String variable, Nominal declared, Nominal initial) { } } + /** + * Declare lifetime parameters for a method. In the case that this + * environment has a reference count of 1, then an "in place" update is + * performed. Otherwise, a fresh copy of this environment is returned + * with the given variable associated with the given type, whilst this + * environment is unchanged. + * + * @param lifetimeParameters + * @return An updated version of the environment which contains the + * lifetime parameters. + */ + public Environment declareLifetimeParameters(List lifetimeParameters) { + // TODO: check duplicated variable/lifetime names + if (count == 1) { + this.lifetimeRelation.addParameters(lifetimeParameters); + return this; + } else { + Environment nenv = new Environment(this); + nenv.lifetimeRelation.addParameters(lifetimeParameters); + count--; + return nenv; + } + } + + /** + * Declare a lifetime for a named block. In the case that this + * environment has a reference count of 1, then an "in place" update is + * performed. Otherwise, a fresh copy of this environment is returned + * with the given variable associated with the given type, whilst this + * environment is unchanged. + * + * @param lifetime + * @return An updated version of the environment which contains the + * named block. + */ + public Environment startNamedBlock(String lifetime) { + // TODO: check duplicated variable/lifetime names + if (count == 1) { + this.lifetimeRelation.startNamedBlock(lifetime); + return this; + } else { + Environment nenv = new Environment(this); + nenv.lifetimeRelation.startNamedBlock(lifetime); + count--; + return nenv; + } + } + + /** + * End the last named block, i.e. remove its declared lifetime. In the + * case that this environment has a reference count of 1, then an + * "in place" update is performed. Otherwise, a fresh copy of this + * environment is returned with the given variable associated with the + * given type, whilst this environment is unchanged. + * + * @param lifetime + * @return An updated version of the environment without the given named + * block. + */ + public Environment endNamedBlock(String lifetime) { + if (count == 1) { + this.lifetimeRelation.endNamedBlock(lifetime); + return this; + } else { + Environment nenv = new Environment(this); + nenv.lifetimeRelation.endNamedBlock(lifetime); + count--; + return nenv; + } + } + /** * Update the current type of a given variable. If that variable already * had a current type, then this is overwritten. In the case that this @@ -3509,6 +4007,46 @@ public Environment remove(String key) { } } + /** + * Create a fresh copy of this environment, but set the lambda flag and + * remember the given context lifetimes and lifetime parameters. + * + * @param contextLifetimes + * the declared context lifetimes + * @param lifetimeParameters + * The lifetime names that are allowed to be dereferenced + * inside the lambda. + */ + public Environment startLambda(Collection contextLifetimes, Collection lifetimeParameters) { + Environment nenv = new Environment(this); + nenv.inLambda = true; + nenv.lambdaLifetimes.clear(); + nenv.lambdaLifetimes.addAll(contextLifetimes); + nenv.lambdaLifetimes.addAll(lifetimeParameters); + return nenv; + } + + /** + * Check whether we are allowed to dereference the given lifetime. + * Inside a lambda, only "*", the declared context lifetimes and the + * lifetime parameters can be dereferenced. + * + * @param lifetime + * @return + */ + public boolean canDereferenceLifetime(String lifetime) { + return !inLambda || lifetime.equals("*") || lambdaLifetimes.contains(lifetime); + } + + /** + * Get the current lifetime relation. + * + * @return + */ + public LifetimeRelation getLifetimeRelation() { + return this.lifetimeRelation; + } + /** * Merge a given environment with this environment to produce an * environment representing their join. Only variables from a given set @@ -3546,6 +4084,7 @@ public final Environment merge(Set declared, Environment env) { Nominal rhs_t = env.getCurrentType(variable); result.declare(variable, this.getDeclaredType(variable), Nominal.Union(lhs_t, rhs_t)); } + result.lifetimeRelation.replaceWithMerge(this.lifetimeRelation, env.lifetimeRelation); return result; } @@ -3572,13 +4111,14 @@ public String toString() { } public int hashCode() { - return currentTypes.hashCode(); + return 31 * currentTypes.hashCode() + lambdaLifetimes.hashCode(); } public boolean equals(Object o) { if (o instanceof Environment) { Environment r = (Environment) o; - return currentTypes.equals(r.currentTypes); + return currentTypes.equals(r.currentTypes) + && lambdaLifetimes.equals(r.lambdaLifetimes); } return false; } diff --git a/modules/wyc/src/wyc/io/WhileyFileLexer.java b/modules/wyc/src/wyc/io/WhileyFileLexer.java index 12cf696da0..df0379ddea 100755 --- a/modules/wyc/src/wyc/io/WhileyFileLexer.java +++ b/modules/wyc/src/wyc/io/WhileyFileLexer.java @@ -563,6 +563,8 @@ private void syntaxError(String msg, int index) { put("export", Token.Kind.Export); put("method", Token.Kind.Method); put("package", Token.Kind.Package); + // lifetimes + put("this", Token.Kind.This); } }; @@ -627,6 +629,8 @@ public enum Kind { Export("export"), Function("function"), Method("method"), + // Lifetimes + This("this"), // Expressions All("all"), No("no"), diff --git a/modules/wyc/src/wyc/io/WhileyFileParser.java b/modules/wyc/src/wyc/io/WhileyFileParser.java index be2b94c3f4..28ab0955c3 100644 --- a/modules/wyc/src/wyc/io/WhileyFileParser.java +++ b/modules/wyc/src/wyc/io/WhileyFileParser.java @@ -36,8 +36,6 @@ import java.util.List; import java.util.Set; -import com.sun.org.apache.bcel.internal.Constants; - import wyc.lang.*; import wyc.lang.Expr.ConstantAccess; import wyc.io.WhileyFileLexer.Token; @@ -154,7 +152,7 @@ private void parseImportDeclaration(WhileyFile wf) { // First, parse "from" usage (if applicable) Token token = tryAndMatch(true, Identifier, Star); if (token == null) { - syntaxError("expected identifier or '*' here", token); + syntaxError("expected identifier or '*' here", tokens.get(index)); } String name = token.text; // NOTE: we don't specify "from" as a keyword because this prevents it @@ -278,16 +276,22 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, List modifiers, boolean isFunction) { int start = index; + EnclosingScope scope = new EnclosingScope(); + List lifetimeParameters; + if (isFunction) { match(Function); + lifetimeParameters = Collections.emptyList(); } else { match(Method); + + // Lifetime parameters + lifetimeParameters = parseOptionalLifetimeParameters(scope); } Token name = match(Identifier); // Parse function or method parameters - EnclosingScope scope = new EnclosingScope(); List parameters = parseParameters(wf,scope); // Parse (optional) return type @@ -334,6 +338,7 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, match(Colon); end = index; matchEndLine(); + scope.declareThisLifetime(); stmts = parseBlock(wf, scope, false); } @@ -342,8 +347,8 @@ private void parseFunctionOrMethodDeclaration(WhileyFile wf, declaration = wf.new Function(modifiers, name.text, returns, parameters, requires, ensures, stmts, sourceAttr(start, end - 1)); } else { - declaration = wf.new Method(modifiers, name.text, returns, parameters, requires, ensures, stmts, - sourceAttr(start, end - 1)); + declaration = wf.new Method(modifiers, name.text, returns, parameters, + lifetimeParameters, requires, ensures, stmts, sourceAttr(start, end - 1)); } wf.add(declaration); } @@ -358,13 +363,9 @@ public List parseParameters(WhileyFile wf, EnclosingScope scope) { } firstTime = false; int pStart = index; - Pair p = parseMixedType(); + Pair p = parseMixedType(scope); Token id = p.second(); - if (scope.contains(id.text)) { - syntaxError("parameter already declared", id); - } else { - scope.add(id.text); - } + scope.declareVariable(id); parameters.add(wf.new Parameter(p.first(), id.text, sourceAttr( pStart, index - 1))); } @@ -390,17 +391,13 @@ public Parameter parseOptionalParameter(WhileyFile wf, EnclosingScope scope) { SyntacticType type; String name; if(tryAndMatch(true,LeftBrace) != null) { - Pair p = parseMixedType(); + Pair p = parseMixedType(scope); type = p.first(); - name = p.second().text; - if (scope.contains(name)) { - syntaxError("parameter already declared",p.second()); - } else { - scope.add(name); - } + name = p.second().text; + scope.declareVariable(p.second()); match(RightBrace); } else { - type = parseType(); + type = parseType(scope); name = null; } return wf.new Parameter(type, name, sourceAttr(start, index - 1)); @@ -646,7 +643,9 @@ private Stmt parseStatement(WhileyFile wf, EnclosingScope scope) { } // At this point, we have three possibilities remaining: variable - // declaration, invocation or assignment. To disambiguate these, we + // declaration, invocation, assignment, or a named block. + // The latter one can be detected easily as it is just an identifier + // followed by a colon. To disambiguate the remaining cases, we // first determine whether or not what follows *must* be parsed as a // type (i.e. parsing it as an expression would fail). If so, then it // must be a variable declaration that follows. Otherwise, it can still @@ -658,8 +657,8 @@ private Stmt parseStatement(WhileyFile wf, EnclosingScope scope) { /** * A headless statement is one which has no identifying keyword. The set of - * headless statements include assignments, invocations and variable - * declarations. + * headless statements include assignments, invocations, variable + * declarations and named blocks. * * @param wf * @param scope @@ -670,7 +669,25 @@ private Stmt parseStatement(WhileyFile wf, EnclosingScope scope) { */ private Stmt parseHeadlessStatement(WhileyFile wf, EnclosingScope scope) { int start = index; - SyntacticType type = parseDefiniteType(); + + // See if it is a named block + Token blockName = tryAndMatch(true, Identifier); + if (blockName != null) { + if (tryAndMatch(true, Colon) != null && isAtEOL()) { + int end = index; + matchEndLine(); + scope = scope.newEnclosingScope(); + scope.declareLifetime(blockName); + + List body = parseBlock(wf, scope, false); + return new Stmt.NamedBlock(blockName.text, body, sourceAttr(start, end - 1)); + } else { + index = start; // backtrack + } + } + + // Remaining cases: assignments, invocations and variable declarations + SyntacticType type = parseDefiniteType(scope); if (type == null) { // Can still be a variable declaration, assignment or invocation. @@ -698,7 +715,7 @@ private Stmt parseHeadlessStatement(WhileyFile wf, EnclosingScope scope) { // Therefore, we backtrack and parse the expression again as a // type. index = start; // backtrack - type = parseType(); + type = parseType(scope); } } // Must be a variable declaration here. @@ -738,11 +755,7 @@ private Stmt.VariableDeclaration parseVariableDeclaration(int start, // Ensure at least one variable is defined by this pattern. // Check that declared variables are not already defined. - if (scope.contains(parameter.name)) { - syntaxError("variable already declared", parameter); - } else { - - } + scope.checkNameAvailable(parameter); // A variable declaration may optionally be assigned an initialiser // expression. @@ -756,7 +769,7 @@ private Stmt.VariableDeclaration parseVariableDeclaration(int start, // Finally, register the new variable in the enclosing scope. This // should be done after parsing the initialiser expression to prevent it // from referring to this variable. - scope.add(parameter.name); + scope.declareVariable(parameter); // Done. return new Stmt.VariableDeclaration(parameter, initialiser, sourceAttr( start, end - 1)); @@ -1887,7 +1900,7 @@ private Expr parseConditionExpression(WhileyFile wf, bop = Expr.BOp.NEQ; break; case Is: - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); Expr.TypeVal rhs = new Expr.TypeVal(type, sourceAttr(start, index - 1)); return new Expr.BinOp(Expr.BOp.IS, lhs, rhs, sourceAttr(start, @@ -1968,16 +1981,13 @@ private Expr parseQuantifierExpression(Token lookahead, WhileyFile wf, } firstTime = false; Token id = match(Identifier); - if (scope.contains(id.text)) { - // It is already defined which is a syntax error - syntaxError("variable already declared", id); - } + scope.checkNameAvailable(id); match(In); Expr lhs = parseAdditiveExpression(wf, scope, terminated); match(DotDot); Expr rhs = parseAdditiveExpression(wf, scope, terminated); srcs.add(new Triple(id.text, lhs, rhs)); - scope.add(id.text); + scope.declareVariable(id); } while (eventuallyMatch(VerticalBar) == null); // Parse condition over source variables @@ -2278,7 +2288,32 @@ private Expr parseAccessExpression(WhileyFile wf, // is not a declared variable name. Path.ID id = parsePossiblePathID(lhs, scope); + // First we have to see if it is a method invocation. We can + // have optional lifetime arguments in angle brackets. + boolean isInvocation = false; + List lifetimeArguments = null; if (tryAndMatch(terminated, LeftBrace) != null) { + isInvocation = true; + } else if (lookaheadSequence(terminated, LeftAngle)) { + // This one is a little tricky, as we need some lookahead + // effort. We want to see whether it is a method invocation with + // lifetime arguments. But "Identifier < ..." can also be a + // boolean expression! + int oldindex = index; + match(LeftAngle); + Token lifetime = tryAndMatch(terminated, RightAngle, Identifier, This, Star); + if (lifetime != null && ( + lifetime.kind != Identifier // then it's definitely a lifetime + || scope.isLifetime(lifetime.text))) { + isInvocation = true; + index--; // don't forget the first argument! + lifetimeArguments = parseLifetimeArguments(wf, scope); + match(LeftBrace); + } else { + index = oldindex; // backtrack + } + } + if (isInvocation) { // This indicates a direct or indirect invocation. First, // parse arguments to invocation ArrayList arguments = parseInvocationArguments(wf,scope); @@ -2288,11 +2323,11 @@ private Expr parseAccessExpression(WhileyFile wf, lhs = new Expr.FieldAccess(lhs, name, sourceAttr( start, index - 1)); lhs = new Expr.AbstractIndirectInvoke(lhs, arguments, - sourceAttr(start, index - 1)); + lifetimeArguments, sourceAttr(start, index - 1)); } else { // This indicates we have an direct invocation lhs = new Expr.AbstractInvoke(name, id, arguments, - sourceAttr(start, index - 1)); + lifetimeArguments, sourceAttr(start, index - 1)); } } else if(id != null) { @@ -2378,13 +2413,38 @@ private Expr parseTermExpression(WhileyFile wf, case LeftBrace: return parseBracketedExpression(wf, scope, terminated); case New: + case This: return parseNewExpression(wf, scope, terminated); case Identifier: match(Identifier); if (tryAndMatch(terminated, LeftBrace) != null) { return parseInvokeExpression(wf, scope, start, token, - terminated); - } else if (scope.contains(token.text)) { + terminated, null); + } else if (lookaheadSequence(terminated, Colon, New)) { + // Identifier is lifetime name in "new" expression + index = start; + return parseNewExpression(wf, scope, terminated); + } else if (lookaheadSequence(terminated, LeftAngle)) { + // This one is a little tricky, as we need some lookahead + // effort. We want to see whether it is a method invocation with + // lifetime arguments. But "Identifier < ..." can also be a + // boolean expression! + int oldindex = index; + match(LeftAngle); + Token lifetime = tryAndMatch(terminated, RightAngle, Identifier, This, Star); + if (lifetime != null && ( + lifetime.kind != Identifier // then it's definitely a lifetime + || scope.isLifetime(lifetime.text))) { + index--; // don't forget the first argument! + List lifetimeArguments = parseLifetimeArguments(wf, scope); + match(LeftBrace); + return parseInvokeExpression( + wf, scope, start, token, terminated, lifetimeArguments); + } else { + index = oldindex; // backtrack + } + } // no else if, in case the former one didn't return + if (scope.isVariable(token.text)) { // Signals a local variable access return new Expr.LocalVariable(token.text, sourceAttr(start, index - 1)); @@ -2436,6 +2496,10 @@ private Expr parseTermExpression(WhileyFile wf, case Shreak: return parseLogicalNotExpression(wf, scope, terminated); case Star: + if (lookaheadSequence(terminated, Star, Colon, New)) { + // Star is default lifetime + return parseNewExpression(wf, scope, terminated); + } return parseDereferenceExpression(wf, scope, terminated); case Tilde: return parseBitwiseComplementExpression(wf, scope, terminated); @@ -2534,7 +2598,7 @@ private Expr parseBracketedExpression(WhileyFile wf, // "(nat,nat)" could either be a tuple type (if "nat" is a type) or a // tuple expression (if "nat" is a variable or constant). - SyntacticType t = parseDefiniteType(); + SyntacticType t = parseDefiniteType(scope); if (t != null) { // At this point, it's looking likely that we have a cast. However, @@ -2594,7 +2658,7 @@ private Expr parseBracketedExpression(WhileyFile wf, // Ok, this must be cast so back tract and reparse // expression as a type. index = start; // backtrack - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); // Now, parse cast expression e = parseExpression(wf, scope, terminated); return new Expr.Cast(type, e, sourceAttr(start, index - 1)); @@ -2822,6 +2886,7 @@ private Expr parseRecordExpression(WhileyFile wf, *
 	 * TermExpr::= ...
 	 *                 |  "new" Expr
+	 *                 |  Lifetime ":" "new" Identifier Expr
 	 * 
* * @param wf @@ -2849,9 +2914,21 @@ private Expr parseRecordExpression(WhileyFile wf, private Expr parseNewExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { int start = index; + + // try to match a lifetime + String lifetime; + Token lifetimeIdentifier = tryAndMatch(terminated, Identifier, This, Star); + if (lifetimeIdentifier != null) { + scope.mustBeLifetime(lifetimeIdentifier); + lifetime = lifetimeIdentifier.text; + match(Colon); + } else { + lifetime = "*"; + } + match(New); Expr e = parseExpression(wf, scope, terminated); - return new Expr.New(e, sourceAttr(start, index - 1)); + return new Expr.New(e, lifetime, sourceAttr(start, index - 1)); } /** @@ -2970,23 +3047,23 @@ private Expr parseNegationExpression(WhileyFile wf, * @return */ private Expr parseInvokeExpression(WhileyFile wf, EnclosingScope scope, int start, Token name, - boolean terminated) { + boolean terminated, List lifetimeArguments) { // First, parse the arguments to this invocation. ArrayList args = parseInvocationArguments(wf, scope); // Second, determine what kind of invocation we have. If the name of the // method is a local variable, then it must be an indirect invocation on // this variable. - if (scope.contains(name.text)) { + if (scope.isVariable(name.text)) { // indirect invocation on local variable Expr.LocalVariable lv = new Expr.LocalVariable(name.text, sourceAttr(start, start)); - return new Expr.AbstractIndirectInvoke(lv, args, sourceAttr(start, - index - 1)); + return new Expr.AbstractIndirectInvoke(lv, args, lifetimeArguments, + sourceAttr(start, index - 1)); } else { // unqualified direct invocation - return new Expr.AbstractInvoke(name.text, null, args, sourceAttr( - start, index - 1)); + return new Expr.AbstractInvoke(name.text, null, args, lifetimeArguments, + sourceAttr(start, index - 1)); } } @@ -3046,6 +3123,39 @@ private ArrayList parseInvocationArguments(WhileyFile wf, return args; } + /** + * Parse a sequence of lifetime arguments separated by commas that ends in a + * right-angle: + * + *
+	 * LifetimeArguments ::= [ Lifetime (',' Lifetime)* ] '>'
+	 * 
+ * + * Note, when this function is called we're assuming the left angle was + * already parsed. + * + * @param wf + * @param scope + * @return + */ + private ArrayList parseLifetimeArguments(WhileyFile wf, EnclosingScope scope) { + boolean firstTime = true; + ArrayList lifetimeArgs = new ArrayList(); + while (eventuallyMatch(RightAngle) == null) { + if (!firstTime) { + match(Comma); + } else { + firstTime = false; + } + + // termindated by '>' + String lifetime = parseLifetime(scope, true); + + lifetimeArgs.add(lifetime); + } + return lifetimeArgs; + } + /** * Parse a logical not expression, which has the form: * @@ -3120,8 +3230,10 @@ private Expr parseDereferenceExpression(WhileyFile wf, * *
 	 * TermExpr::= ...
-	 *                 | '&' '(' [Type Identifier (',' Type Identifier)*] '->' Expr ')'
-	 *                 | '&' Identifier [ '(' Type Identifier (',' Type Identifier)* ')']
+	 *                 | '&' [ '[' [ Lifetime   (',' Lifetime  )* ] ']' ]
+	 *                       [ '<' [ Identifier (',' Identifier)* ] '>' ]
+	 *                   '(' [Type Identifier (',' Type Identifier)*] '->' Expr ')'
+	 *                 | '&' Identifier [ '(' Type (',' Type)* ')']
 	 * 
* * Disambiguating these two forms is relatively straightforward, and we just @@ -3153,7 +3265,7 @@ private Expr parseLambdaOrAddressExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { int start = index; match(Ampersand); - if (tryAndMatch(terminated, LeftBrace) != null) { + if (tryAndMatch(terminated, LeftBrace, LeftSquare, LeftAngle) != null) { index = start; // backtrack return parseLambdaExpression(wf, scope, terminated); } else { @@ -3167,7 +3279,9 @@ private Expr parseLambdaOrAddressExpression(WhileyFile wf, * *
 	 * TermExpr::= ...
-	 *                 |  '&' '(' [Type Identifier (',' Type Identifier)*] '->' Expr ')'
+	 *                 | '&' [ '[' [ Lifetime   (',' Lifetime  )* ] ']' ]
+	 *                       [ '<' [ Identifier (',' Identifier)* ] '>' ]
+	 *                   '(' [Type Identifier (',' Type Identifier)*] '->' Expr ')'
 	 * 
* * @param wf @@ -3196,11 +3310,21 @@ private Expr parseLambdaExpression(WhileyFile wf, EnclosingScope scope, boolean terminated) { int start = index; match(Ampersand); + + // First parse the context lifetimes with the original scope + Set contextLifetimes = parseOptionalContextLifetimes(scope); + + // Now we create a new scope for this lambda expression. + // It keeps all variables but only the given context lifetimes. + // But it keeps all unavailable names, i.e. unaccessible lifetimes + // from the outer scope cannot be redeclared. + scope = scope.newEnclosingScope(contextLifetimes); + + // Parse the optional lifetime parameters + List lifetimeParameters = parseOptionalLifetimeParameters(scope); + match(LeftBrace); ArrayList parameters = new ArrayList(); - // Clone the environment so we can update it with those declared - // parameters. - scope = scope.newEnclosingScope(); boolean firstTime = true; while (eventuallyMatch(MinusGreater) == null) { int p_start = index; @@ -3208,12 +3332,9 @@ private Expr parseLambdaExpression(WhileyFile wf, match(Comma); } firstTime = false; - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); Token id = match(Identifier); - if (scope.contains(id.text)) { - syntaxError("duplicate variable or parameter name", id); - } - scope.add(id.text); + scope.declareVariable(id); parameters.add(wf.new Parameter(type, id.text, sourceAttr(p_start, index - 1))); } @@ -3222,7 +3343,7 @@ private Expr parseLambdaExpression(WhileyFile wf, Expr body = parseExpression(wf, scope, true); match(RightBrace); - return new Expr.Lambda(parameters, body, sourceAttr(start, index - 1)); + return new Expr.Lambda(parameters, contextLifetimes, lifetimeParameters, body, sourceAttr(start, index - 1)); } /** @@ -3230,7 +3351,7 @@ private Expr parseLambdaExpression(WhileyFile wf, * *
 	 * TermExpr::= ...
-	 *                 | '&' Identifier [ '(' Type Identifier (',' Type Identifier)* ')']
+	 *                 | '&' Identifier [ '(' Type (',' Type)* ')']
 	 * 
* * @param wf @@ -3273,15 +3394,15 @@ private Expr parseAddressExpression(WhileyFile wf, match(Comma); } firstTime = false; - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); parameters.add(type); } return new Expr.AbstractFunctionOrMethod(id.text, parameters, - sourceAttr(start, index - 1)); + null, sourceAttr(start, index - 1)); } else { // No, parameters are not supplied. - return new Expr.AbstractFunctionOrMethod(id.text, null, sourceAttr( - start, index - 1)); + return new Expr.AbstractFunctionOrMethod(id.text, null, null, + sourceAttr(start, index - 1)); } } @@ -3330,10 +3451,10 @@ private Expr parseBitwiseComplementExpression(WhileyFile wf, * * @return An instance of SyntacticType or null. */ - public SyntacticType parseDefiniteType() { + public SyntacticType parseDefiniteType(EnclosingScope scope) { int start = index; // backtrack point try { - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); if (mustParseAsType(type)) { return type; } @@ -3393,6 +3514,12 @@ private boolean mustParseAsType(SyntacticType type) { return false; // always can be an expression } else if (type instanceof SyntacticType.Reference) { SyntacticType.Reference tt = (SyntacticType.Reference) type; + if (tt.lifetime.equals("this") || + tt.lifetime.equals("*") && tt.lifetimeWasExplicit) { + // &this and &* is not a valid expression because "this" is keyword + // &ident could also be an address expression + return true; + } return mustParseAsType(tt.element); } else if (type instanceof SyntacticType.Union) { SyntacticType.Union tt = (SyntacticType.Union) type; @@ -3487,7 +3614,7 @@ private boolean mustParseAsExpr(Expr e) { return false; // dead-code } } - + /** * Parse a top-level type, which is of the form: * @@ -3498,8 +3625,8 @@ private boolean mustParseAsExpr(Expr e) { * @see wyc.lang.SyntacticType.Tuple * @return */ - private SyntacticType parseType() { - return parseUnionType(); + private SyntacticType parseType(EnclosingScope scope) { + return parseUnionType(scope); } /** @@ -3511,9 +3638,9 @@ private SyntacticType parseType() { * * @return */ - private SyntacticType parseUnionType() { + private SyntacticType parseUnionType(EnclosingScope scope) { int start = index; - SyntacticType t = parseIntersectionType(); + SyntacticType t = parseIntersectionType(scope); // Now, attempt to look for union and/or intersection types if (tryAndMatch(true, VerticalBar) != null) { @@ -3521,7 +3648,7 @@ private SyntacticType parseUnionType() { ArrayList types = new ArrayList(); types.add(t); do { - types.add(parseIntersectionType()); + types.add(parseIntersectionType(scope)); } while (tryAndMatch(true, VerticalBar) != null); return new SyntacticType.Union(types, sourceAttr(start, index - 1)); } else { @@ -3538,9 +3665,9 @@ private SyntacticType parseUnionType() { * * @return */ - private SyntacticType parseIntersectionType() { + private SyntacticType parseIntersectionType(EnclosingScope scope) { int start = index; - SyntacticType t = parseArrayType(); + SyntacticType t = parseArrayType(scope); // Now, attempt to look for union and/or intersection types if (tryAndMatch(true, Ampersand) != null) { @@ -3548,7 +3675,7 @@ private SyntacticType parseIntersectionType() { ArrayList types = new ArrayList(); types.add(t); do { - types.add(parseArrayType()); + types.add(parseArrayType(scope)); } while (tryAndMatch(true, Ampersand) != null); return new SyntacticType.Intersection(types, sourceAttr(start, index - 1)); @@ -3566,9 +3693,9 @@ private SyntacticType parseIntersectionType() { * * @return */ - private SyntacticType parseArrayType() { + private SyntacticType parseArrayType(EnclosingScope scope) { int start = index; - SyntacticType element = parseBaseType(); + SyntacticType element = parseBaseType(scope); while (tryAndMatch(true, LeftSquare) != null) { match(RightSquare); @@ -3578,7 +3705,7 @@ private SyntacticType parseArrayType() { return element; } - private SyntacticType parseBaseType() { + private SyntacticType parseBaseType(EnclosingScope scope) { checkNotEof(); int start = index; Token token = tokens.get(index); @@ -3598,19 +3725,19 @@ private SyntacticType parseBaseType() { case Int: return new SyntacticType.Int(sourceAttr(start, index++)); case LeftBrace: - return parseBracketedType(); + return parseBracketedType(scope); case LeftCurly: - return parseRecordType(); + return parseRecordType(scope); case Shreak: - return parseNegationType(); + return parseNegationType(scope); case Ampersand: - return parseReferenceType(); + return parseReferenceType(scope); case Identifier: return parseNominalType(); case Function: - return parseFunctionOrMethodType(true); + return parseFunctionOrMethodType(true, scope); case Method: - return parseFunctionOrMethodType(false); + return parseFunctionOrMethodType(false, scope); default: syntaxError("unknown type encountered", token); return null; @@ -3626,10 +3753,10 @@ private SyntacticType parseBaseType() { * * @return */ - private SyntacticType parseNegationType() { + private SyntacticType parseNegationType(EnclosingScope scope) { int start = index; match(Shreak); - SyntacticType element = parseArrayType(); + SyntacticType element = parseArrayType(scope); return new SyntacticType.Negation(element, sourceAttr(start, index - 1)); } @@ -3638,18 +3765,57 @@ private SyntacticType parseNegationType() { * *
 	 * ReferenceType ::= '&' Type
+	 *                 | '&' Lifetime ':' Type
+	 *      Lifetime ::= Identifier | 'this' | '*'
 	 * 
* * @return */ - private SyntacticType parseReferenceType() { + private SyntacticType parseReferenceType(EnclosingScope scope) { int start = index; match(Ampersand); - SyntacticType element = parseArrayType(); - return new SyntacticType.Reference(element, - sourceAttr(start, index - 1)); + + // Try to parse an annotated lifetime + int backtrack = index; + Token lifetimeIdentifier = tryAndMatch(true, Identifier, This, Star); + if (lifetimeIdentifier != null) { + // We cannot allow a newline after the colon, as it would + // unintentionally match a return type that happens to be reference + // type without lifetime annotation (return type in method signature + // is always followed by colon and newline). + if (tryAndMatch(true, Colon) != null && !isAtEOL()) { + // Now we know that there is an annotated lifetime + scope.mustBeLifetime(lifetimeIdentifier); + SyntacticType element = parseArrayType(scope); + return new SyntacticType.Reference(element, lifetimeIdentifier.text, true, + sourceAttr(start, index - 1)); + } + } + index = backtrack; + + SyntacticType element = parseArrayType(scope); + return new SyntacticType.Reference(element, "*", false, sourceAttr(start, index - 1)); } + /** + * Parse a currently declared lifetime. + * + * @return the matched lifetime name + */ + private String parseLifetime(EnclosingScope scope, boolean terminated) { + int next = terminated ? skipWhiteSpace(index) : skipLineSpace(index); + if (next < tokens.size()) { + Token t = tokens.get(next); + if (t.kind == Identifier || t.kind == This || t.kind == Star) { + index = next + 1; + scope.mustBeLifetime(t); + return t.text; + } + syntaxError("expectiong a lifetime identifier here", t); + } + syntaxError("unexpected end-of-file", tokens.get(next - 1)); + throw new RuntimeException("deadcode"); // dead-code + } /** * Parse a bracketed type, which is of the form: @@ -3660,10 +3826,10 @@ private SyntacticType parseReferenceType() { * * @return */ - private SyntacticType parseBracketedType() { + private SyntacticType parseBracketedType(EnclosingScope scope) { int start = index; match(LeftBrace); - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); match(RightBrace); return type; } @@ -3685,12 +3851,12 @@ private SyntacticType parseBracketedType() { * * @return */ - private SyntacticType parseRecordType() { + private SyntacticType parseRecordType(EnclosingScope scope) { int start = index; match(LeftCurly); HashMap types = new HashMap(); - Pair p = parseMixedType(); + Pair p = parseMixedType(scope); types.put(p.second().text, p.first()); // Now, we continue to parse any remaining fields. @@ -3704,7 +3870,7 @@ private SyntacticType parseRecordType() { isOpen = true; break; } else { - p = parseMixedType(); + p = parseMixedType(scope); Token id = p.second(); if (types.containsKey(id.text)) { syntaxError("duplicate record key", id); @@ -3755,18 +3921,25 @@ private SyntacticType parseNominalType() { * * @return */ - private SyntacticType parseFunctionOrMethodType(boolean isFunction) { + private SyntacticType parseFunctionOrMethodType(boolean isFunction, EnclosingScope scope) { int start = index; + List lifetimeParameters; + Set contextLifetimes; if (isFunction) { match(Function); + contextLifetimes = Collections.emptySet(); + lifetimeParameters = Collections.emptyList(); } else { match(Method); + contextLifetimes = parseOptionalContextLifetimes(scope); + scope = scope.newEnclosingScope(); + lifetimeParameters = parseOptionalLifetimeParameters(scope); } // First, parse the parameter type(s). - List paramTypes = parseParameterTypes(); - List returnTypes = Collections.EMPTY_LIST; + List paramTypes = parseParameterTypes(scope); + List returnTypes = Collections.emptyList(); // Second, parse the right arrow. if (isFunction) { @@ -3774,18 +3947,19 @@ private SyntacticType parseFunctionOrMethodType(boolean isFunction) { // nops) match(MinusGreater); // Third, parse the return types. - returnTypes = parseOptionalParameterTypes(); + returnTypes = parseOptionalParameterTypes(scope); } else if (tryAndMatch(true, MinusGreater) != null) { // Methods have an optional return type // Third, parse the return type - returnTypes = parseOptionalParameterTypes(); + returnTypes = parseOptionalParameterTypes(scope); } // Done if (isFunction) { return new SyntacticType.Function(returnTypes, paramTypes, sourceAttr(start, index - 1)); } else { - return new SyntacticType.Method(returnTypes, paramTypes, sourceAttr(start, index - 1)); + return new SyntacticType.Method(returnTypes, paramTypes, contextLifetimes, + lifetimeParameters, sourceAttr(start, index - 1)); } } @@ -3800,7 +3974,7 @@ private SyntacticType parseFunctionOrMethodType(boolean isFunction) { * * @return */ - private Pair parseMixedType() { + private Pair parseMixedType(EnclosingScope scope) { Token lookahead; int start = index; @@ -3808,21 +3982,32 @@ private Pair parseMixedType() { // At this point, we *might* have a mixed function / method type // definition. To disambiguate, we need to see whether an identifier // follows or not. + // Similar to normal method declarations, the lifetime parameters + // go before the method name. We do not allow to have context lifetimes + // for mixed method types. + List lifetimeParameters = Collections.emptyList(); + if (lookahead.kind == Method && tryAndMatch(true, LeftAngle) != null) { + // mixed method type with lifetime parameters + scope = scope.newEnclosingScope(); + lifetimeParameters = parseLifetimeParameters(scope); + } + + // Now try to parse the identifier Token id = tryAndMatch(true, Identifier); if (id != null) { // Yes, we have found a mixed function / method type definition. // Therefore, we continue to pass the remaining type parameters. - List paramTypes = parseParameterTypes(); - List returnTypes = Collections.EMPTY_LIST; + List paramTypes = parseParameterTypes(scope); + List returnTypes = Collections.emptyList(); if (lookahead.kind == Function) { // Functions require a return type (since otherwise they are // just nops) match(MinusGreater); // Third, parse the return type - returnTypes = parseOptionalParameterTypes(); + returnTypes = parseOptionalParameterTypes(scope); } else if (tryAndMatch(true, MinusGreater) != null) { // Third, parse the (optional) return type. Observe that // this is forced to be a @@ -3831,7 +4016,7 @@ private Pair parseMixedType() { // may be part of an enclosing record type and we must // disambiguate // this. - returnTypes = parseOptionalParameterTypes(); + returnTypes = parseOptionalParameterTypes(scope); } // Done @@ -3839,7 +4024,8 @@ private Pair parseMixedType() { if (lookahead.kind == Token.Kind.Function) { type = new SyntacticType.Function(returnTypes, paramTypes, sourceAttr(start, index - 1)); } else { - type = new SyntacticType.Method(returnTypes, paramTypes, sourceAttr(start, index - 1)); + type = new SyntacticType.Method(returnTypes, paramTypes, Collections.emptySet(), + lifetimeParameters, sourceAttr(start, index - 1)); } return new Pair(type, id); } else { @@ -3852,24 +4038,24 @@ private Pair parseMixedType() { // This is the normal case, where we expect an identifier to follow the // type. - SyntacticType type = parseType(); + SyntacticType type = parseType(scope); Token id = match(Identifier); return new Pair(type, id); } - public List parseOptionalParameterTypes() { + public List parseOptionalParameterTypes(EnclosingScope scope) { int next = skipWhiteSpace(index); if(next < tokens.size() && tokens.get(next).kind == LeftBrace) { - return parseParameterTypes(); + return parseParameterTypes(scope); } else { - SyntacticType t = parseType(); + SyntacticType t = parseType(scope); ArrayList rs = new ArrayList(); rs.add(t); return rs; } } - public List parseParameterTypes() { + public List parseParameterTypes(EnclosingScope scope) { ArrayList paramTypes = new ArrayList(); match(LeftBrace); @@ -3879,12 +4065,60 @@ public List parseParameterTypes() { match(Comma); } firstTime = false; - paramTypes.add(parseType()); + paramTypes.add(parseType(scope)); } return paramTypes; } - + + /** + * Attention: Enters the lifetime names to the passed scope! + * @param scope + * @return + */ + public List parseOptionalLifetimeParameters(EnclosingScope scope) { + if (tryAndMatch(true, LeftAngle) != null && tryAndMatch(true, RightAngle) == null) { + // The if above skips an empty list of identifiers "<>"! + return parseLifetimeParameters(scope); + } + return Collections.emptyList(); + } + + /** + * Attention: Enters the lifetime names to the passed scope! + * Assumes that '<' has already been matched. + * @param scope + * @return + */ + private List parseLifetimeParameters(EnclosingScope scope) { + List lifetimeParameters = new ArrayList(); + do { + Token lifetimeIdentifier = match(Identifier); + scope.declareLifetime(lifetimeIdentifier); + lifetimeParameters.add(lifetimeIdentifier.text); + } while (tryAndMatch(true, Comma) != null); + match(RightAngle); + return lifetimeParameters; + } + + + /** + * @param scope + * @return + */ + public Set parseOptionalContextLifetimes(EnclosingScope scope) { + if (tryAndMatch(true, LeftSquare) != null && tryAndMatch(true, RightSquare) == null) { + // The if above skips an empty list of identifiers "[]"! + Set contextLifetimes = new HashSet(); + do { + contextLifetimes.add(parseLifetime(scope, true)); + } while (tryAndMatch(true, Comma) != null); + match(RightSquare); + return contextLifetimes; + } + return Collections.emptySet(); + } + public boolean mustParseAsMixedType() { int start = index; if (tryAndMatch(true, Function, Method) != null @@ -4030,6 +4264,44 @@ private Token tryAndMatch(boolean terminated, Token.Kind... kinds) { return null; } + /** + * Attempt to match a given sequence of tokens in the given order, whilst + * ignoring any whitespace in between. Note that, in any case, the index + * will be unchanged! + * + * @param terminated + * Indicates whether or not this function should be concerned + * with new lines. The terminated flag indicates whether or not + * the current construct being parsed is known to be terminated. + * If so, then we don't need to worry about newlines and can + * greedily consume them (i.e. since we'll eventually run into + * the terminating symbol). + * @param kinds + * + * @return whether the sequence matches + */ + private boolean lookaheadSequence(boolean terminated, Token.Kind... kinds) { + int next = index; + for (Token.Kind k : kinds) { + next = terminated ? skipWhiteSpace(next) : skipLineSpace(next); + if (next >= tokens.size() || tokens.get(next++).kind != k) { + return false; + } + } + return true; + } + + /** + * Check whether the current index is, after skipping all line spaces, at + * the end of a line. This method does not change the state! + * + * @return whether index is at end of line + */ + private boolean isAtEOL() { + int next = skipLineSpace(index); + return next >= tokens.size() || tokens.get(next).kind == NewLine; + } + /** * Attempt to match a given token on the *same* line, whilst ignoring any * whitespace in between. Note that, in the case it fails to match, then the @@ -4399,7 +4671,7 @@ public boolean equivalent(Indent other) { * @author David J. Pearce * */ - private static class EnclosingScope { + private class EnclosingScope { /** * The indent level of the enclosing scope. */ @@ -4408,7 +4680,20 @@ private static class EnclosingScope { /** * The set of declared variables in the enclosing scope. */ - private final HashSet environment; + private final HashSet variables; + + /** + * The set of declared lifetimes in the enclosing scope. + */ + private final HashSet lifetimes; + + /** + * The set of all names that cannot be used for variables or lifetimes. + * They are either in the variables or lifetimes set, or a special lifetime, + * or they are unavailable because it is an unaccessible lifetime from + * an outer scope. + */ + private final HashSet unavailableNames; /** * A simple flag that tells us whether or not we are currently within a @@ -4419,13 +4704,22 @@ private static class EnclosingScope { public EnclosingScope() { this.indent = ROOT_INDENT; - this.environment = new HashSet(); + this.variables = new HashSet(); + this.lifetimes = new HashSet(); + this.unavailableNames = new HashSet(); this.inLoop = false; + + // prevent declaring these lifetimes + this.unavailableNames.add("*"); + this.unavailableNames.add("this"); } - private EnclosingScope(Indent indent, Set environment, boolean inLoop) { + private EnclosingScope(Indent indent, Set variables, Set lifetimes, + Set unavailableNames, boolean inLoop) { this.indent = indent; - this.environment = new HashSet(environment); + this.variables = new HashSet(variables); + this.lifetimes = new HashSet(lifetimes); + this.unavailableNames = new HashSet(unavailableNames); this.inLoop = inLoop; } @@ -4436,7 +4730,7 @@ public Indent getIndent() { public boolean isInLoop() { return inLoop; } - + /** * Check whether a given name corresponds to a declared variable in this * scope. @@ -4444,19 +4738,119 @@ public boolean isInLoop() { * @param name * @return */ - public boolean contains(String name) { - return environment.contains(name); + public boolean isVariable(String name) { + return this.variables.contains(name); } - + /** - * Declare a new variable in this scope. + * Check whether a given name corresponds to a declared lifetime in this + * scope. * * @param name + * @return */ - public void add(String name) { - environment.add(name); + public boolean isLifetime(String name) { + return name.equals("*") || this.lifetimes.contains(name); } - + + /** + * Checks that the given identifier is a declared lifetime. + * + * @param id + * @throws SyntaxError + * if the given identifier is not a lifetime + */ + public void mustBeLifetime(Token id) { + if (!this.isLifetime(id.text)) { + syntaxError("use of undeclared lifetime", id); + } + } + + /** + * Check whether a given name is available, i.e. can be declared. + * + * @param id + * identifier that holds the name to check + * @throws SyntaxError + * if the name is unavailable (already declared) + */ + public void checkNameAvailable(Token id) { + if (this.unavailableNames.contains(id.text)) { + // name is not available! + syntaxError("name already declared", id); + } + } + + /** + * Check whether a given name is available, i.e. can be declared. + * + * @param p + * parameter that holds the name to check + * @throws SyntaxError + * if the name is unavailable (already declared) + */ + public void checkNameAvailable(Parameter p) { + if (this.unavailableNames.contains(p.name)) { + // name is not available! + syntaxError("name already declared", p); + } + } + + /** + * Declare a new variable in this scope. + * + * @param id + * identifier that holds the name to declare + * @throws SyntaxError + * if the name is already declared + */ + public void declareVariable(Token id) { + if (!this.unavailableNames.add(id.text)) { + // name is not available! + syntaxError("name already declared", id); + } + this.variables.add(id.text); + } + + /** + * Declare a new variable in this scope. + * + * @param p + * parameter that holds the name to declare + * @throws SyntaxError + * if the name is already declared + */ + public void declareVariable(Parameter p) { + if (!this.unavailableNames.add(p.name)) { + // name is not available! + syntaxError("name already declared", p); + } + this.variables.add(p.name); + } + + /** + * Declare a new lifetime in this scope. + * + * @param id + * identifier that holds the name to declare + * @throws SyntaxError + * if the name is already declared + */ + public void declareLifetime(Token id) { + if (!this.unavailableNames.add(id.text)) { + // name is not available! + syntaxError("name already declared", id); + } + this.lifetimes.add(id.text); + } + + /** + * Make lifetime "this" available. + */ + public void declareThisLifetime() { + this.lifetimes.add("this"); + } + /** * Create a new enclosing scope in which variables can be declared which * are remain invisible to this enclosing scope. All variables declared @@ -4468,7 +4862,7 @@ public void add(String name) { * @return */ public EnclosingScope newEnclosingScope() { - return new EnclosingScope(indent,environment,inLoop); + return new EnclosingScope(indent,variables,lifetimes,unavailableNames,inLoop); } /** @@ -4482,7 +4876,7 @@ public EnclosingScope newEnclosingScope() { * @return */ public EnclosingScope newEnclosingScope(Indent indent) { - return new EnclosingScope(indent,environment,inLoop); + return new EnclosingScope(indent,variables,lifetimes,unavailableNames,inLoop); } /** @@ -4496,7 +4890,21 @@ public EnclosingScope newEnclosingScope(Indent indent) { * @return */ public EnclosingScope newEnclosingScope(Indent indent, boolean inLoop) { - return new EnclosingScope(indent,environment,inLoop); + return new EnclosingScope(indent,variables,lifetimes,unavailableNames,inLoop); + } + + /** + * Create a new enclosing scope in which variables can be declared which + * are remain invisible to this enclosing scope. All variables declared + * in this enclosing scope remain declared in the new enclosing scope. + * + * @param indent + * the indent level for the new scope + * + * @return + */ + public EnclosingScope newEnclosingScope(Set contextLifetimes) { + return new EnclosingScope(indent,variables,contextLifetimes,unavailableNames,false); } } } diff --git a/modules/wyc/src/wyc/io/WhileyFilePrinter.java b/modules/wyc/src/wyc/io/WhileyFilePrinter.java index c24e0fe2b1..f86cad3bc9 100644 --- a/modules/wyc/src/wyc/io/WhileyFilePrinter.java +++ b/modules/wyc/src/wyc/io/WhileyFilePrinter.java @@ -160,6 +160,8 @@ public void print(Stmt stmt, int indent) { print((Stmt.Skip) stmt); } else if(stmt instanceof Stmt.Switch) { print((Stmt.Switch) stmt, indent); + } else if(stmt instanceof Stmt.NamedBlock) { + print((Stmt.NamedBlock) stmt, indent); } else if(stmt instanceof Stmt.While) { print((Stmt.While) stmt, indent); } else if(stmt instanceof Stmt.VariableDeclaration) { @@ -255,6 +257,11 @@ public void print(Stmt.DoWhile s, int indent) { out.println(); } + public void print(Stmt.NamedBlock s, int indent) { + out.println(s.name + ":"); + print(s.body,indent+1); + } + public void print(Stmt.While s, int indent) { out.print("while "); print(s.condition); @@ -455,6 +462,18 @@ public void print(Expr.AbstractInvoke e) { out.print("."); } out.print(e.name); + if (!e.lifetimeArguments.isEmpty()) { + out.print("<"); + boolean firstTime = true; + for (String lifetime : e.lifetimeArguments) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print(">"); + } out.print("("); boolean firstTime = true; for(Expr i : e.arguments) { @@ -469,6 +488,18 @@ public void print(Expr.AbstractInvoke e) { public void print(Expr.IndirectFunctionCall e) { print(e.src); + if (!e.lifetimeArguments.isEmpty()) { + out.print("<"); + boolean firstTime = true; + for (String lifetime : e.lifetimeArguments) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print(">"); + } out.print("("); boolean firstTime = true; for(Expr i : e.arguments) { @@ -483,6 +514,18 @@ public void print(Expr.IndirectFunctionCall e) { public void print(Expr.IndirectMethodCall e) { print(e.src); + if (!e.lifetimeArguments.isEmpty()) { + out.print("<"); + boolean firstTime = true; + for (String lifetime : e.lifetimeArguments) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print(">"); + } out.print("("); boolean firstTime = true; for(Expr i : e.arguments) { @@ -571,7 +614,32 @@ public void print(Expr.AbstractFunctionOrMethod e) { } public void print(Expr.Lambda e) { - out.print("&("); + out.print("&"); + if (!e.contextLifetimes.isEmpty()) { + out.print("["); + boolean firstTime = true; + for (String lifetime : e.contextLifetimes) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print("]"); + } + if (!e.lifetimeParameters.isEmpty()) { + out.print("<"); + boolean firstTime = true; + for (String lifetime : e.lifetimeParameters) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print(">"); + } + out.print("("); boolean firstTime = true; for(WhileyFile.Parameter p : e.parameters) { if(!firstTime) { @@ -666,6 +734,30 @@ public void print(SyntacticType t) { } else { out.print("function "); } + if (!tt.contextLifetimes.isEmpty()) { + out.print("["); + boolean firstTime = true; + for (String lifetime : tt.contextLifetimes) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print("]"); + } + if (!tt.lifetimeParameters.isEmpty()) { + out.print("<"); + boolean firstTime = true; + for (String lifetime : tt.lifetimeParameters) { + if (!firstTime) { + out.print(", "); + } + firstTime = false; + out.print(lifetime); + } + out.print(">"); + } printParameterTypes(tt.paramTypes); out.print("->"); printParameterTypes(tt.returnTypes); diff --git a/modules/wyc/src/wyc/lang/Expr.java b/modules/wyc/src/wyc/lang/Expr.java index 17c43059d6..bd9e622173 100755 --- a/modules/wyc/src/wyc/lang/Expr.java +++ b/modules/wyc/src/wyc/lang/Expr.java @@ -27,6 +27,7 @@ import java.util.*; +import wyc.builder.FlowTypeChecker; import wyc.io.WhileyFileLexer; import wycc.lang.Attribute; import wycc.lang.NameID; @@ -207,9 +208,11 @@ public Nominal result() { public static class AbstractFunctionOrMethod extends SyntacticElement.Impl implements Expr { public final String name; public final ArrayList paramTypes; + public final ArrayList lifetimeParameters; public Nominal.FunctionOrMethod type; - public AbstractFunctionOrMethod(String name, Collection paramTypes, Attribute... attributes) { + public AbstractFunctionOrMethod(String name, Collection paramTypes, + Collection lifetimeParameters, Attribute... attributes) { super(attributes); this.name = name; if(paramTypes != null) { @@ -217,10 +220,16 @@ public AbstractFunctionOrMethod(String name, Collection paramType } else { this.paramTypes = null; } + if(lifetimeParameters != null) { + this.lifetimeParameters = new ArrayList(lifetimeParameters); + } else { + this.lifetimeParameters = null; + } } public AbstractFunctionOrMethod(String name, Collection paramTypes, + Collection lifetimeParameters, Collection attributes) { super(attributes); this.name = name; @@ -229,6 +238,11 @@ public AbstractFunctionOrMethod(String name, } else { this.paramTypes = null; } + if(lifetimeParameters != null) { + this.lifetimeParameters = new ArrayList(lifetimeParameters); + } else { + this.lifetimeParameters = null; + } } public Nominal.FunctionOrMethod result() { @@ -240,34 +254,40 @@ public static class FunctionOrMethod extends AbstractFunctionOrMethod { public final NameID nid; public FunctionOrMethod(NameID nid, Collection paramTypes, - Attribute... attributes) { - super(nid.name(), paramTypes, attributes); + Collection lifetimeParameters, Attribute... attributes) { + super(nid.name(), paramTypes, lifetimeParameters, attributes); this.nid = nid; } public FunctionOrMethod(NameID nid, Collection paramTypes, - Collection attributes) { - super(nid.name(), paramTypes, attributes); + Collection lifetimeParameters, Collection attributes) { + super(nid.name(), paramTypes, lifetimeParameters, attributes); this.nid = nid; } } public static class Lambda extends SyntacticElement.Impl implements Expr { public final ArrayList parameters; + public final HashSet contextLifetimes; + public final ArrayList lifetimeParameters; public Expr body; public Nominal.FunctionOrMethod type; - public Lambda(Collection parameters, Expr body, - Attribute... attributes) { + public Lambda(Collection parameters, Collection contextLifetimes, + Collection lifetimeParameters, Expr body, Attribute... attributes) { super(attributes); this.parameters = new ArrayList(parameters); + this.contextLifetimes = new HashSet(contextLifetimes); + this.lifetimeParameters = new ArrayList(lifetimeParameters); this.body = body; } - public Lambda(Collection parameters, Expr body, - Collection attributes) { + public Lambda(Collection parameters, Collection contextLifetimes, + Collection lifetimeParameters, Expr body, Collection attributes) { super(attributes); this.parameters = new ArrayList(parameters); + this.contextLifetimes = new HashSet(contextLifetimes); + this.lifetimeParameters = new ArrayList(lifetimeParameters); this.body = body; } @@ -564,22 +584,34 @@ public static class AbstractInvoke extends SyntacticElement.Impl implements Expr public final String name; public Path.ID qualification; public final ArrayList arguments; + public final ArrayList lifetimeArguments; public AbstractInvoke(String name, Path.ID receiver, - Collection arguments, Attribute... attributes) { + Collection arguments, Collection lifetimeArguments, + Attribute... attributes) { super(attributes); this.name = name; this.qualification = receiver; this.arguments = new ArrayList(arguments); + if (lifetimeArguments != null) { + this.lifetimeArguments = new ArrayList(lifetimeArguments); + } else { + this.lifetimeArguments = null; + } } public AbstractInvoke(String name, Path.ID receiver, - Collection arguments, + Collection arguments, Collection lifetimeArguments, Collection attributes) { super(attributes); this.name = name; this.qualification = receiver; this.arguments = new ArrayList(arguments); + if (lifetimeArguments != null) { + this.lifetimeArguments = new ArrayList(lifetimeArguments); + } else { + this.lifetimeArguments = null; + } } public Nominal result() { @@ -591,14 +623,14 @@ public static abstract class FunctionOrMethodCall extends AbstractInvoke impleme public final NameID nid; public FunctionOrMethodCall(NameID nid, Path.ID qualification, Collection arguments, - Attribute... attributes) { - super(nid.name(),qualification,arguments,attributes); + Collection lifetimeArguments, Attribute... attributes) { + super(nid.name(),qualification,arguments,lifetimeArguments,attributes); this.nid = nid; } public FunctionOrMethodCall(NameID nid, Path.ID qualification, Collection arguments, - Collection attributes) { - super(nid.name(),qualification,arguments,attributes); + Collection lifetimeArguments, Collection attributes) { + super(nid.name(),qualification,arguments,lifetimeArguments,attributes); this.nid = nid; } @@ -617,13 +649,13 @@ public static class MethodCall extends FunctionOrMethodCall { public Nominal.Method methodType; public MethodCall(NameID nid, Path.ID qualification, Collection arguments, - Attribute... attributes) { - super(nid,qualification,arguments,attributes); + Collection lifetimeArguments, Attribute... attributes) { + super(nid,qualification,arguments,lifetimeArguments,attributes); } public MethodCall(NameID nid, Path.ID qualification, Collection arguments, - Collection attributes) { - super(nid,qualification,arguments,attributes); + Collection lifetimeArguments, Collection attributes) { + super(nid,qualification,arguments,lifetimeArguments,attributes); } public Nominal.Method type() { @@ -632,7 +664,11 @@ public Nominal.Method type() { public Nominal result() { if (methodType.returns().size() == 1) { - return methodType.returns().get(0); + Nominal returnType = methodType.returns().get(0); + if (this.lifetimeArguments == null || this.lifetimeArguments.isEmpty()) { + return returnType; + } + return FlowTypeChecker.applySubstitution(methodType.raw().lifetimeParams(), this.lifetimeArguments, returnType); } else { throw new IllegalArgumentException("incorrect number of returns for function call"); } @@ -654,12 +690,12 @@ public static class FunctionCall extends FunctionOrMethodCall { public FunctionCall(NameID nid, Path.ID qualification, Collection arguments, Attribute... attributes) { - super(nid,qualification,arguments,attributes); + super(nid,qualification,arguments,Collections.emptyList(),attributes); } public FunctionCall(NameID nid, Path.ID qualification, Collection arguments, Collection attributes) { - super(nid,qualification,arguments,attributes); + super(nid,qualification,arguments,Collections.emptyList(),attributes); } public Nominal.Function type() { @@ -679,21 +715,26 @@ public static class AbstractIndirectInvoke extends SyntacticElement.Impl impleme Stmt { public Expr src; public final ArrayList arguments; + public final ArrayList lifetimeArguments; public AbstractIndirectInvoke(Expr src, Collection arguments, + Collection lifetimeArguments, Attribute... attributes) { super(attributes); this.src = src; this.arguments = new ArrayList(arguments); + this.lifetimeArguments = lifetimeArguments == null ? null : new ArrayList(lifetimeArguments); } public AbstractIndirectInvoke(Expr src, Collection arguments, + Collection lifetimeArguments, Collection attributes) { super(attributes); this.src = src; this.arguments = new ArrayList(arguments); + this.lifetimeArguments = lifetimeArguments == null ? null : new ArrayList(lifetimeArguments); } public Nominal result() { @@ -703,13 +744,13 @@ public Nominal result() { public static abstract class IndirectFunctionOrMethodCall extends AbstractIndirectInvoke implements Multi { public IndirectFunctionOrMethodCall(Expr src, Collection arguments, - Attribute... attributes) { - super(src,arguments,attributes); + Collection lifetimeArguments, Attribute... attributes) { + super(src,arguments,lifetimeArguments,attributes); } public IndirectFunctionOrMethodCall(Expr src, Collection arguments, - Collection attributes) { - super(src,arguments,attributes); + Collection lifetimeArguments, Collection attributes) { + super(src,arguments,lifetimeArguments,attributes); } public abstract Nominal.FunctionOrMethod type(); @@ -723,18 +764,22 @@ public static class IndirectMethodCall extends IndirectFunctionOrMethodCall { public Nominal.Method methodType; public IndirectMethodCall(Expr src, Collection arguments, - Attribute... attributes) { - super(src,arguments,attributes); + Collection lifetimeArguments, Attribute... attributes) { + super(src,arguments,lifetimeArguments,attributes); } public IndirectMethodCall(Expr src, Collection arguments, - Collection attributes) { - super(src,arguments,attributes); + Collection lifetimeArguments, Collection attributes) { + super(src,arguments,lifetimeArguments,attributes); } public Nominal result() { if(methodType.returns().size() == 1) { - return methodType.returns().get(0); + Nominal returnType = methodType.returns().get(0); + if (this.lifetimeArguments == null || this.lifetimeArguments.isEmpty()) { + return returnType; + } + return FlowTypeChecker.applySubstitution(methodType.raw().lifetimeParams(), this.lifetimeArguments, returnType); } else { throw new IllegalArgumentException("incorrect number of returns for indirect method call"); } @@ -750,12 +795,12 @@ public static class IndirectFunctionCall extends IndirectFunctionOrMethodCall { public IndirectFunctionCall(Expr src, Collection arguments, Attribute... attributes) { - super(src,arguments,attributes); + super(src,arguments,Collections.emptyList(),attributes); } public IndirectFunctionCall(Expr src, Collection arguments, Collection attributes) { - super(src,arguments,attributes); + super(src,arguments,Collections.emptyList(),attributes); } public Nominal result() { @@ -774,9 +819,11 @@ public Nominal.FunctionOrMethod type() { public static class New extends SyntacticElement.Impl implements Expr,Stmt { public Expr expr; public Nominal.Reference type; + public String lifetime; - public New(Expr expr, Attribute... attributes) { + public New(Expr expr, String lifetime, Attribute... attributes) { super(attributes); + this.lifetime = lifetime; this.expr = expr; } diff --git a/modules/wyc/src/wyc/lang/Nominal.java b/modules/wyc/src/wyc/lang/Nominal.java index 34ab1b19ed..b31f0d0585 100755 --- a/modules/wyc/src/wyc/lang/Nominal.java +++ b/modules/wyc/src/wyc/lang/Nominal.java @@ -56,9 +56,9 @@ public static Nominal intersect(Nominal lhs, Nominal rhs) { return Nominal.construct(nominal, raw); } - public static Reference Reference(Nominal element) { - Type.Reference nominal = Type.Reference(element.nominal()); - Type.Reference raw = Type.Reference(element.raw()); + public static Reference Reference(Nominal element, String lifetime) { + Type.Reference nominal = Type.Reference(element.nominal(), lifetime); + Type.Reference raw = Type.Reference(element.raw(), lifetime); return new Reference(nominal,raw); } diff --git a/modules/wyc/src/wyc/lang/Stmt.java b/modules/wyc/src/wyc/lang/Stmt.java index a7ac25abac..8fb618cbe9 100755 --- a/modules/wyc/src/wyc/lang/Stmt.java +++ b/modules/wyc/src/wyc/lang/Stmt.java @@ -260,6 +260,59 @@ public String toString() { } } + /** + * Represents a named block, which has the form: + * + *
+	 * NamedBlcok ::= LifetimeIdentifier ':' NewLine Block
+	 * 
+ * + * As an example: + * + *
+	 * function sum():
+	 *   &this:int x = new:this x
+	 *   myblock:
+	 *     &myblock:int y = new:myblock y
+	 * 
+ */ + public static final class NamedBlock extends SyntacticElement.Impl implements Stmt { + public final String name; + public final ArrayList body; + + /** + * Construct a named block from a given name and body of statements. + * + * @param name + * name of this named block. + * @param body + * non-null collection which contains zero or more + * statements. + * @param attributes + */ + public NamedBlock(String name, Collection body, Attribute... attributes) { + super(attributes); + this.name = name; + this.body = new ArrayList(body); + } + + /** + * Construct a named block from a given name and body of statements. + * + * @param name + * name of this named block. + * @param body + * non-null collection which contains zero or more + * statements. + * @param attributes + */ + public NamedBlock(String name, Collection body, Collection attributes) { + super(attributes); + this.name = name; + this.body = new ArrayList(body); + } + } + /** * Represents a while statement, which has the form: * diff --git a/modules/wyc/src/wyc/lang/SyntacticType.java b/modules/wyc/src/wyc/lang/SyntacticType.java index ff008fc97b..61e20fb393 100755 --- a/modules/wyc/src/wyc/lang/SyntacticType.java +++ b/modules/wyc/src/wyc/lang/SyntacticType.java @@ -291,8 +291,12 @@ public Intersection(ArrayList bounds, */ public static final class Reference extends SyntacticElement.Impl implements NonUnion { public final SyntacticType element; - public Reference(SyntacticType element, Attribute... attributes) { + public final String lifetime; + public final boolean lifetimeWasExplicit; + public Reference(SyntacticType element, String lifetime, boolean lifetimeWasExplicit, Attribute... attributes) { this.element = element; + this.lifetime = lifetime; + this.lifetimeWasExplicit = lifetimeWasExplicit; } } @@ -338,12 +342,16 @@ public abstract static class FunctionOrMethod extends SyntacticElement.Impl implements NonUnion { public final ArrayList returnTypes; public final ArrayList paramTypes; + public final ArrayList contextLifetimes; + public final ArrayList lifetimeParameters; public FunctionOrMethod(Collection returnTypes, Collection paramTypes, - Attribute... attributes) { + Collection contextLifetimes, Collection lifetimeParameters, Attribute... attributes) { super(attributes); this.returnTypes = new ArrayList(returnTypes); this.paramTypes = new ArrayList(paramTypes); + this.contextLifetimes = new ArrayList(contextLifetimes); + this.lifetimeParameters = new ArrayList(lifetimeParameters); for(SyntacticType t : paramTypes) { if(t == null) { throw new IllegalArgumentException("parameter cannot be null"); @@ -354,13 +362,20 @@ public FunctionOrMethod(Collection returnTypes, Collection returnTypes, Collection paramTypes, - Collection attributes) { + Collection contextLifetimes, Collection lifetimeParameters, Collection attributes) { super(attributes); this.returnTypes = new ArrayList(returnTypes); this.paramTypes = new ArrayList(paramTypes); + this.contextLifetimes = new ArrayList(contextLifetimes); + this.lifetimeParameters = new ArrayList(lifetimeParameters); for(SyntacticType t : paramTypes) { if(t == null) { throw new IllegalArgumentException("parameter cannot be null"); @@ -371,6 +386,11 @@ public FunctionOrMethod(Collection returnTypes, Collection returnTypes, Collection paramTypes, Attribute... attributes) { - super(returnTypes,paramTypes,attributes); + super(returnTypes,paramTypes,Collections.emptySet(),Collections.emptyList(),attributes); } public Function(Collection returnTypes, Collection paramTypes, Collection attributes) { - super(returnTypes,paramTypes,attributes); + super(returnTypes,paramTypes,Collections.emptySet(),Collections.emptyList(),attributes); } } public static class Method extends FunctionOrMethod implements NonUnion { - public Method(Collection returnTypes, Collection paramTypes, - Attribute... attributes) { - super(returnTypes,paramTypes,attributes); + public Method(Collection returnTypes, Collection paramTypes, + Collection contextLifetimes, Collection lifetimeParameters, Attribute... attributes) { + super(returnTypes,paramTypes,contextLifetimes,lifetimeParameters,attributes); } public Method(Collection returnTypes, Collection paramTypes, - Collection attributes) { - super(returnTypes,paramTypes,attributes); + Collection contextLifetimes, Collection lifetimeParameters, Collection attributes) { + super(returnTypes,paramTypes,contextLifetimes,lifetimeParameters,attributes); } } } diff --git a/modules/wyc/src/wyc/lang/WhileyFile.java b/modules/wyc/src/wyc/lang/WhileyFile.java index ecc75799c2..503549e8a9 100755 --- a/modules/wyc/src/wyc/lang/WhileyFile.java +++ b/modules/wyc/src/wyc/lang/WhileyFile.java @@ -422,6 +422,7 @@ public Type(List modifiers, Parameter type, */ public abstract class FunctionOrMethod extends NamedDeclaration { public final ArrayList parameters; + public final ArrayList lifetimeParameters; public final ArrayList returns; public final ArrayList statements; public List requires; @@ -446,12 +447,14 @@ public abstract class FunctionOrMethod extends NamedDeclaration { */ public FunctionOrMethod(List modifiers, String name, List returns, List parameters, + List lifetimeParameters, List requires, List ensures, List statements, Attribute... attributes) { super(name, modifiers,attributes); this.returns = new ArrayList(returns); this.parameters = new ArrayList(parameters); + this.lifetimeParameters = lifetimeParameters == null ? new ArrayList() : new ArrayList(lifetimeParameters); this.requires = new ArrayList(requires); this.ensures = new ArrayList(ensures); this.statements = new ArrayList(statements); @@ -503,8 +506,7 @@ public Function(List modifiers, String name, List returns, List parameters, List requires, List ensures, List statements, Attribute... attributes) { - super(modifiers, name, returns, parameters, requires, ensures, - statements, attributes); + super(modifiers, name, returns, parameters, null, requires, ensures, statements, attributes); } public SyntacticType.Function unresolvedType() { @@ -560,8 +562,9 @@ public final class Method extends FunctionOrMethod { public Nominal.Method resolvedType; public Method(List modifiers, String name, List returns, List parameters, - List requires, List ensures, List statements, Attribute... attributes) { - super(modifiers, name, returns, parameters, requires, ensures, statements, attributes); + List lifetimeParameters, List requires, List ensures, + List statements, Attribute... attributes) { + super(modifiers, name, returns, parameters, lifetimeParameters, requires, ensures, statements, attributes); } public SyntacticType.Method unresolvedType() { @@ -573,7 +576,8 @@ public SyntacticType.Method unresolvedType() { for (Parameter r : returns) { returnTypes.add(r.type); } - return new SyntacticType.Method(returnTypes, parameterTypes, attributes()); + return new SyntacticType.Method(returnTypes, parameterTypes, + Collections.emptySet(), lifetimeParameters, attributes()); } public Nominal.Method resolvedType() { diff --git a/modules/wyc/src/wyc/testing/AllInvalidTests.java b/modules/wyc/src/wyc/testing/AllInvalidTests.java index 5aea055c2e..8904e3d7bf 100755 --- a/modules/wyc/src/wyc/testing/AllInvalidTests.java +++ b/modules/wyc/src/wyc/testing/AllInvalidTests.java @@ -198,7 +198,7 @@ protected void runTest(String name) { fail("Test compiled when it shouldn't have!"); } else if (r == WycMain.INTERNAL_FAILURE) { // This indicates some other kind of internal failure. - fail("Test caused internal failure!"); + fail("Test caused internal failure!\n" + output); } else { // Now, let's check the expected output against the file which // contains the sample output for this test diff --git a/modules/wyc/src/wyc/testing/AllValidVerificationTests.java b/modules/wyc/src/wyc/testing/AllValidVerificationTests.java index 327be731d7..b3ace5b91a 100644 --- a/modules/wyc/src/wyc/testing/AllValidVerificationTests.java +++ b/modules/wyc/src/wyc/testing/AllValidVerificationTests.java @@ -121,6 +121,7 @@ public class AllValidVerificationTests { IGNORED.put("Lambda_Valid_3", "#344"); IGNORED.put("Lambda_Valid_4", "#344"); IGNORED.put("Lambda_Valid_7", "#344"); + IGNORED.put("Lifetime_Lambda_Valid_4", "#298"); IGNORED.put("ListAccess_Valid_6", "Known Issue"); IGNORED.put("ListAssign_Valid_1", "#233"); IGNORED.put("ListAssign_Valid_6", "#233"); diff --git a/modules/wyc/src/wyc/testing/TestUtils.java b/modules/wyc/src/wyc/testing/TestUtils.java index 75f8b79f75..5c24ec792c 100644 --- a/modules/wyc/src/wyc/testing/TestUtils.java +++ b/modules/wyc/src/wyc/testing/TestUtils.java @@ -102,7 +102,8 @@ public static Pair compile(String... args) { * @throws IOException */ public static void execWyil(String wyilDir, Path.ID id) throws IOException { - Type.Method sig = Type.Method(Collections.EMPTY_LIST, Collections.EMPTY_LIST); + Type.Method sig = Type.Method(Collections.emptyList(), Collections.emptySet(), + Collections.emptyList(), Collections.emptyList()); NameID name = new NameID(id,"test"); Build.Project project = initialiseProject(wyilDir); new Interpreter(project,null).execute(name,sig); diff --git a/modules/wyil/src/wyil/Main.java b/modules/wyil/src/wyil/Main.java index 7267717b52..381e3df2dc 100644 --- a/modules/wyil/src/wyil/Main.java +++ b/modules/wyil/src/wyil/Main.java @@ -89,7 +89,8 @@ public static void main(String[] args) { WyilFile wf = new WyilFileReader(args[0]).read(); new WyilFilePrinter(System.out).apply(wf); // FIXME: this is all a hack for now - Type.Method sig = Type.Method(Collections.EMPTY_LIST, Collections.EMPTY_LIST); + Type.Method sig = Type.Method(Collections.emptyList(), Collections.emptySet(), + Collections.emptyList(), Collections.emptyList()); NameID name = new NameID(wf.id(),"test"); Build.Project project = initialiseProject("."); Constant[] returns = new Interpreter(project,System.out).execute(name,sig); diff --git a/modules/wyil/src/wyil/lang/Type.java b/modules/wyil/src/wyil/lang/Type.java index 9d50edb152..0b79d75a9b 100755 --- a/modules/wyil/src/wyil/lang/Type.java +++ b/modules/wyil/src/wyil/lang/Type.java @@ -83,7 +83,7 @@ public abstract class Type { // the following are strictly unnecessary, but since they occur very // commonly it is helpful to provide them as constants. - public static final Reference T_REF_ANY = Reference(T_ANY); + // public static final Reference T_REF_ANY = Reference(T_ANY); /** * The type representing all possible list types. @@ -95,8 +95,8 @@ public abstract class Type { * * @param element */ - public static final Type.Reference Reference(Type element) { - Type r = construct(K_REFERENCE, null, element); + public static final Type.Reference Reference(Type element, String lifetime) { + Type r = construct(K_REFERENCE, lifetime, element); if (r instanceof Type.Reference) { return (Type.Reference) r; } else { @@ -163,8 +163,10 @@ public static final Type.Function Function(List returns, } for(int i=0;i!=returns.size();++i) { rparams[i+params_size] = returns.get(i); - } - Type r = construct(K_FUNCTION, params_size, rparams); + } + Type.FunctionOrMethod.Data data = new Type.FunctionOrMethod.Data(params_size, + Collections.emptySet(), Collections.emptyList()); + Type r = construct(K_FUNCTION, data, rparams); if (r instanceof Type.Function) { return (Type.Function) r; } else { @@ -183,7 +185,9 @@ public static final Type.Function Function(Type[] returns, Type[] rparams = new Type[params.length+returns.length]; System.arraycopy(params, 0, rparams, 0, params.length); System.arraycopy(returns, 0, rparams, params.length, returns.length); - Type r = construct(K_FUNCTION, params.length, rparams); + Type.FunctionOrMethod.Data data = new Type.FunctionOrMethod.Data(params.length, + Collections.emptySet(), Collections.emptyList()); + Type r = construct(K_FUNCTION, data, rparams); if (r instanceof Type.Function) { return (Type.Function) r; } else { @@ -197,7 +201,8 @@ public static final Type.Function Function(Type[] returns, * * @param element */ - public static final Type.Method Method(List returns, List params) { + public static final Type.Method Method(List returns, Set contextLifetimes, + List lifetimeParameters, List params) { Type[] rparams = new Type[params.size()+returns.size()]; int params_size = params.size(); for(int i=0;i!=params_size;++i) { @@ -205,8 +210,10 @@ public static final Type.Method Method(List returns, List params) { } for(int i=0;i!=returns.size();++i) { rparams[i+params_size] = returns.get(i); - } - Type r = construct(K_METHOD, params_size, rparams); + } + Type.FunctionOrMethod.Data data = new Type.FunctionOrMethod.Data(params_size, + contextLifetimes, lifetimeParameters); + Type r = construct(K_METHOD, data, rparams); if (r instanceof Type.Method) { return (Type.Method) r; } else { @@ -220,11 +227,14 @@ public static final Type.Method Method(List returns, List params) { * * @param element */ - public static final Type.Method Method(Type[] returns, Type... params) { + public static final Type.Method Method(Type[] returns, Set contextLifetimes, + List lifetimeParameters, Type... params) { Type[] rparams = new Type[params.length+returns.length]; System.arraycopy(params, 0, rparams, 0, params.length); System.arraycopy(returns, 0, rparams, params.length, returns.length); - Type r = construct(K_METHOD, params.length, rparams); + Type.FunctionOrMethod.Data data = new Type.FunctionOrMethod.Data(params.length, + contextLifetimes, lifetimeParameters); + Type r = construct(K_METHOD, data, rparams); if (r instanceof Type.Method) { return (Type.Method) r; } else { @@ -400,22 +410,36 @@ public Automaton.State readState() throws IOException { fields.add(readString()); } state.data = fields; - } else if(state.kind == Type.K_LIST || state.kind == Type.K_SET) { + } else if(state.kind == Type.K_REFERENCE) { + state.data = readString(); // lifetime + } else if(state.kind == Type.K_LIST || state.kind == Type.K_SET) { boolean nonEmpty = reader.read_bit(); state.data = nonEmpty; } else if(state.kind == Type.K_FUNCTION || state.kind == Type.K_METHOD) { + ArrayList contextLifetimes = readStringList(); + ArrayList lifetimeParameters = readStringList(); int numParameters = reader.read_uv(); - state.data = numParameters; + state.data = new Type.FunctionOrMethod.Data(numParameters, + new HashSet(contextLifetimes), lifetimeParameters); } return state; } private String readString() throws IOException { - String r = ""; + StringBuilder r = new StringBuilder(); int nchars = reader.read_uv(); for(int i=0;i!=nchars;++i) { char c = (char) reader.read_u16(); - r = r + c; + r.append(c); + } + return r.toString(); + } + + private ArrayList readStringList() throws IOException { + int len = reader.read_uv(); + ArrayList r = new ArrayList(len); + for(int i=0;i!=len;++i) { + r.add(readString()); } return r; } @@ -456,10 +480,15 @@ public void write(Automaton.State state) throws IOException { for(String field : fields) { writeString(field); } + } else if(state.kind == Type.K_REFERENCE) { + writeString((String) state.data); // lifetime } else if(state.kind == Type.K_LIST || state.kind == Type.K_SET) { writer.write_bit((Boolean) state.data); } else if(state.kind == Type.K_FUNCTION || state.kind == Type.K_METHOD) { - writer.write_uv((Integer) state.data); + Type.FunctionOrMethod.Data data = (Type.FunctionOrMethod.Data) state.data; + writeStringList(data.contextLifetimes); + writeStringList(data.lifetimeParameters); + writer.write_uv(data.numParams); } } @@ -469,6 +498,13 @@ private void writeString(String str) throws IOException { writer.write_u16(str.charAt(i)); } } + + private void writeStringList(List strl) throws IOException { + writer.write_uv(strl.size()); + for (String str : strl) { + writeString(str); + } + } } // ============================================================= @@ -479,26 +515,44 @@ private void writeString(String str) throws IOException { * Determine whether type t2 is an explicit coercive * subtype of type t1. */ - public static boolean isExplicitCoerciveSubtype(Type t1, Type t2) { + public static boolean isExplicitCoerciveSubtype(Type t1, Type t2, LifetimeRelation lr) { Automaton a1 = destruct(t1); Automaton a2 = destruct(t2); - ExplicitCoercionOperator relation = new ExplicitCoercionOperator(a1,a2); + ExplicitCoercionOperator relation = new ExplicitCoercionOperator(a1,a2,lr); return relation.isSubtype(0, 0); } + /** + * Determine whether type t2 is an explicit coercive + * subtype of type t1. + */ + public static boolean isExplicitCoerciveSubtype(Type t1, Type t2) { + return isExplicitCoerciveSubtype(t1, t2, LifetimeRelation.EMPTY); + } + /** * Determine whether type t2 is a subtype of type * t1 (written t1 :> t2). In other words, whether the set of * all possible values described by the type t2 is a subset of * that described by t1. */ - public static boolean isSubtype(Type t1, Type t2) { + public static boolean isSubtype(Type t1, Type t2, LifetimeRelation lr) { Automaton a1 = destruct(t1); Automaton a2 = destruct(t2); - SubtypeOperator relation = new SubtypeOperator(a1,a2); + SubtypeOperator relation = new SubtypeOperator(a1,a2,lr); return relation.isSubtype(0, 0); } + /** + * Determine whether type t2 is a subtype of type + * t1 (written t1 :> t2). In other words, whether the set of + * all possible values described by the type t2 is a subset of + * that described by t1. + */ + public static boolean isSubtype(Type t1, Type t2) { + return isSubtype(t1, t2, LifetimeRelation.EMPTY); + } + /** *

* Contractive types are types which cannot accept value because they have @@ -874,6 +928,9 @@ public Type element() { int elemIdx = automaton.states[0].children[0]; return construct(Automata.extract(automaton,elemIdx)); } + public String lifetime() { + return (String) automaton.states[0].data; + } } /** @@ -1154,16 +1211,79 @@ public abstract static class FunctionOrMethod extends Compound { FunctionOrMethod(Automaton automaton) { super(automaton); } - + + public static final class Data implements Comparable { + public final int numParams; + public final List contextLifetimes; + public final List lifetimeParameters; + + public Data(int numParams, Set contextLifetimes, List lifetimeParameters) { + this.numParams = numParams; + this.contextLifetimes = new ArrayList(contextLifetimes); + this.contextLifetimes.remove("*"); + Collections.sort(this.contextLifetimes); + this.lifetimeParameters = new ArrayList(lifetimeParameters); + } + + @Override + public int hashCode() { + return 31 * (31 * numParams + contextLifetimes.hashCode()) + lifetimeParameters.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (this.getClass() != obj.getClass()) return false; + Data other = (Data) obj; + if (this.numParams != other.numParams) return false; + if (!this.contextLifetimes.equals(other.contextLifetimes)) { + return false; + } + if (!this.lifetimeParameters.equals(other.lifetimeParameters)) { + return false; + } + return true; + } + + @Override + public int compareTo(Data other) { + int r = Integer.compare(this.numParams, other.numParams); + if (r != 0) return r; + r = compareLists(this.contextLifetimes, other.contextLifetimes); + if (r != 0) return r; + return compareLists(this.lifetimeParameters, other.lifetimeParameters); + } + + private static int compareLists(List my, List other) { + Iterator it1 = my.iterator(); + Iterator it2 = other.iterator(); + while (true) { + if (it1.hasNext()) { + if (it2.hasNext()) { + int r = it1.next().compareTo(it2.next()); + if (r != 0) return r; + } else { + return 1; + } + } else if (it2.hasNext()) { + return -1; + } else { + return 0; + } + } + } + } + /** - * Get the parameter types of this function or method type. + * Get the return types of this function or method type. * * @return */ public ArrayList returns() { Automaton.State state = automaton.states[0]; int[] fields = state.children; - int numParams = (Integer) state.data; + int numParams = ((Data) state.data).numParams; ArrayList r = new ArrayList(); for(int i=numParams;i returns() { public ArrayList params() { Automaton.State state = automaton.states[0]; int[] fields = state.children; - int numParams = (Integer) state.data; + int numParams = ((Data) state.data).numParams; ArrayList r = new ArrayList(); for(int i=0;i contextLifetimes() { + Automaton.State state = automaton.states[0]; + Data data = (Data) state.data; + return new LinkedHashSet(data.contextLifetimes); + } + + /** + * Get the lifetime parameters of this function or method type. + * + * @return + */ + public ArrayList lifetimeParams() { + Automaton.State state = automaton.states[0]; + Data data = (Data) state.data; + return new ArrayList(data.lifetimeParameters); + } } /** @@ -1303,7 +1445,12 @@ private final static String toString(int index, BitSet visited, middle = state.data.toString(); break; case K_REFERENCE: - middle = "&" + toString(state.children[0], visited, headers, automaton); + middle = "&"; + String lifetime = (String) state.data; + if (!"*".equals(lifetime)) { + middle += lifetime + ":"; + } + middle += toString(state.children[0], visited, headers, automaton); break; case K_NEGATION: { middle = "!" + toBracesString(state.children[0], visited, headers, automaton); @@ -1364,8 +1511,9 @@ private final static String toString(int index, BitSet visited, case K_METHOD: case K_FUNCTION: { String parameters = ""; - int[] children = state.children; ; - int numParameters = (Integer) state.data; + int[] children = state.children; + Type.FunctionOrMethod.Data data = (Type.FunctionOrMethod.Data) state.data; + int numParameters = data.numParams; for (int i = 0; i != numParameters; ++i) { if (i!=0) { parameters += ","; @@ -1379,11 +1527,40 @@ private final static String toString(int index, BitSet visited, } returns += toString(children[i], visited, headers, automaton); } - if(state.kind == K_FUNCTION) { - middle = "function(" + parameters + ")->(" + returns + ")"; - } else { - middle = "method(" + parameters + ")->(" + returns + ")"; - } + StringBuilder sb = new StringBuilder(); + sb.append(state.kind == K_FUNCTION ? "function" : "method"); + if (!data.contextLifetimes.isEmpty()) { + sb.append('['); + boolean first = true; + for (String l : data.contextLifetimes) { + if (!first) { + sb.append(','); + } else { + first = false; + } + sb.append(l); + } + sb.append(']'); + } + if (!data.lifetimeParameters.isEmpty()) { + sb.append('<'); + boolean first = true; + for (String l : data.lifetimeParameters) { + if (!first) { + sb.append(','); + } else { + first = false; + } + sb.append(l); + } + sb.append('>'); + } + sb.append('('); + sb.append(parameters); + sb.append(")->("); + sb.append(returns); + sb.append(')'); + middle = sb.toString(); break; } default: @@ -1764,33 +1941,33 @@ private static T get(T type) { } } - public static void main(String[] args) { - //Type from = fromString("(null,null)"); - //Type to = fromString("X<[X]>"); - Type from = fromString("!(!{int x,int z} | !{int x,int y})"); - Type to = fromString("{string name,...}"); - System.out.println(from + " :> " + to + " = " + isSubtype(from, to)); - System.out.println(from + " & " + to + " = " + intersect(from,to)); - //System.out.println(from + " - " + to + " = " + intersect(from,Type.Negation(to))); - //System.out.println(to + " - " + from + " = " + intersect(to,Type.Negation(from))); - //System.out.println("!" + from + " & !" + to + " = " - // + intersect(Type.Negation(from), Type.Negation(to))); - } - - public static Type linkedList(int n) { - NameID label = new NameID(Trie.fromString(""),"X"); - return Recursive(label,innerLinkedList(n)); - } - - public static Type innerLinkedList(int n) { - if(n == 0) { - return Nominal(new NameID(Trie.fromString(""),"X")); - } else { - Type leaf = Reference(innerLinkedList(n-1)); - HashMap fields = new HashMap(); - fields.put("next", Union(T_NULL,leaf)); - fields.put("data", T_BOOL); - return Record(false,fields); - } - } +// public static void main(String[] args) { +// //Type from = fromString("(null,null)"); +// //Type to = fromString("X<[X]>"); +// Type from = fromString("!(!{int x,int z} | !{int x,int y})"); +// Type to = fromString("{string name,...}"); +// System.out.println(from + " :> " + to + " = " + isSubtype(from, to)); +// System.out.println(from + " & " + to + " = " + intersect(from,to)); +// //System.out.println(from + " - " + to + " = " + intersect(from,Type.Negation(to))); +// //System.out.println(to + " - " + from + " = " + intersect(to,Type.Negation(from))); +// //System.out.println("!" + from + " & !" + to + " = " +// // + intersect(Type.Negation(from), Type.Negation(to))); +// } +// +// public static Type linkedList(int n) { +// NameID label = new NameID(Trie.fromString(""),"X"); +// return Recursive(label,innerLinkedList(n)); +// } +// +// public static Type innerLinkedList(int n) { +// if(n == 0) { +// return Nominal(new NameID(Trie.fromString(""),"X")); +// } else { +// Type leaf = Reference(innerLinkedList(n-1)); +// HashMap fields = new HashMap(); +// fields.put("next", Union(T_NULL,leaf)); +// fields.put("data", T_BOOL); +// return Record(false,fields); +// } +// } } diff --git a/modules/wyil/src/wyil/util/TypeExpander.java b/modules/wyil/src/wyil/util/TypeExpander.java index d04b421634..5cae2e2f8c 100644 --- a/modules/wyil/src/wyil/util/TypeExpander.java +++ b/modules/wyil/src/wyil/util/TypeExpander.java @@ -237,7 +237,7 @@ public int getTypeHelper(Type type, boolean maximallyConsumed, for(int i=0;i!=tt_returns_size;++i) { myChildren[i+tt_params_size] = getTypeHelper(tt_returns.get(i),maximallyConsumed,states,roots); } - myData = tt_params_size; + myData = new Type.FunctionOrMethod.Data(tt_params_size, tt.contextLifetimes(), tt.lifetimeParams()); myKind = tt instanceof Type.Function ? Type.K_FUNCTION : Type.K_METHOD; }else { diff --git a/modules/wyil/src/wyil/util/interpreter/Interpreter.java b/modules/wyil/src/wyil/util/interpreter/Interpreter.java index 264c74faf0..216ebe6463 100644 --- a/modules/wyil/src/wyil/util/interpreter/Interpreter.java +++ b/modules/wyil/src/wyil/util/interpreter/Interpreter.java @@ -299,8 +299,12 @@ private Constant convert(Constant value, Type to, Context context) { // In this case, we don't need to do anything because the value is // already of the correct type. return value; - } - if (to instanceof Type.Record) { + } else if (type instanceof Type.Reference && to instanceof Type.Reference) { + if (Type.isSubtype(((Type.Reference) to).element(), ((Type.Reference) type).element())) { + // OK, it's just the lifetime that differs. + return value; + } + } else if (to instanceof Type.Record) { return convert(value, (Type.Record) to, context); } else if (to instanceof Type.Array) { return convert(value, (Type.Array) to, context); @@ -941,7 +945,8 @@ public int compareTo(Constant o) { @Override public wyil.lang.Type type() { - return wyil.lang.Type.Reference(value.type()); + // TODO: extend wyil.lang.Codes.NewObject with a lifetime and use it + return wyil.lang.Type.Reference(value.type(), "*"); } } diff --git a/modules/wyil/src/wyil/util/type/ExplicitCoercionOperator.java b/modules/wyil/src/wyil/util/type/ExplicitCoercionOperator.java index 06c1501b51..2d1933ac2b 100644 --- a/modules/wyil/src/wyil/util/type/ExplicitCoercionOperator.java +++ b/modules/wyil/src/wyil/util/type/ExplicitCoercionOperator.java @@ -59,8 +59,8 @@ */ public class ExplicitCoercionOperator extends SubtypeOperator { - public ExplicitCoercionOperator(Automaton fromAutomata, Automaton toAutomata) { - super(fromAutomata,toAutomata); + public ExplicitCoercionOperator(Automaton fromAutomata, Automaton toAutomata, LifetimeRelation lr) { + super(fromAutomata,toAutomata,lr); } @Override diff --git a/modules/wyil/src/wyil/util/type/LifetimeRelation.java b/modules/wyil/src/wyil/util/type/LifetimeRelation.java new file mode 100644 index 0000000000..71831cf0f9 --- /dev/null +++ b/modules/wyil/src/wyil/util/type/LifetimeRelation.java @@ -0,0 +1,140 @@ +package wyil.util.type; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Stack; + +/** + * This relation tracks the partial order among lifetimes. + */ +public class LifetimeRelation { + + /** + * These are all lifetime parameters to the current method. There is no + * order between lifetimes in this set. + */ + private final Set parameters; + + /** + * These are lifetimes declared by named blocks. These lifetimes are totally + * ordered according to their position in the stack. Each lifetime in + * {@link #parameters} outlives all block lifetimes. + */ + private final Stack blocks; + + /** + * Create a new and empty lifetime relation. + */ + public LifetimeRelation() { + this.parameters = new HashSet(); + this.blocks = new Stack(); + } + + /** + * Create an independent copy of the given lifetime relation. + * + * @param lifetimeRelation + */ + public LifetimeRelation(LifetimeRelation lifetimeRelation) { + this.parameters = new HashSet(lifetimeRelation.parameters); + this.blocks = new Stack(); + this.blocks.addAll(lifetimeRelation.blocks); + } + + /** + * Check whether the first (outer) lifetime outlives the second (inner) + * lifetime. + * + * @param outerLifetime + * @param innerLifetime + * @return + */ + public boolean outlives(String outerLifetime, String innerLifetime) { + if (outerLifetime.equals("*")) { + // * outlives everything + return true; + } + + if (outerLifetime.equals(innerLifetime)) { + return true; + } + + // Check whether the inner lifetime is from a named block. + int innerIndex = this.blocks.indexOf(innerLifetime); + if (innerIndex != -1) { + // All parameters are ordered before blocks. + if (this.parameters.contains(outerLifetime)) { + return true; + } + + // Maybe another block at lower stack position? + int outerIndex = this.blocks.indexOf(outerLifetime); + if (outerIndex != -1 && outerIndex < innerIndex) { + return true; + } + } + + return false; + } + + /** + * Add a method's lifetime parameters to this relation. + * + * @param lifetimeParameters + */ + public void addParameters(Collection lifetimeParameters) { + this.parameters.addAll(lifetimeParameters); + } + + /** + * Enter a named block to this relation. + * + * @param lifetime + */ + public void startNamedBlock(String lifetime) { + this.blocks.push(lifetime); + } + + /** + * Remove the named block with the given name from this relation. It also + * removes inner blocks in case that the given block is not the latest one. + * + * @param lifetime + */ + public void endNamedBlock(String lifetime) { + int i = this.blocks.lastIndexOf(lifetime); + if (i != -1) { + this.blocks.subList(i, this.blocks.size()).clear(); + } + } + + /** + * Replace this lifetime relation with the merge result of the given two + * relations. + * + * @param first + * @param second + */ + public void replaceWithMerge(LifetimeRelation first, LifetimeRelation second) { + this.parameters.clear(); + this.blocks.clear(); + + this.parameters.addAll(first.parameters); + this.parameters.retainAll(second.parameters); + Iterator it1 = first.blocks.iterator(); + Iterator it2 = second.blocks.iterator(); + while (it1.hasNext() && it2.hasNext()) { + String b1 = it1.next(); + String b2 = it2.next(); + if (b1.equals(b2)) { + this.blocks.push(b1); + } else { + break; + } + } + } + + public final static LifetimeRelation EMPTY = new LifetimeRelation(); +} diff --git a/modules/wyil/src/wyil/util/type/LifetimeSubstitution.java b/modules/wyil/src/wyil/util/type/LifetimeSubstitution.java new file mode 100644 index 0000000000..ae712323b0 --- /dev/null +++ b/modules/wyil/src/wyil/util/type/LifetimeSubstitution.java @@ -0,0 +1,262 @@ +package wyil.util.type; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import wyautl_old.lang.Automaton; +import wyautl_old.lang.Automaton.State; +import wyil.lang.Type; + +/** + * This class can be used to substitute lifetimes, i.e. instantiate lifetime + * parameters with actual lifetime arguments. + * + * It is not a trivial task, as you must take care to not capture lifetime + * parameters within nested method types. This is especially difficult when + * types are recursive. + * + * The method type defined by + * + *

+ * type mymethod is method(&*:mymethod)->(&a:mymethod)
+ * 
+ * + * returns + * + *
+ * X<&a:Y(&Y)->(X)>>
+ * 
+ * + * Assume the argument for parameter "a" is "b". You cannot just replace "&a:" + * with "&b:", because it would also affect the nested return type. Instead, we + * need this type here: + * + *
+ * &b:X(&X)->( Y<&a:Z(&Z)->(Y)>> )>
+ * 
+ * + * It can be further simplified to + * + *
+ * &b:X(&X)->(&a:X)>
+ * 
+ * + * Note that the original type has two different references, but the substituted + * and minimized type has three reference types with different lifetimes. They + * cannot be mapped to the same automaton state. Substitution might yield an + * automaton with more states! + */ +public class LifetimeSubstitution { + + /** + * The substitution to apply + */ + private final Map substitution; + + /** + * The substituted type + */ + private final Type substituted; + + /** + * The states in the original automaton + */ + private final State[] originalStates; + + /** + * The states in our new automaton + */ + private final List substitutedStates; + + /** + * For each old state we can have several new states. They differ in the set + * of lifetimes that had to be substituted. + */ + private final Map> mapping; + + /** + * Apply the substitution. + * + * @param original + * the original automaton + * @param substitution + * the substitution to apply + */ + public LifetimeSubstitution(Type original, Map substitution) { + this.substitution = substitution; + + if (substitution.isEmpty()) { + // easy! + substituted = original; + + // we do not need those fields + originalStates = null; + substitutedStates = null; + mapping = null; + } else { + // initialize all fields + originalStates = Type.destruct(original).states; + substitutedStates = new ArrayList(originalStates.length); + mapping = new HashMap<>(originalStates.length); + + // Start with the root state + SubstitutedState newRootState = copy(0, Collections.emptySet()); + if (newRootState.substitutedLiffetimes.isEmpty()) { + // nothing got substituted! + substituted = original; + } else { + // Construct the resulting type. + substituted = Type.construct(new Automaton(substitutedStates)); + } + } + } + + /** + * Get the substituted type. + * + * @return the substituted type + */ + public Type getType() { + return substituted; + } + + /** + * Copy the given state into our new automaton while applying the + * substitution but ignoring the given lifetimes. + * + * @param index + * the state index from the original automaton + * @param ignored + * do not apply substitution for these lifetimes + * @return + */ + private SubstitutedState copy(int index, Set ignored) { + List mapped = mapping.get(index); + if (mapped == null) { + mapped = new LinkedList<>(); + mapping.put(index, mapped); + } else { + outer: for (SubstitutedState entry : mapped) { + if (ignored.containsAll(entry.ignoredLifetimes)) { + // This entry might be suitable for us, but only if it did + // not substitute any lifetime that should be ignored now. + for (String substitutedLifetime : entry.substitutedLiffetimes) { + if (ignored.contains(substitutedLifetime)) { + // no, cannot re-use that one + continue outer; + } + } + return entry; + } + } + } + + // OK, we need to copy that state. + SubstitutedState substitutedState = new SubstitutedState( + substitutedStates.size(), // next available index + ignored, + new HashSet() + ); + mapped.add(substitutedState); + State state = new State(originalStates[index]); + substitutedStates.add(state); + + switch (state.kind) { + case Type.K_REFERENCE: + // If it is a reference, then we might have to fix the lifetime + String lifetime = (String) state.data; + if (!ignored.contains(lifetime)) { + String replacement = substitution.get(lifetime); + if (replacement != null) { + // OK, we need to replace + substitutedState.substitutedLiffetimes.add(lifetime); + state.data = replacement; + } + } + break; + + case Type.K_FUNCTION: + case Type.K_METHOD: + // We need to apply the substitution to the context lifetimes + Type.FunctionOrMethod.Data data = (Type.FunctionOrMethod.Data) state.data; + if (!data.contextLifetimes.isEmpty()) { + ListIterator it = data.contextLifetimes.listIterator(); + boolean updated = false; + while (it.hasNext()) { + String lifetime2 = it.next(); + if (!ignored.contains(lifetime2)) { + String replacement = substitution.get(lifetime2); + if (replacement != null) { + // OK, we need to replace + substitutedState.substitutedLiffetimes.add(lifetime2); + it.set(replacement); + updated = true; + } + } + } + if (updated) { + // Context lifetimes should be sorted + Collections.sort(data.contextLifetimes); + } + } + + // We need to ignore lifetime parameters when recursing to the children children. + if (!data.lifetimeParameters.isEmpty()) { + ignored = new HashSet(ignored); + ignored.addAll(data.lifetimeParameters); + } + break; + + default: + } + + if (state.children.length != 0) { + // We cannot yet know which lifetimes will be substituted, but need + // to specify something before we enter the children. + // Overestimating does not harm, so we assume that all lifetimes + // that should be substituted actually will be substituted + // But we keep track of which lifetimes got substituted in the children. + Set childSubstituted = new HashSet(substitutedState.substitutedLiffetimes); + substitutedState.substitutedLiffetimes.addAll(substitution.keySet()); + substitutedState.substitutedLiffetimes.removeAll(ignored); + + // Now copy all children + for (int i = 0; i < state.children.length; ++i) { + SubstitutedState child = copy(state.children[i], ignored); + if (child != substitutedState) { + // remember the substituted lifetimes + // but only if that child is not directly recursively + // referring to us :) + childSubstituted.addAll(child.substitutedLiffetimes); + } + + // fix the index + state.children[i] = child.newStateIndex; + } + + // Update the substituted lifetimes + substitutedState.substitutedLiffetimes.retainAll(childSubstituted); + } + + return substitutedState; + } + + private static class SubstitutedState { + private final int newStateIndex; + private final Set ignoredLifetimes; + private final Set substitutedLiffetimes; + + private SubstitutedState(int newStateIndex, Set ignoredLifetimes, Set substitutedLiffetimes) { + this.newStateIndex = newStateIndex; + this.ignoredLifetimes = ignoredLifetimes; + this.substitutedLiffetimes = substitutedLiffetimes; + } + } +} diff --git a/modules/wyil/src/wyil/util/type/SubtypeOperator.java b/modules/wyil/src/wyil/util/type/SubtypeOperator.java index 01611853dd..7407b07de7 100644 --- a/modules/wyil/src/wyil/util/type/SubtypeOperator.java +++ b/modules/wyil/src/wyil/util/type/SubtypeOperator.java @@ -78,17 +78,37 @@ public class SubtypeOperator { protected final Automaton from; protected final Automaton to; + private final Map fromLifetimeSubstitution; + private final Map toLifetimeSubstitution; + private final LifetimeRelation lifetimeRelation; private final BitSet assumptions; - public SubtypeOperator(Automaton from, Automaton to) { + public SubtypeOperator(Automaton from, Automaton to, LifetimeRelation lr) { this.from = from; this.to = to; + this.lifetimeRelation = lr; + this.fromLifetimeSubstitution = Collections.emptyMap(); + this.toLifetimeSubstitution = Collections.emptyMap(); // matrix is twice the size to accommodate positive and negative signs this.assumptions = new BitSet((2*from.size()) * (to.size()*2)); //System.out.println("FROM: " + from); //System.out.println("TO: " + to); } + private SubtypeOperator(SubtypeOperator sto, Map fromLifetimeSubstitution, + Map toLifetimeSubstitution) { + this.from = sto.from; + this.to = sto.to; + this.fromLifetimeSubstitution = new HashMap(sto.fromLifetimeSubstitution); + prependLifetimes(this.fromLifetimeSubstitution); + this.fromLifetimeSubstitution.putAll(fromLifetimeSubstitution); + this.toLifetimeSubstitution = new HashMap(sto.toLifetimeSubstitution); + prependLifetimes(this.toLifetimeSubstitution); + this.toLifetimeSubstitution.putAll(toLifetimeSubstitution); + this.lifetimeRelation = sto.lifetimeRelation; + this.assumptions = sto.assumptions; + } + /** * Test whether from :> to * @param fromIndex @@ -194,9 +214,50 @@ protected boolean isIntersectionInner(int fromIndex, boolean fromSign, int toInd if(fromSign || toSign) { int fromChild = fromState.children[0]; int toChild = toState.children[0]; - return isIntersection(fromChild, fromSign, toChild, toSign) - || isIntersection(fromChild, !fromSign, toChild, !toSign); + + if (fromSign && toSign) { + // "Is there any value in both &a:A and &b:B ?" + // + // This is equivalent to the question "Is there any value in both &A and &B?" + // If there is a value in both &a:A and &b:B, then we can take that value + // and change its lifetime to *. It will then be in both &A and &B. + // If there is a value in both &A and &B, then it is also in both &a:A and &b:B + // because the lifetime * outlives a and b. + // + // Finally, there is a value in both &A and &B iff the types A and B intersect. + return isIntersection(fromChild, true, toChild, true); + } else { + String fromLifetime = applyFromLifetimeSubstitution((String) fromState.data); + String toLifetime = applyToLifetimeSubstitution((String) toState.data); + + if (fromSign) { + // Equivalent to the negation of "is &a:A (fromType) a subtype of &b:B (toType)?" + return !( + // &a:A is a subtype of &b:B iff all these are true: + // a outlives b + lifetimeRelation.outlives(fromLifetime, toLifetime) + // A is a subtype of B + && !isIntersection(fromChild, true, toChild, false) + // B is a subtype of A + && !isIntersection(fromChild, false, toChild, true) + ); + } else { + // Equivalent to the negation of "is &b:B (toType) a subtype of &a:A (fromType)?" + return !( + // &b:B is a subtype of &a:A iff all these are true: + // b outlives a + lifetimeRelation.outlives(toLifetime, fromLifetime) + // B is a subtype of A + && !isIntersection(fromChild, false, toChild, true) + // A is a subtype of B + && !isIntersection(fromChild, true, toChild, false) + ); + } + } } + + // An inverted reference type always contains the value "null". + // Both are inverted, so they intersect with "null". return true; case K_MAP: case K_TUPLE: { @@ -239,49 +300,85 @@ protected boolean isIntersectionInner(int fromIndex, boolean fromSign, int toInd // nary nodes int[] fromChildren = fromState.children; int[] toChildren = toState.children; - int fromNumParams = (Integer) fromState.data; - int toNumParams = (Integer) toState.data; + Type.FunctionOrMethod.Data fromData = (Type.FunctionOrMethod.Data) fromState.data; + Type.FunctionOrMethod.Data toData = (Type.FunctionOrMethod.Data) toState.data; + int fromNumParams = fromData.numParams; + int toNumParams = toData.numParams; + List fromLifetimeParameters = fromData.lifetimeParameters; + List toLifetimeParameters = toData.lifetimeParameters; if (fromSign && toSign) { // Two intersecting method types must have the same number - // of parameters and returns. - if (fromChildren.length != toChildren.length || fromNumParams != toNumParams) { + // of parameters, returns and lifetime parameters. + if (fromChildren.length != toChildren.length + || fromNumParams != toNumParams + || fromLifetimeParameters.size() != toLifetimeParameters.size()){ return false; } + // Context lifetimes do not matter, because we can always choose + // a method without context lifetimes to find an intersecting method. + } else { + // Now we have one of the following: + // fromSign == true: negation of "is fromType a subtype of toType?" + // toSign == true: negation of "is toType a subtype of fromType?" + + // Two method types can only be subtypes of each other if they have + // the same number of parameters, returns and lifetime parameters. + if (fromChildren.length != toChildren.length + || fromNumParams != toNumParams + || fromLifetimeParameters.size() != toLifetimeParameters.size()){ + return true; // true means "not subtype" + } + // The supertype must contain all context lifetimes of the subtype + List fromContextLifetimes = applyFromLifetimeSubstitution(fromData.contextLifetimes); + List toContextLifetimes = applyToLifetimeSubstitution(toData.contextLifetimes); + if (fromSign) { + if (!toContextLifetimes.containsAll(fromContextLifetimes)) { + return true; + } + } else { + if (!fromContextLifetimes.containsAll(toContextLifetimes)) { + return true; + } + } + } + + // Calculate the substitutions for lifetime parameters + Map fromLifetimeSubstitution = buildLifetimeSubstitution(fromLifetimeParameters); + Map toLifetimeSubstitution = buildLifetimeSubstitution(toLifetimeParameters); + SubtypeOperator sto = this; + if (!fromLifetimeSubstitution.isEmpty() || !toLifetimeSubstitution.isEmpty()) { + sto = new SubtypeOperator(this, fromLifetimeSubstitution, toLifetimeSubstitution); + } + + if (fromSign && toSign) { // Two intersecting method types must have intersecting return types. // Parameter types can always be chosen as "any". for (int i = fromNumParams; i < fromChildren.length; ++i) { - if (!isIntersection(fromChildren[i], true, toChildren[i], true)) { + if (!sto.isIntersection(fromChildren[i], true, toChildren[i], true)) { return false; } } return true; } - // Now we have one of the following: - // fromSign == true: negation of "is fromType a subtype of toType?" - // toSign == true: negation of "is toType a subtype of fromType?" - - // Two method types can only be subtypes of each other if they have - // the same number of parameters and returns. - if (fromChildren.length != toChildren.length || fromNumParams != toNumParams) { - return true; // true means "not subtype" - } + // here we have the subtype test again... // Parameter types are contra-variant for (int i = 0; i < fromNumParams; ++i) { - if (isIntersection(fromChildren[i], !fromSign, toChildren[i], !toSign)) { + if (sto.isIntersection(fromChildren[i], !fromSign, toChildren[i], !toSign)) { return true; } } // Return types are co-variant for (int i = fromNumParams; i < fromChildren.length; ++i) { - if (isIntersection(fromChildren[i], fromSign, toChildren[i], toSign)) { + if (sto.isIntersection(fromChildren[i], fromSign, toChildren[i], toSign)) { return true; } } + return false; } return true; @@ -343,6 +440,62 @@ protected boolean isIntersectionInner(int fromIndex, boolean fromSign, int toInd return !fromSign || !toSign; } + private static Map buildLifetimeSubstitution(List lifetimeParameters) { + if (lifetimeParameters.isEmpty()) { + return Collections.emptyMap(); + } + Map substitution = new HashMap(); + int i = 0; + for (String lifetime : lifetimeParameters) { + substitution.put(lifetime, "$" + (i++)); + } + return substitution; + } + + private static void prependLifetimes(Map substitution) { + for (Map.Entry entry : substitution.entrySet()) { + entry.setValue("$" + entry.getValue()); + } + } + + private String applyFromLifetimeSubstitution(String lifetime) { + String replacement = fromLifetimeSubstitution.get(lifetime); + if (replacement != null) { + return replacement; + } + return lifetime; + } + + private String applyToLifetimeSubstitution(String lifetime) { + String replacement = toLifetimeSubstitution.get(lifetime); + if (replacement != null) { + return replacement; + } + return lifetime; + } + + private List applyFromLifetimeSubstitution(List lifetimes) { + if (lifetimes.isEmpty() || fromLifetimeSubstitution.isEmpty()) { + return lifetimes; + } + List replacement = new ArrayList(lifetimes.size()); + for (String lifetime : lifetimes) { + replacement.add(applyFromLifetimeSubstitution(lifetime)); + } + return replacement; + } + + private List applyToLifetimeSubstitution(List lifetimes) { + if (lifetimes.isEmpty() || toLifetimeSubstitution.isEmpty()) { + return lifetimes; + } + List replacement = new ArrayList(lifetimes.size()); + for (String lifetime : lifetimes) { + replacement.add(applyToLifetimeSubstitution(lifetime)); + } + return replacement; + } + /** *

* Check for intersection between two states with kind K_RECORD. The diff --git a/modules/wyil/src/wyil/util/type/TypeAlgorithms.java b/modules/wyil/src/wyil/util/type/TypeAlgorithms.java index 9b37301301..f9cb69536f 100755 --- a/modules/wyil/src/wyil/util/type/TypeAlgorithms.java +++ b/modules/wyil/src/wyil/util/type/TypeAlgorithms.java @@ -108,9 +108,9 @@ public int compare(Automaton.State s1, Automaton.State s2) { Boolean nid2 = (Boolean) s2.data; return nid1.toString().compareTo(nid2.toString()); } else if(s1.kind == Type.K_FUNCTION || s1.kind == Type.K_METHOD) { - int s1NumParams = (Integer) s1.data; - int s2NumParams = (Integer) s2.data; - return Integer.compare(s1NumParams, s2NumParams); + Type.FunctionOrMethod.Data s1Data = (Type.FunctionOrMethod.Data) s1.data; + Type.FunctionOrMethod.Data s2Data = (Type.FunctionOrMethod.Data) s2.data; + return s1Data.compareTo(s2Data); } else { String str1 = (String) s1.data; String str2 = (String) s2.data; @@ -772,7 +772,7 @@ private static boolean simplifyUnion_2(int index, Automaton.State state, private static boolean isSubtype(int fromIndex, int toIndex, Automaton automaton) { - SubtypeOperator op = new SubtypeOperator(automaton,automaton); + SubtypeOperator op = new SubtypeOperator(automaton,automaton,LifetimeRelation.EMPTY); return op.isSubtype(fromIndex, toIndex); } @@ -1057,6 +1057,7 @@ private static int intersectSameKind(int fromIndex, boolean fromSign, Automaton case Type.K_SET: return intersectSetsOrLists(fromIndex,fromSign,from,toIndex,toSign,to,allocations,states); case Type.K_REFERENCE: + return intersectCompounds(fromIndex,fromSign,from,toIndex,toSign,to,fromState.data,allocations,states); case Type.K_MAP: return intersectCompounds(fromIndex,fromSign,from,toIndex,toSign,to,null,allocations,states); case Type.K_NEGATION: diff --git a/modules/wyil/src/wyil/util/type/TypeTester.java b/modules/wyil/src/wyil/util/type/TypeTester.java index 6b15b7bf67..047d38a2b5 100755 --- a/modules/wyil/src/wyil/util/type/TypeTester.java +++ b/modules/wyil/src/wyil/util/type/TypeTester.java @@ -97,7 +97,8 @@ public boolean accepts(int index, Automaton automaton, Term value) { return false; } int length = schildren.length; - int sNumParams = (Integer) state.data; + Type.FunctionOrMethod.Data data = (Type.FunctionOrMethod.Data) state.data; + int sNumParams = data.numParams; // First, do parameters (which are contravariant). for(int i=0;iemptySet(), + Collections.emptyList(), WHILEY_SYSTEM_T); JvmType.Function ft3 = convertFunType(wyft); codes.add(new Bytecode.Invoke(owner, nameMangle("main", wyft), ft3, Bytecode.InvokeMode.STATIC)); diff --git a/modules/wyrt/src/whiley/io/File.whiley b/modules/wyrt/src/whiley/io/File.whiley index 909bbfff89..33fe027032 100755 --- a/modules/wyrt/src/whiley/io/File.whiley +++ b/modules/wyrt/src/whiley/io/File.whiley @@ -55,13 +55,13 @@ public type Reader is { } public method Reader(string fileName) -> Reader: - NativeFile this = NativeFileReader(fileName) + NativeFile reader = NativeFileReader(fileName) return { - readAll: &( -> read(this)), - read: &(uint n -> read(this,n)), - hasMore: &( -> hasMore(this)), - close: &( -> close(this)), - available: &( -> available(this)) + readAll: &( -> read(reader)), + read: &(uint n -> read(reader,n)), + hasMore: &( -> hasMore(reader)), + close: &( -> close(reader)), + available: &( -> available(reader)) } // ==================================================== @@ -70,11 +70,11 @@ public method Reader(string fileName) -> Reader: type Writer is whiley.io.Writer.Writer public method Writer(string fileName) -> Writer: - NativeFile this = NativeFileWriter(fileName) + NativeFile reader = NativeFileWriter(fileName) return { - write: &(byte[] data -> write(this,data)), - close: &( -> close(this)), - flush: &( -> flush(this)) + write: &(byte[] data -> write(reader,data)), + close: &( -> close(reader)), + flush: &( -> flush(reader)) } // ==================================================== diff --git a/modules/wyrt/src/whiley/lang/Stack.whiley b/modules/wyrt/src/whiley/lang/Stack.whiley index cca66e9845..7351c97d88 100755 --- a/modules/wyrt/src/whiley/lang/Stack.whiley +++ b/modules/wyrt/src/whiley/lang/Stack.whiley @@ -36,31 +36,31 @@ public function create(int max) -> Stack: length: 0 } -public function size(Stack this) -> int: - return this.length +public function size(Stack stack) -> int: + return stack.length /** * Return the top element of the "stack". */ -public function top(Stack this) -> int: +public function top(Stack stack) -> int: // - return this.items[this.length-1] + return stack.items[stack.length-1] /** * Push an element onto the "stack". */ -public function push(Stack this, int element) -> (Stack r): +public function push(Stack stack, int element) -> (Stack r): // - this.items[this.length] = element - this.length = this.length + 1 - return this + stack.items[stack.length] = element + stack.length = stack.length + 1 + return stack /** * Pop an element off the "stack". */ -public function pop(Stack this) -> (Stack r): +public function pop(Stack stack) -> (Stack r): // - this.length = this.length - 1 + stack.length = stack.length - 1 // - return this + return stack diff --git a/tests/invalid/ConstrainedRecord_Invalid_1.whiley b/tests/invalid/ConstrainedRecord_Invalid_1.whiley index 822636db9c..e5710c1af8 100644 --- a/tests/invalid/ConstrainedRecord_Invalid_1.whiley +++ b/tests/invalid/ConstrainedRecord_Invalid_1.whiley @@ -1,7 +1,7 @@ type tup is {int y, int x} -type point is ({int y, int x} this) where (this.x > 0) && (this.y > 0) +type point is ({int y, int x} _this) where (_this.x > 0) && (_this.y > 0) function f(point p) -> point: return p diff --git a/tests/invalid/DefiniteAssign_Invalid_4.whiley b/tests/invalid/DefiniteAssign_Invalid_4.whiley index 3d24e46bd5..cebab57928 100644 --- a/tests/invalid/DefiniteAssign_Invalid_4.whiley +++ b/tests/invalid/DefiniteAssign_Invalid_4.whiley @@ -1,5 +1,5 @@ -method f(any this) : - debug this +method f(any _this) : + debug _this method g() : f(x) diff --git a/tests/invalid/FunctionRef_Invalid_5.whiley b/tests/invalid/FunctionRef_Invalid_5.whiley index d79c0f0ff6..c2be32b6b1 100644 --- a/tests/invalid/FunctionRef_Invalid_5.whiley +++ b/tests/invalid/FunctionRef_Invalid_5.whiley @@ -1,6 +1,6 @@ type Proc is &{int data} -method read(Proc this, int x) -> int: +method read(Proc _this, int x) -> int: return x + 1 type Func is { diff --git a/tests/invalid/FunctionRef_Invalid_7.sysout b/tests/invalid/FunctionRef_Invalid_7.sysout index 0ac8eb7ded..abffbcb04c 100644 --- a/tests/invalid/FunctionRef_Invalid_7.sysout +++ b/tests/invalid/FunctionRef_Invalid_7.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/FunctionRef_Invalid_7.whiley:6: insufficient arguments for function or method invocation - return this->func(arg) - ^^^^^^^^^^^^^^^ + return _this->func(arg) + ^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/FunctionRef_Invalid_7.whiley b/tests/invalid/FunctionRef_Invalid_7.whiley index 055297462a..5334ca1f76 100644 --- a/tests/invalid/FunctionRef_Invalid_7.whiley +++ b/tests/invalid/FunctionRef_Invalid_7.whiley @@ -2,5 +2,5 @@ type Proc is &{ function func(bool,int)->bool } -method test(Proc this, bool arg) -> bool: - return this->func(arg) +method test(Proc _this, bool arg) -> bool: + return _this->func(arg) diff --git a/tests/invalid/Lifetime_Invalid_1.sysout b/tests/invalid/Lifetime_Invalid_1.sysout new file mode 100644 index 0000000000..6a9ed1ad58 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Invalid_1.whiley:2: expected type &int, found &this:int + &*:int ptr1 = this:new 42 + ^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Invalid_1.whiley b/tests/invalid/Lifetime_Invalid_1.whiley new file mode 100644 index 0000000000..0bfad4b10c --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_1.whiley @@ -0,0 +1,2 @@ +public export method test(): + &*:int ptr1 = this:new 42 diff --git a/tests/invalid/Lifetime_Invalid_2.sysout b/tests/invalid/Lifetime_Invalid_2.sysout new file mode 100644 index 0000000000..b6a23c9438 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Invalid_2.whiley:4: expected type &this:int, found &myblock:int + x = myblock:new 1 + ^ diff --git a/tests/invalid/Lifetime_Invalid_2.whiley b/tests/invalid/Lifetime_Invalid_2.whiley new file mode 100644 index 0000000000..d9ab74f5b0 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_2.whiley @@ -0,0 +1,4 @@ +public export method test(): + &this:int x = this:new 1 + myblock: + x = myblock:new 1 diff --git a/tests/invalid/Lifetime_Invalid_3.sysout b/tests/invalid/Lifetime_Invalid_3.sysout new file mode 100644 index 0000000000..b2a6203d5a --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_3.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Invalid_3.whiley:1: use of undeclared lifetime +method foo() -> &this:int: + ^^^^ diff --git a/tests/invalid/Lifetime_Invalid_3.whiley b/tests/invalid/Lifetime_Invalid_3.whiley new file mode 100644 index 0000000000..afb6491ace --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_3.whiley @@ -0,0 +1,5 @@ +method foo() -> &this:int: + return this:new 1 + +public export method test(): + foo() diff --git a/tests/invalid/Lifetime_Invalid_4.sysout b/tests/invalid/Lifetime_Invalid_4.sysout new file mode 100644 index 0000000000..4899a2d063 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Invalid_4.whiley:2: expected type &int, found &this:int + return this:new 1 + ^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Invalid_4.whiley b/tests/invalid/Lifetime_Invalid_4.whiley new file mode 100644 index 0000000000..40002aefe2 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_4.whiley @@ -0,0 +1,5 @@ +method foo() -> &*:int: + return this:new 1 + +public export method test(): + foo() diff --git a/tests/invalid/Lifetime_Invalid_5.sysout b/tests/invalid/Lifetime_Invalid_5.sysout new file mode 100644 index 0000000000..2e2329ebbe --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_5.sysout @@ -0,0 +1,6 @@ +../../tests/invalid/Lifetime_Invalid_5.whiley:16: unable to resolve name (m(&int,&int) is ambiguous + found: Lifetime_Invalid_5:m : method(&a:int,&a:int)->(int) instantiated with <*> + found: Lifetime_Invalid_5:m : method(&a:int,&int)->(int) instantiated with <*> + found: Lifetime_Invalid_5:m : method(&int,&a:int)->(int) instantiated with <*>) + m(new 1, new 2) + ^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Invalid_5.whiley b/tests/invalid/Lifetime_Invalid_5.whiley new file mode 100644 index 0000000000..b361227376 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_5.whiley @@ -0,0 +1,16 @@ +// test lifetime inference with lifetime overloading but same types + +method m(&a:int x, &a:int y) -> int: + return 1 + +method m(&a:int x, &*:int y) -> int: + return 2 + +method m(&*:int x, &a:int y) -> int: + return 3 + +public export method test(): + // variant1: (&*:int, &*:int) <-- + // variant2: (&*:int, &*:int) <-- + // variant3: (&*:int, &*:int) <-- + m(new 1, new 2) diff --git a/tests/invalid/Lifetime_Invalid_6.sysout b/tests/invalid/Lifetime_Invalid_6.sysout new file mode 100644 index 0000000000..eec939dd70 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_6.sysout @@ -0,0 +1,5 @@ +../../tests/invalid/Lifetime_Invalid_6.whiley:12: unable to resolve name (m(&this:int,&this:int) is ambiguous + found: Lifetime_Invalid_6:m : method(&a:int,&b:int)->(int) instantiated with + found: Lifetime_Invalid_6:m : method(&a:int,&a:int)->(int) instantiated with ) + m(this:new 1, this:new 2) + ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Invalid_6.whiley b/tests/invalid/Lifetime_Invalid_6.whiley new file mode 100644 index 0000000000..04ee77d2c0 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_6.whiley @@ -0,0 +1,12 @@ +// test lifetime inference with lifetime overloading but same types + +method m(&a:int x, &a:int y) -> int: + return 1 + +method m(&a:int x, &b:int y) -> int: + return 2 + +public export method test(): + // variant1: (&this:int, &this:int) <-- + // variant2: (&this:int, &this:int) <-- + m(this:new 1, this:new 2) diff --git a/tests/invalid/Lifetime_Invalid_7.sysout b/tests/invalid/Lifetime_Invalid_7.sysout new file mode 100644 index 0000000000..b9135cb95e --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_7.sysout @@ -0,0 +1,5 @@ +../../tests/invalid/Lifetime_Invalid_7.whiley:16: unable to resolve name (m(&int,&this:int,bool) is ambiguous + found: Lifetime_Invalid_7:m : method(&a:int|&b:bool,&a:int,bool)->(&b:int) instantiated with + found: Lifetime_Invalid_7:m : method(&int,&a:int,int|bool)->(&a:int) instantiated with ) + m(new 1, this:new 2, true) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Invalid_7.whiley b/tests/invalid/Lifetime_Invalid_7.whiley new file mode 100644 index 0000000000..a1226a2c06 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_7.whiley @@ -0,0 +1,16 @@ +// test lifetime inference with lifetime overloading and different types + +method m((&a:int)|(&b:bool) x, &a:int y, bool z) -> &b:int: + return b:new 1 + +method m(&a:bool x, &a:int y, bool z) -> &a:int: + return a:new 2 + +method m(&*:int x, &a:int y, int|bool z) -> &a:int: + return a:new 3 + +public export method test(): + // variant1: ((&this:int)|(&*:bool) x, &this:int, bool z) + // variant2: no + // variant3: (&this:int x, &this:int y, int|bool z) + m(new 1, this:new 2, true) diff --git a/tests/invalid/Lifetime_Invalid_8.sysout b/tests/invalid/Lifetime_Invalid_8.sysout new file mode 100644 index 0000000000..fcdbe8c789 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_8.sysout @@ -0,0 +1,5 @@ +../../tests/invalid/Lifetime_Invalid_8.whiley:10: unable to resolve name (m(&int,&this:int) is ambiguous + found: Lifetime_Invalid_8:m : method(&a:int|&b:bool,&a:int|&b:int)->() instantiated with <*, this> + found: Lifetime_Invalid_8:m : method(&a:int|&b:bool,&a:int|&b:int)->() instantiated with ) + m(x, y) + ^^^^^^^ diff --git a/tests/invalid/Lifetime_Invalid_8.whiley b/tests/invalid/Lifetime_Invalid_8.whiley new file mode 100644 index 0000000000..5e83f899b3 --- /dev/null +++ b/tests/invalid/Lifetime_Invalid_8.whiley @@ -0,0 +1,10 @@ +// test lifetime inference with lifetime overloading and different types + +method m((&a:int)|(&b:bool) x, (&a:int)|(&b:int) y): + +public export method test(): + // --> ((&this:int)|(&*:bool), &this:int) + // <*, this> --> ((&*:int)|(&this:bool), &this:int) + (&*:int)|(&*:bool) x = *:new 1 + &this:int y = this:new 1 + m(x, y) diff --git a/tests/invalid/Lifetime_Lambda_Invalid_1.sysout b/tests/invalid/Lifetime_Lambda_Invalid_1.sysout new file mode 100644 index 0000000000..14eb3ca605 --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_1.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Lambda_Invalid_1.whiley:4: expected type method(&a:int,&a:int)->(&a:int), found method(&a:int,&int)->(&a:int) + method(&a:int, &a:int)->(&a:int) m = &(&a:int x, &int y -> a:new 1) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Lambda_Invalid_1.whiley b/tests/invalid/Lifetime_Lambda_Invalid_1.whiley new file mode 100644 index 0000000000..1feb0990ca --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_1.whiley @@ -0,0 +1,4 @@ +// wrong method type + +public export method test(): + method(&a:int, &a:int)->(&a:int) m = &(&a:int x, &int y -> a:new 1) diff --git a/tests/invalid/Lifetime_Lambda_Invalid_2.sysout b/tests/invalid/Lifetime_Lambda_Invalid_2.sysout new file mode 100644 index 0000000000..d92433987a --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_2.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Lambda_Invalid_2.whiley:4: expected type method(&a:int,&int)->(&int), found method(&a:int,&int)->(&a:int) + method(&a:int, &int)->(&*:int) m = &(&a:int x, &int y -> a:new 1) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Lambda_Invalid_2.whiley b/tests/invalid/Lifetime_Lambda_Invalid_2.whiley new file mode 100644 index 0000000000..f5a5c88284 --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_2.whiley @@ -0,0 +1,4 @@ +// wrong method type + +public export method test(): + method(&a:int, &int)->(&*:int) m = &(&a:int x, &int y -> a:new 1) diff --git a/tests/invalid/Lifetime_Lambda_Invalid_3.sysout b/tests/invalid/Lifetime_Lambda_Invalid_3.sysout new file mode 100644 index 0000000000..21de4ae53e --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_3.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Lambda_Invalid_3.whiley:4: expected type method()->(&int), found method()->(&int) + method()->(&int) m = &(-> new 1) + ^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Lambda_Invalid_3.whiley b/tests/invalid/Lifetime_Lambda_Invalid_3.whiley new file mode 100644 index 0000000000..653767e2bd --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_3.whiley @@ -0,0 +1,4 @@ +// wrong method type + +public export method test(): + method()->(&int) m = &(-> new 1) diff --git a/tests/invalid/Lifetime_Lambda_Invalid_4.sysout b/tests/invalid/Lifetime_Lambda_Invalid_4.sysout new file mode 100644 index 0000000000..ca75e48b38 --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_4.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Lambda_Invalid_4.whiley:4: use of undeclared lifetime + method()->(&this:int) m = &(-> this:new 1) + ^^^^ diff --git a/tests/invalid/Lifetime_Lambda_Invalid_4.whiley b/tests/invalid/Lifetime_Lambda_Invalid_4.whiley new file mode 100644 index 0000000000..517b1158b8 --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_4.whiley @@ -0,0 +1,4 @@ +// missing context lifetime + +public export method test(): + method()->(&this:int) m = &(-> this:new 1) diff --git a/tests/invalid/Lifetime_Lambda_Invalid_5.sysout b/tests/invalid/Lifetime_Lambda_Invalid_5.sysout new file mode 100644 index 0000000000..88d32511c4 --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_5.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Lambda_Invalid_5.whiley:7: lifetime 'this' cannot be dereferenced here + return &(->*x) + ^^ diff --git a/tests/invalid/Lifetime_Lambda_Invalid_5.whiley b/tests/invalid/Lifetime_Lambda_Invalid_5.whiley new file mode 100644 index 0000000000..a7d772131f --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_5.whiley @@ -0,0 +1,7 @@ +// missing context lifetime + +type mymethod is method()->(int) + +public export method test()->mymethod: + &this:int x = this:new 1 + return &(->*x) diff --git a/tests/invalid/Lifetime_Lambda_Invalid_6.sysout b/tests/invalid/Lifetime_Lambda_Invalid_6.sysout new file mode 100644 index 0000000000..21590fbaef --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_6.sysout @@ -0,0 +1,3 @@ +../../tests/invalid/Lifetime_Lambda_Invalid_6.whiley:7: expected type Lifetime_Lambda_Invalid_6:mymethod, found method[this]()->(int) + return &[this](-> (*x) + (*(new 1))) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/invalid/Lifetime_Lambda_Invalid_6.whiley b/tests/invalid/Lifetime_Lambda_Invalid_6.whiley new file mode 100644 index 0000000000..d7acf31da3 --- /dev/null +++ b/tests/invalid/Lifetime_Lambda_Invalid_6.whiley @@ -0,0 +1,7 @@ +// missing context lifetime + +type mymethod is method()->(int) + +public export method test()->mymethod: + &this:int x = new 1 + return &[this](-> (*x) + (*(new 1))) diff --git a/tests/invalid/MethodCall_Invalid_1.sysout b/tests/invalid/MethodCall_Invalid_1.sysout index ee15fa7964..2b1fc6f92a 100644 --- a/tests/invalid/MethodCall_Invalid_1.sysout +++ b/tests/invalid/MethodCall_Invalid_1.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/MethodCall_Invalid_1.whiley:5: unable to resolve name (no match for f(&int,int)) - f(this, 1) - ^^^^^^^^^^ + f(_this, 1) + ^^^^^^^^^^^ diff --git a/tests/invalid/MethodCall_Invalid_1.whiley b/tests/invalid/MethodCall_Invalid_1.whiley index 2548ebd4c5..477cccc403 100644 --- a/tests/invalid/MethodCall_Invalid_1.whiley +++ b/tests/invalid/MethodCall_Invalid_1.whiley @@ -1,5 +1,5 @@ method f(int x) -> int: return 1 -method main(&int this): - f(this, 1) +method main(&int _this): + f(_this, 1) diff --git a/tests/invalid/MethodCall_Invalid_2.sysout b/tests/invalid/MethodCall_Invalid_2.sysout index cb4ddb1af4..b72463589f 100644 --- a/tests/invalid/MethodCall_Invalid_2.sysout +++ b/tests/invalid/MethodCall_Invalid_2.sysout @@ -1,4 +1,4 @@ ../../tests/invalid/MethodCall_Invalid_2.whiley:7: unable to resolve name (no match for f(&int,int) found: MethodCall_Invalid_2:f : method(MethodCall_Invalid_2:dummy,int)->(int)) - f(this, 1) - ^^^^^^^^^^ + f(_this, 1) + ^^^^^^^^^^^ diff --git a/tests/invalid/MethodCall_Invalid_2.whiley b/tests/invalid/MethodCall_Invalid_2.whiley index cad59547e0..a20777b837 100644 --- a/tests/invalid/MethodCall_Invalid_2.whiley +++ b/tests/invalid/MethodCall_Invalid_2.whiley @@ -1,7 +1,7 @@ type dummy is &{int x} -method f(dummy this, int x) -> int: +method f(dummy _this, int x) -> int: return 1 -method main(&int this) : - f(this, 1) +method main(&int _this) : + f(_this, 1) diff --git a/tests/invalid/MethodCall_Invalid_3.sysout b/tests/invalid/MethodCall_Invalid_3.sysout index 042ffbc899..e52594be18 100644 --- a/tests/invalid/MethodCall_Invalid_3.sysout +++ b/tests/invalid/MethodCall_Invalid_3.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/MethodCall_Invalid_3.whiley:2: record required, got: int[] - this.f(1) - ^^^^^^^^^ + _this.f(1) + ^^^^^^^^^^ diff --git a/tests/invalid/MethodCall_Invalid_3.whiley b/tests/invalid/MethodCall_Invalid_3.whiley index 9c9600463d..e57d389f1a 100644 --- a/tests/invalid/MethodCall_Invalid_3.whiley +++ b/tests/invalid/MethodCall_Invalid_3.whiley @@ -1,2 +1,2 @@ -method main(int[] this) : - this.f(1) +method main(int[] _this) : + _this.f(1) diff --git a/tests/invalid/Parameter_Invalid_1.sysout b/tests/invalid/Parameter_Invalid_1.sysout index 36d4e04b46..ac91ba609c 100644 --- a/tests/invalid/Parameter_Invalid_1.sysout +++ b/tests/invalid/Parameter_Invalid_1.sysout @@ -1,3 +1,3 @@ -../../tests/invalid/Parameter_Invalid_1.whiley:1: parameter already declared +../../tests/invalid/Parameter_Invalid_1.whiley:1: name already declared function f(int x, int x) -> int: ^ diff --git a/tests/invalid/Parsing_Invalid_5.sysout b/tests/invalid/Parsing_Invalid_5.sysout index 6fc18f1769..f152eb6c3c 100644 --- a/tests/invalid/Parsing_Invalid_5.sysout +++ b/tests/invalid/Parsing_Invalid_5.sysout @@ -1,3 +1,3 @@ -../../tests/invalid/Parsing_Invalid_5.whiley:2: variable already declared +../../tests/invalid/Parsing_Invalid_5.whiley:2: name already declared int x = x ^^^^^ diff --git a/tests/invalid/Parsing_Invalid_6.sysout b/tests/invalid/Parsing_Invalid_6.sysout index ba47ce4e03..f20fb796ea 100644 --- a/tests/invalid/Parsing_Invalid_6.sysout +++ b/tests/invalid/Parsing_Invalid_6.sysout @@ -1,3 +1,3 @@ -../../tests/invalid/Parsing_Invalid_6.whiley:3: variable already declared +../../tests/invalid/Parsing_Invalid_6.whiley:3: name already declared int x = x ^^^^^ diff --git a/tests/invalid/ProcessAccess_Invalid_1.sysout b/tests/invalid/ProcessAccess_Invalid_1.sysout index 09a6d711af..a9e40d1621 100644 --- a/tests/invalid/ProcessAccess_Invalid_1.sysout +++ b/tests/invalid/ProcessAccess_Invalid_1.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/ProcessAccess_Invalid_1.whiley:6: invalid assignment expression - this.op = 1 - ^^^^^^^ + _this.op = 1 + ^^^^^^^^ diff --git a/tests/invalid/ProcessAccess_Invalid_1.whiley b/tests/invalid/ProcessAccess_Invalid_1.whiley index 64c106eb04..6f10f01ae8 100644 --- a/tests/invalid/ProcessAccess_Invalid_1.whiley +++ b/tests/invalid/ProcessAccess_Invalid_1.whiley @@ -2,6 +2,6 @@ type etype is {int mode, ...} type Ptype is &etype -method get(Ptype this) -> int: - this.op = 1 - return this.mode +method get(Ptype _this) -> int: + _this.op = 1 + return _this.mode diff --git a/tests/invalid/ProcessAccess_Invalid_2.sysout b/tests/invalid/ProcessAccess_Invalid_2.sysout index c2528fe7f0..b54465f384 100644 --- a/tests/invalid/ProcessAccess_Invalid_2.sysout +++ b/tests/invalid/ProcessAccess_Invalid_2.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/ProcessAccess_Invalid_2.whiley:6: expected type ProcessAccess_Invalid_2:pState, found &{int x,int z} - this = new {z: 4, x: 3} - ^^^^ + _this = new {z: 4, x: 3} + ^^^^^ diff --git a/tests/invalid/ProcessAccess_Invalid_2.whiley b/tests/invalid/ProcessAccess_Invalid_2.whiley index 26169c52ef..fbade3ddf8 100644 --- a/tests/invalid/ProcessAccess_Invalid_2.whiley +++ b/tests/invalid/ProcessAccess_Invalid_2.whiley @@ -2,5 +2,5 @@ type state is {int y, int x} type pState is &state -method f(pState this) : - this = new {z: 4, x: 3} +method f(pState _this) : + _this = new {z: 4, x: 3} diff --git a/tests/invalid/ProcessAccess_Invalid_3.sysout b/tests/invalid/ProcessAccess_Invalid_3.sysout index 82800bdab2..9ab5ce1848 100644 --- a/tests/invalid/ProcessAccess_Invalid_3.sysout +++ b/tests/invalid/ProcessAccess_Invalid_3.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/ProcessAccess_Invalid_3.whiley:4: invalid assignment expression - p.data = this.data + p.data = _this.data ^^^^^^ diff --git a/tests/invalid/ProcessAccess_Invalid_3.whiley b/tests/invalid/ProcessAccess_Invalid_3.whiley index 856e6339f3..7a229e3e0c 100644 --- a/tests/invalid/ProcessAccess_Invalid_3.whiley +++ b/tests/invalid/ProcessAccess_Invalid_3.whiley @@ -1,7 +1,7 @@ type MyProc is &{int data} -method copy(MyProc this, MyProc p): - p.data = this.data +method copy(MyProc _this, MyProc p): + p.data = _this.data method create(int data) -> MyProc: return new {data: data} diff --git a/tests/invalid/Process_Invalid_1.sysout b/tests/invalid/Process_Invalid_1.sysout index 10c009a77c..9e5a7bea28 100644 --- a/tests/invalid/Process_Invalid_1.sysout +++ b/tests/invalid/Process_Invalid_1.sysout @@ -1,3 +1,3 @@ ../../tests/invalid/Process_Invalid_1.whiley:5: invalid assignment expression - this.data = d - ^^^^^^^^^ + _this.data = d + ^^^^^^^^^^ diff --git a/tests/invalid/Process_Invalid_1.whiley b/tests/invalid/Process_Invalid_1.whiley index 69f097a5c2..2ce16487f1 100644 --- a/tests/invalid/Process_Invalid_1.whiley +++ b/tests/invalid/Process_Invalid_1.whiley @@ -1,11 +1,11 @@ type MyProc1 is &{int data} type MyProc2 is &{any data} -method set(MyProc2 this, any d) : - this.data = d +method set(MyProc2 _this, any d) : + _this.data = d -method get(MyProc1 this) -> int: - return this.data +method get(MyProc1 _this) -> int: + return _this.data method create(int data) -> MyProc1: return new {data: data} diff --git a/tests/invalid/Subtype_Invalid_6.whiley b/tests/invalid/Subtype_Invalid_6.whiley index 53febce133..46d8e01bbb 100644 --- a/tests/invalid/Subtype_Invalid_6.whiley +++ b/tests/invalid/Subtype_Invalid_6.whiley @@ -1,7 +1,7 @@ type scf6nat is (int n) where n > 0 -type scf6tup is ({scf6nat f, int g} this) where this.g > this.f +type scf6tup is ({scf6nat f, int g} _this) where _this.g > _this.f function f(scf6tup x) -> int: return x.f diff --git a/tests/invalid/TupleAssign_Invalid_1.whiley b/tests/invalid/TupleAssign_Invalid_1.whiley index cab7436296..a45f58c340 100644 --- a/tests/invalid/TupleAssign_Invalid_1.whiley +++ b/tests/invalid/TupleAssign_Invalid_1.whiley @@ -1,5 +1,5 @@ -type tac1tup is ({int f1, int f2} this) where this.f1 < this.f2 +type tac1tup is ({int f1, int f2} _this) where _this.f1 < _this.f2 method main() : tac1tup x = {f1: 1, f2: 3} diff --git a/tests/invalid/TupleAssign_Invalid_2.whiley b/tests/invalid/TupleAssign_Invalid_2.whiley index d5da147024..63a20e62c1 100644 --- a/tests/invalid/TupleAssign_Invalid_2.whiley +++ b/tests/invalid/TupleAssign_Invalid_2.whiley @@ -1,7 +1,7 @@ -type tac2ta is ({int f1, int f2} this) where this.f1 < this.f2 +type tac2ta is ({int f1, int f2} _this) where _this.f1 < _this.f2 -type tac2tb is ({int f1, int f2} this) where (this.f1 + 1) < this.f2 +type tac2tb is ({int f1, int f2} _this) where (_this.f1 + 1) < _this.f2 function f(tac2tb y) -> tac2tb: return y diff --git a/tests/invalid/TupleAssign_Invalid_3.whiley b/tests/invalid/TupleAssign_Invalid_3.whiley index 2e9899f245..94e18e979c 100644 --- a/tests/invalid/TupleAssign_Invalid_3.whiley +++ b/tests/invalid/TupleAssign_Invalid_3.whiley @@ -1,5 +1,5 @@ -type tac3ta is ({int f1, int f2} this) where this.f1 < this.f2 +type tac3ta is ({int f1, int f2} _this) where _this.f1 < _this.f2 method main(): {int f1, int f2} x = {f1: 2, f2: 3} diff --git a/tests/invalid/TupleDefine_Invalid_2.whiley b/tests/invalid/TupleDefine_Invalid_2.whiley index e38aa6ec41..02c16757ed 100644 --- a/tests/invalid/TupleDefine_Invalid_2.whiley +++ b/tests/invalid/TupleDefine_Invalid_2.whiley @@ -1,4 +1,4 @@ -type point is ({int y, int x} this) where (this.x > 0) && (this.y > 0) +type point is ({int y, int x} _this) where (_this.x > 0) && (_this.y > 0) function f(point p) -> point: return p diff --git a/tests/invalid/UnionType_Invalid_7.whiley b/tests/invalid/UnionType_Invalid_7.whiley index 93362db63c..8fb42e0bc0 100644 --- a/tests/invalid/UnionType_Invalid_7.whiley +++ b/tests/invalid/UnionType_Invalid_7.whiley @@ -7,7 +7,7 @@ constant MUL is 3 constant DIV is 4 -type bop is ({int op, int rhs, int lhs} this) where ADD <= this.op && this.op <= DIV +type bop is ({int op, int rhs, int lhs} _this) where ADD <= _this.op && _this.op <= DIV function f(bop b) -> bop: return b diff --git a/tests/valid/ConstrainedList_Valid_11.whiley b/tests/valid/ConstrainedList_Valid_11.whiley index 308285eece..889a6cc037 100644 --- a/tests/valid/ConstrainedList_Valid_11.whiley +++ b/tests/valid/ConstrainedList_Valid_11.whiley @@ -1,8 +1,8 @@ type state is ({ int[] input, int pos -} this) -where (this.pos >= 0) && (this.pos <= |this.input|) +} _this) +where (_this.pos >= 0) && (_this.pos <= |_this.input|) public function isLetter(int c) -> bool: return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') diff --git a/tests/valid/FunctionRef_Valid_12.whiley b/tests/valid/FunctionRef_Valid_12.whiley index 60c8adf291..55f9f6a422 100644 --- a/tests/valid/FunctionRef_Valid_12.whiley +++ b/tests/valid/FunctionRef_Valid_12.whiley @@ -8,7 +8,7 @@ function f(null|SizeGetter x) -> int: else: return 0 -function getSize(Sized this) -> int: +function getSize(Sized _this) -> int: return 1 public export method test(): diff --git a/tests/valid/FunctionRef_Valid_13.whiley b/tests/valid/FunctionRef_Valid_13.whiley index f1bd116b6a..5cc1361b1e 100644 --- a/tests/valid/FunctionRef_Valid_13.whiley +++ b/tests/valid/FunctionRef_Valid_13.whiley @@ -9,10 +9,10 @@ function f((SizeSetter|SizeGetter) x) -> int: else: return 0 -function getSize(Sized this) -> int: - return this.value +function getSize(Sized _this) -> int: + return _this.value -function setSize(Sized this, int value) -> Sized: +function setSize(Sized _this, int value) -> Sized: return { value: value } public export method test(): diff --git a/tests/valid/FunctionRef_Valid_7.whiley b/tests/valid/FunctionRef_Valid_7.whiley index 0b1b94696c..3a750cb6b0 100644 --- a/tests/valid/FunctionRef_Valid_7.whiley +++ b/tests/valid/FunctionRef_Valid_7.whiley @@ -2,8 +2,8 @@ type Proc is &{int data} -method read(Proc this, int x) -> int: - return x + this->data +method read(Proc _this, int x) -> int: + return x + _this->data public export method test(Proc p, int arg) -> int: return read(p,arg) diff --git a/tests/valid/FunctionRef_Valid_9.whiley b/tests/valid/FunctionRef_Valid_9.whiley index 87b6ca6335..f09ee414a6 100644 --- a/tests/valid/FunctionRef_Valid_9.whiley +++ b/tests/valid/FunctionRef_Valid_9.whiley @@ -4,11 +4,11 @@ type Proc is &{ function func(int) -> int } -method func(Proc this, int x) -> int: +method func(Proc _this, int x) -> int: return x + 1 -public export method test(Proc this, int arg) -> int: - return (*this).func(arg) +public export method test(Proc _this, int arg) -> int: + return (*_this).func(arg) function id(int x) -> int: return x diff --git a/tests/valid/Lambda_Valid_3.whiley b/tests/valid/Lambda_Valid_3.whiley index 642290eece..45224c11bd 100644 --- a/tests/valid/Lambda_Valid_3.whiley +++ b/tests/valid/Lambda_Valid_3.whiley @@ -54,8 +54,8 @@ requires amount >= 0: // Construct buffer from list of bytes public method BufferInputStream(byte[] buffer) -> InputStream: - BufferState this = new {bytes: buffer, pos: 0} - return {read: &(int x -> read(this, x))} + BufferState _this = new {bytes: buffer, pos: 0} + return {read: &(int x -> read(_this, x))} public export method test() : InputStream bis = BufferInputStream(toBytes("hello")) diff --git a/tests/valid/Lambda_Valid_4.whiley b/tests/valid/Lambda_Valid_4.whiley index 6c83a75e79..bbd54c9d50 100644 --- a/tests/valid/Lambda_Valid_4.whiley +++ b/tests/valid/Lambda_Valid_4.whiley @@ -59,10 +59,10 @@ method eof(BufferState state) -> bool: // Construct buffer from list of bytes public method BufferInputStream(byte[] buffer) -> InputStream: - BufferState this = new {bytes: buffer, pos: 0} + BufferState _this = new {bytes: buffer, pos: 0} return { - read: &(int x -> read(this, x)), - eof: &( -> eof(this)) + read: &(int x -> read(_this, x)), + eof: &( -> eof(_this)) } method read(string s) -> byte[]: diff --git a/tests/valid/Lifetime_Lambda_Valid_1.whiley b/tests/valid/Lifetime_Lambda_Valid_1.whiley new file mode 100644 index 0000000000..76d7943059 --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_1.whiley @@ -0,0 +1,5 @@ +type meth is method(&a:int, &b:int)->(&a:int) + +public export method test(): + meth m = &(&a:int x, &b:int y -> a:new 1) + m = &(&a:int x, &b:int y -> new 1) diff --git a/tests/valid/Lifetime_Lambda_Valid_2.whiley b/tests/valid/Lifetime_Lambda_Valid_2.whiley new file mode 100644 index 0000000000..247c99c467 --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_2.whiley @@ -0,0 +1,9 @@ +// Test that lifetime parameters are replaced without capturing nested parameters in return types + +type meth1 is method(&a:int, &b:int)->(&b:meth2) +type meth2 is method(&a:int, &b:int)->(int) + +public export method test(): + meth2 m2 = &(&aa:int x, &bb:int y -> *(new 1)) + meth1 m1 = &(&bb:int x, &aa:int y -> aa:new m2) + &*:meth2 m3 = m1(this:new 2, new 3) diff --git a/tests/valid/Lifetime_Lambda_Valid_3.whiley b/tests/valid/Lifetime_Lambda_Valid_3.whiley new file mode 100644 index 0000000000..dceaf4722a --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_3.whiley @@ -0,0 +1,12 @@ +// Test that variable declarations are parsed correctly +// (tricky because of headless statements and definite types) + +type myint is int + +public export method test(): + method()->(&myint) m = &(-> new 1) + &myint a = m() + &*:myint b = m() + &this:myint c = m() + myblock: + &myblock:myint d = m() diff --git a/tests/valid/Lifetime_Lambda_Valid_4.whiley b/tests/valid/Lifetime_Lambda_Valid_4.whiley new file mode 100644 index 0000000000..fe232c1b39 --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_4.whiley @@ -0,0 +1,16 @@ +// Test recursive types + +type mymethod is method(&*:mymethod)->(&a:mymethod) + +method m (&*:mymethod x) -> &a:mymethod: + return a:new &m + +public export method test(): + &mymethod x = m<*>(new &m) + mymethod xx = &(&mymethod y -> *(new y)) + xx = &(&mymethod y -> *(new y)) + &this:mymethod y = m(new &m) + mymethod yy = &(&mymethod z -> *(new z)) + yy = &(&mymethod z -> *(new z)) + a: + &a:mymethod z = m(new &m) diff --git a/tests/valid/Lifetime_Lambda_Valid_5.whiley b/tests/valid/Lifetime_Lambda_Valid_5.whiley new file mode 100644 index 0000000000..48fdc0f73c --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_5.whiley @@ -0,0 +1,6 @@ +// Test contra-variant parameter types + +public export method test(): + method(&a:int, &int)->(&int) m1 = &(&b:int x, &b:int y -> new 1) + method(&a:int, &int)->(&int) m2 = &(&b:int x, &b:int y -> new 1) + method(&a:int, &b:int)->(&a:int) m3 = &(&b:int x, &a:int y -> b:new 1) diff --git a/tests/valid/Lifetime_Lambda_Valid_6.whiley b/tests/valid/Lifetime_Lambda_Valid_6.whiley new file mode 100644 index 0000000000..7c15d2c706 --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_6.whiley @@ -0,0 +1,11 @@ +// Test co-variant return types + +public export method test(): + method(&a:int)->(&a:int) m1 = &(&b:int x -> new 1) + &int xx = m1(new 2) + assume (*xx) == 1 + method(&a:int)->(&a:int|&b:bool) m2 = &(&b:int x -> c:new true) + c: + d: + &this:bool yy = m2(c:new 1) + assume (*yy) == true diff --git a/tests/valid/Lifetime_Lambda_Valid_7.whiley b/tests/valid/Lifetime_Lambda_Valid_7.whiley new file mode 100644 index 0000000000..dd83bc5a6d --- /dev/null +++ b/tests/valid/Lifetime_Lambda_Valid_7.whiley @@ -0,0 +1,26 @@ +// Test context lifetimes + +public export method test(): + &this:int x = this:new 2 + function()->(&this:int) m1 = &(->x) + int i = *m1() + assume i == 2 + + method[this]()->(int) m2 = &[this](->*x) + i = m2() + assume i == 2 + *x = 3 + i = m2() + assume i == 3 + + a: + &a:int y = a:new 4 + method[this,a]()->(int) m3 = &[this](->*x) + i = m3() + assume i == 3 + m3 = &[this,a](->*x) + i = m3() + assume i == 3 + method[this,a]()->(int) m4 = &[this,a](->*y) + i = m4() + assume i == 4 diff --git a/tests/valid/Lifetime_Valid_1.whiley b/tests/valid/Lifetime_Valid_1.whiley new file mode 100644 index 0000000000..f63beb625c --- /dev/null +++ b/tests/valid/Lifetime_Valid_1.whiley @@ -0,0 +1,45 @@ +// test different lifetimes without subtyping + +public export method test(): + &int ptr1 = new 1 + assume (*ptr1) == 1 + + &*:int ptr2 = new 2 + assume (*ptr2) == 2 + + &int ptr3 = *:new 3 + assume (*ptr3) == 3 + + &this:int ptr4 = this:new 4 + assume (*ptr4) == 4 + + myblock: + &myblock:int ptr5 = myblock:new 5 + assume (*ptr5) == 5 + + mynestedblock: + &mynestedblock:int ptr6 = mynestedblock:new 6 + assume (*ptr6) == 6 + + &myblock:int ptr7 = myblock:new 7 + assume (*ptr7) == 7 + + &int ptr8 = *:new 8 + assume (*ptr8) == 8 + + &this:int ptr9 = this:new 9 + assume (*ptr9) == 9 + + &myblock:int ptr10 = myblock:new 10 + assume (*ptr10) == 10 + + &*:int ptr11 = *:new 11 + assume (*ptr11) == 11 + + // name should be available again + mynestedblock: + &mynestedblock:int ptr12 = mynestedblock:new 12 + assume (*ptr12) == 12 + + &this:int ptr13 = this:new 13 + assume (*ptr13) == 13 diff --git a/tests/valid/Lifetime_Valid_10.whiley b/tests/valid/Lifetime_Valid_10.whiley new file mode 100644 index 0000000000..f90fa2e80f --- /dev/null +++ b/tests/valid/Lifetime_Valid_10.whiley @@ -0,0 +1,12 @@ +// test that substitution does not accidentaly substitute already substituted values + +method m(&a:int x, &b:int y) -> &a:int: + return x + +public export method test(): + a: + b: + &a:int x = a:new 1 + &b:int y = b:new 2 + &a:int z = m(x, y) + assume x == z diff --git a/tests/valid/Lifetime_Valid_2.whiley b/tests/valid/Lifetime_Valid_2.whiley new file mode 100644 index 0000000000..106eadd561 --- /dev/null +++ b/tests/valid/Lifetime_Valid_2.whiley @@ -0,0 +1,22 @@ +// test subtyping (outlives) with different lifetimes + +public export method test(): + &this:int ptr1 = *:new 1 + assume (*ptr1) == 1 + + myblock: + &myblock:int ptr2 = this:new 2 + assume (*ptr2) == 2 + + &myblock:int ptr3 = *:new 3 + assume (*ptr3) == 3 + + mynestedblock: + &mynestedblock:int ptr4 = myblock:new 4 + assume (*ptr4) == 4 + + &mynestedblock:int ptr5 = this:new 5 + assume (*ptr5) == 5 + + &mynestedblock:int ptr6 = *:new 6 + assume (*ptr6) == 6 diff --git a/tests/valid/Lifetime_Valid_3.whiley b/tests/valid/Lifetime_Valid_3.whiley new file mode 100644 index 0000000000..755dc3a9f4 --- /dev/null +++ b/tests/valid/Lifetime_Valid_3.whiley @@ -0,0 +1,61 @@ +// test subtyping (outlives) combined with lifetime parameters + +method m(&a:int x, &b:int y, &*:int z): + &a:int ptr1 = x + ptr1 = a:new 1 + assume (*ptr1) == 1 + + &b:int ptr2 = y + ptr2 = b:new 2 + assume (*ptr2) == 2 + + &a:int ptr3 = *:new 3 + assume (*ptr3) == 3 + + &b:int ptr4 = *:new 4 + assume (*ptr4) == 4 + + &this:int ptr5 = a:new 5 + assume (*ptr5) == 5 + ptr5 = x + ptr5 = y + + &this:int ptr6 = b:new 6 + assume (*ptr6) == 6 + ptr6 = y + ptr6 = x + +public export method test(): + &this:int ptr1 = this:new 1 + assume (*ptr1) == 1 + + m(ptr1, ptr1, new 99) + + myblock: + &myblock:int ptr2 = myblock:new 2 + assume (*ptr2) == 2 + + m(ptr1, ptr2, new 99) + m(ptr2, ptr1, new 99) + m(ptr2, ptr2, new 99) + + &myblock:int ptr3 = *:new 3 + assume (*ptr3) == 3 + + m(ptr1, ptr3, new 99) + m(ptr2, ptr3, new 99) + m(ptr3, ptr1, new 99) + m(ptr3, ptr2, new 99) + m(ptr3, ptr3, new 99) + + mynestedblock: + &mynestedblock:int ptr4 = mynestedblock:new 4 + assume (*ptr4) == 4 + + m(ptr1, ptr4, new 99) + m(ptr2, ptr4, new 99) + m(ptr3, ptr4, new 99) + m(ptr4, ptr1, new 99) + m(ptr4, ptr2, new 99) + m(ptr4, ptr3, new 99) + m(ptr4, ptr4, new 99) diff --git a/tests/valid/Lifetime_Valid_4.whiley b/tests/valid/Lifetime_Valid_4.whiley new file mode 100644 index 0000000000..5b887e1d47 --- /dev/null +++ b/tests/valid/Lifetime_Valid_4.whiley @@ -0,0 +1,19 @@ +// test subtyping (outlives) for method parameters and return types + +method m(&a:int x, &a:int y) -> &*:int: + return new 3 + +public export method test(): + &this:int ptr1 = this:new 1 + &*:int ptr2 = *:new 2 + + &this:int ptr3 = m(ptr1, ptr1) + assume (*ptr3) == 3 + ptr3 = m<*>(ptr2, ptr2) + assume (*ptr3) == 3 + ptr3 = m(ptr2, ptr2) + assume (*ptr3) == 3 + ptr3 = m(ptr1, ptr2) + assume (*ptr3) == 3 + ptr3 = m(ptr2, ptr1) + assume (*ptr3) == 3 diff --git a/tests/valid/Lifetime_Valid_5.whiley b/tests/valid/Lifetime_Valid_5.whiley new file mode 100644 index 0000000000..2d85676817 --- /dev/null +++ b/tests/valid/Lifetime_Valid_5.whiley @@ -0,0 +1,34 @@ +// test lifetime substitution for method calls + +method m1(&a:int x, &b:int y) -> &a:int: + *x = (*x)+1 + &a:int result = a:new (*y) + return result + +method m2(&a:int x, &b:int y) -> &b:int: + *y = (*y)+1 + &b:int result = *:new (*x) + return result + +public export method test(): + test: + &this:int ptr1 = this:new 1 + &test:int ptr2 = test:new 2 + + &this:int ptr3 = m1(ptr1, ptr2) + assume (*ptr1) == 2 + assume (*ptr2) == 2 + assume (*ptr3) == 2 + + &test:int ptr4 = m2(ptr1, ptr2) + assume (*ptr1) == 2 + assume (*ptr2) == 3 + assume (*ptr3) == 2 + assume (*ptr4) == 2 + + &test:int ptr5 = m1(ptr2, ptr2) + assume (*ptr1) == 2 + assume (*ptr2) == 4 + assume (*ptr3) == 2 + assume (*ptr4) == 2 + assume (*ptr5) == 4 diff --git a/tests/valid/Lifetime_Valid_6.whiley b/tests/valid/Lifetime_Valid_6.whiley new file mode 100644 index 0000000000..82e433a0f3 --- /dev/null +++ b/tests/valid/Lifetime_Valid_6.whiley @@ -0,0 +1,33 @@ +// test lifetime inference without overloading + +method m1(&a:int x, &a:int y, &b:int z) -> &a:int: + return new 3 + +method m2(&a:int x, &b:int y, &b:int z) -> &b:int: + return new 4 + +public export method test(): + test: + &this:int ptr1 = this:new 1 + &test:int ptr2 = test:new 2 + &*:int ptr3 = *:new 2 + + // + &test:int ptr4 = m1(ptr1, ptr2, ptr3) + + // + &this:int ptr5 = m1(ptr1, ptr3, ptr2) + + // <*, this> + &*:int ptr6 = m1(ptr3, ptr3, ptr1) + + // -- + + // + &test:int ptr7 = m2(ptr1, ptr2, ptr3) + + // + &this:int ptr8 = m2(ptr1, ptr3, ptr1) + + // + &*:int ptr9 = m2(ptr1, ptr3, ptr3) diff --git a/tests/valid/Lifetime_Valid_7.whiley b/tests/valid/Lifetime_Valid_7.whiley new file mode 100644 index 0000000000..d934ffc401 --- /dev/null +++ b/tests/valid/Lifetime_Valid_7.whiley @@ -0,0 +1,45 @@ +// test lifetime inference with lifetime overloading but same types + +method m(&a:int x, &a:int y) -> int: + return 1 + +method m(&a:int x, &*:int y) -> int: + return 2 + +method m(&*:int x, &a:int y) -> int: + return 3 + +method m2(&a:int x, &a:int y) -> int: + return 4 + +method m2(&a:int x, &b:int y) -> int: + return 5 + +public export method test(): + test: + &this:int ptr1 = this:new 1 + &test:int ptr2 = test:new 2 + &*:int ptr3 = *:new 2 + + // variant1: (&test:int, &test:int) <-- + // variant2: no + // variant3: no + int r = m(ptr1, ptr2) + assume r == 1 + + // variant1: (&this:int, &this:int) + // variant2: no + // variant3: (&*:int, &this:int) <-- + r = m(ptr3, ptr1) + assume r == 3 + + // variant1: (&test:int, &test:int) + // variant2: (&test:int, &*:int) <-- + // variant3: no + r = m(ptr2, ptr3) + assume r == 2 + + // variant4: (&test:int, &test:int) + // variant5: (&test:int, &this:int) <-- + r = m2(ptr2, ptr3) + assume r == 5 diff --git a/tests/valid/Lifetime_Valid_8.whiley b/tests/valid/Lifetime_Valid_8.whiley new file mode 100644 index 0000000000..5ef621971e --- /dev/null +++ b/tests/valid/Lifetime_Valid_8.whiley @@ -0,0 +1,28 @@ +// test lifetime inference with lifetime overloading and different types + +method m((&a:int)|(&b:bool) x, &a:int y, bool z) -> &b:int: + return b:new 1 + +method m(&a:bool x, &a:int y, bool z) -> &a:int: + return a:new 2 + +method m(&*:int x, &a:int y, int|bool z) -> &a:int: + return a:new 3 + +method m(&*:int x, &a:int y, bool z) -> &a:int: + return a:new 4 + +public export method test(): + // variant1: ((&this:int)|(&*:bool), &this:int, bool z) + // variant2: no + // variant3: no + // variant4: no + &*:int p1 = m(this:new 1, this:new 2, true) + assume (*p1) == 1 + + // variant1: ((&this:int)|(&*:bool), &this:int, bool z) + // variant2: no + // variant3: (&*:int, &this:int, int|bool) + // variant4: (&*:int, &this:int, bool) <-- + &this:int p2 = m(*:new 1, this:new 2, true) + assume (*p2) == 4 diff --git a/tests/valid/Lifetime_Valid_9.whiley b/tests/valid/Lifetime_Valid_9.whiley new file mode 100644 index 0000000000..d9030b5a22 --- /dev/null +++ b/tests/valid/Lifetime_Valid_9.whiley @@ -0,0 +1,14 @@ +// use lifetime arguments to disambiguate (see also Lifetime_Invalid_8) + +method m((&a:int)|(&b:bool) x, (&a:int)|(&b:int) y) -> &a:int: + return a:new 1 + +public export method test(): + // --> ((&this:int)|(&*:bool), &this:int) + // <*, this> --> ((&*:int)|(&this:bool), &this:int) + (&*:int)|(&*:bool) x = *:new 1 + &this:int y = this:new 1 + + &this:int p1 = m(x, y) + &*:int p2 = m<*, this>(x, y) + &this:int p3 = m(x, y) diff --git a/tests/valid/MessageRef_Valid_1.whiley b/tests/valid/MessageRef_Valid_1.whiley index 77e1a59626..cebc90ecda 100644 --- a/tests/valid/MessageRef_Valid_1.whiley +++ b/tests/valid/MessageRef_Valid_1.whiley @@ -4,7 +4,7 @@ type MyProc is &{int position} type MyMeth is method(MyProc, int) -> int -method read(MyProc this, int x) -> int: +method read(MyProc _this, int x) -> int: return x + 123 public export method test(MyMeth m, MyProc proc) -> int: diff --git a/tests/valid/MessageRef_Valid_2.whiley b/tests/valid/MessageRef_Valid_2.whiley index 2b23331e2b..5f125209a4 100644 --- a/tests/valid/MessageRef_Valid_2.whiley +++ b/tests/valid/MessageRef_Valid_2.whiley @@ -7,8 +7,8 @@ type Reader is { method read(FileReader, int) -> int } -method read(FileReader this, int amount) -> int: - int r = amount + this->position +method read(FileReader _this, int amount) -> int: + int r = amount + _this->position return r method openReader() -> Reader: diff --git a/tests/valid/MessageSend_Valid_1.whiley b/tests/valid/MessageSend_Valid_1.whiley index 0fbb2a4a9a..9348c5b5bf 100644 --- a/tests/valid/MessageSend_Valid_1.whiley +++ b/tests/valid/MessageSend_Valid_1.whiley @@ -2,7 +2,7 @@ type MyObject is &{int field} -method f(MyObject this, int x) : +method f(MyObject _this, int x) : assume x == 1 public export method test() : diff --git a/tests/valid/MessageSend_Valid_2.whiley b/tests/valid/MessageSend_Valid_2.whiley index b1dd660be7..570b67afee 100644 --- a/tests/valid/MessageSend_Valid_2.whiley +++ b/tests/valid/MessageSend_Valid_2.whiley @@ -2,8 +2,8 @@ type Proc is &{int state} -method get(Proc this) -> int: - return this->state +method get(Proc _this) -> int: + return _this->state method f(Proc x) -> int: return get(x) diff --git a/tests/valid/MessageSend_Valid_3.whiley b/tests/valid/MessageSend_Valid_3.whiley index 208cda363b..eb8e53c094 100644 --- a/tests/valid/MessageSend_Valid_3.whiley +++ b/tests/valid/MessageSend_Valid_3.whiley @@ -2,8 +2,8 @@ type Proc is &{int state} -method get(Proc this) -> int: - return this->state +method get(Proc _this) -> int: + return _this->state method f(Proc x) -> int[]: return [1, 2, 3, get(x)] diff --git a/tests/valid/MessageSend_Valid_4.whiley b/tests/valid/MessageSend_Valid_4.whiley index 3ec5bc69b4..757cee2b2b 100644 --- a/tests/valid/MessageSend_Valid_4.whiley +++ b/tests/valid/MessageSend_Valid_4.whiley @@ -4,8 +4,8 @@ type wmcr6tup is {int y, int x} type Proc is &{int state} -method get(Proc this) -> int: - return this->state +method get(Proc _this) -> int: + return _this->state method f(Proc x, int y) -> wmcr6tup: return {y: get(x), x: y} diff --git a/tests/valid/MessageSend_Valid_5.whiley b/tests/valid/MessageSend_Valid_5.whiley index 6c3a585f85..937e5804b0 100644 --- a/tests/valid/MessageSend_Valid_5.whiley +++ b/tests/valid/MessageSend_Valid_5.whiley @@ -2,17 +2,17 @@ type Sum is &{int result, int[] items} -method start(Sum this) : +method start(Sum _this) : int sum = 0 int i = 0 - int[] items = this->items + int[] items = _this->items while i < |items| where i >= 0: sum = sum + items[i] i = i + 1 - this->result = sum + _this->result = sum -method get(Sum this) -> int: - return this->result +method get(Sum _this) -> int: + return _this->result method create(int[] items) -> Sum: return new {result: 0, items: items} diff --git a/tests/valid/MethodCall_Valid_4.whiley b/tests/valid/MethodCall_Valid_4.whiley index 8f0c884410..75c14a92f7 100644 --- a/tests/valid/MethodCall_Valid_4.whiley +++ b/tests/valid/MethodCall_Valid_4.whiley @@ -1,16 +1,16 @@ type Sum is &{int result, int[] items} -method start(Sum this) : +method start(Sum _this) : int sum = 0 int i = 0 - int[] items = this->items + int[] items = _this->items while i < |items| where i >= 0: sum = sum + items[i] i = i + 1 - this->result = sum + _this->result = sum -method get(Sum this) -> int: - return this->result +method get(Sum _this) -> int: + return _this->result method create(int[] items) -> Sum: return new {result: 0, items: items} diff --git a/tests/valid/ProcessAccess_Valid_1.whiley b/tests/valid/ProcessAccess_Valid_1.whiley index e76b483646..603e56f483 100644 --- a/tests/valid/ProcessAccess_Valid_1.whiley +++ b/tests/valid/ProcessAccess_Valid_1.whiley @@ -1,10 +1,10 @@ type etype is {int rest, int mode} type Ptype is &etype -method get(Ptype this) -> int: - this->mode = 1 - this->rest = 123 - return this->mode +method get(Ptype _this) -> int: + _this->mode = 1 + _this->rest = 123 + return _this->mode public export method test() : Ptype p = new {rest: 2, mode: 2} diff --git a/tests/valid/ProcessAccess_Valid_2.whiley b/tests/valid/ProcessAccess_Valid_2.whiley index 3176f7fd62..88c6b5501f 100644 --- a/tests/valid/ProcessAccess_Valid_2.whiley +++ b/tests/valid/ProcessAccess_Valid_2.whiley @@ -2,9 +2,9 @@ type state is {int y, int x} type pState is &state -method send(pState this, int z) : - assume this->x == 1 - assume this->y == 2 +method send(pState _this, int z) : + assume _this->x == 1 + assume _this->y == 2 assume z == 1 public export method test() : diff --git a/tests/valid/Process_Valid_1.whiley b/tests/valid/Process_Valid_1.whiley index 6b9c87512e..b1506efaec 100644 --- a/tests/valid/Process_Valid_1.whiley +++ b/tests/valid/Process_Valid_1.whiley @@ -4,10 +4,10 @@ type state is {int y, int x} type pState is &state -method send(pState this, int x) : - this->x = x - assert this->x == x - assume (*this) == {x: 3, y: 2} +method send(pState _this, int x) : + _this->x = x + assert _this->x == x + assume (*_this) == {x: 3, y: 2} public export method test() : pState ps = new {y: 2, x: 1} diff --git a/tests/valid/Process_Valid_10.whiley b/tests/valid/Process_Valid_10.whiley index 519ece4278..a21b1238eb 100644 --- a/tests/valid/Process_Valid_10.whiley +++ b/tests/valid/Process_Valid_10.whiley @@ -2,16 +2,16 @@ type Queue is {int[] items, int length} -method get(&Queue this) -> int: - this->length = this->length - 1 - return this->items[this->length] +method get(&Queue _this) -> int: + _this->length = _this->length - 1 + return _this->items[_this->length] -method put(&Queue this, int item) : - this->items[this->length] = item - this->length = this->length + 1 +method put(&Queue _this, int item) : + _this->items[_this->length] = item + _this->length = _this->length + 1 -method isEmpty(&Queue this) -> bool: - return this->length == 0 +method isEmpty(&Queue _this) -> bool: + return _this->length == 0 public export method test() : int[] items = [1, 2, 3, 4, 5] diff --git a/tests/valid/Process_Valid_11.whiley b/tests/valid/Process_Valid_11.whiley index bbc7f6ccf5..3e7b97fb7f 100644 --- a/tests/valid/Process_Valid_11.whiley +++ b/tests/valid/Process_Valid_11.whiley @@ -4,8 +4,8 @@ type state is ({int y, int x} s) where s.x < s.y type pState is &state -method send2(pState this, int x) -> int: - return this->x + this->y +method send2(pState _this, int x) -> int: + return _this->x + _this->y public export method test() : int x = send2(new {y: 2, x: 1},1) diff --git a/tests/valid/Process_Valid_12.whiley b/tests/valid/Process_Valid_12.whiley index 9c694dfe78..5f1389995b 100644 --- a/tests/valid/Process_Valid_12.whiley +++ b/tests/valid/Process_Valid_12.whiley @@ -4,8 +4,8 @@ type state is {int y, int x} type pState is &state -method send2(pState this, int x) -> int: - return this->x + this->y +method send2(pState _this, int x) -> int: + return _this->x + _this->y public export method test() : int x = send2(new {y: 2, x: 1},1) diff --git a/tests/valid/Process_Valid_4.whiley b/tests/valid/Process_Valid_4.whiley index 882c1a2c94..896d829ca3 100644 --- a/tests/valid/Process_Valid_4.whiley +++ b/tests/valid/Process_Valid_4.whiley @@ -2,8 +2,8 @@ type MyProc is &{int x} -method inc(MyProc this, int i) : - this->x = this->x + i +method inc(MyProc _this, int i) : + _this->x = _this->x + i public export method test() : MyProc mproc = new {x: 1} diff --git a/tests/valid/Process_Valid_5.whiley b/tests/valid/Process_Valid_5.whiley index 704d1647dd..0392e3e483 100644 --- a/tests/valid/Process_Valid_5.whiley +++ b/tests/valid/Process_Valid_5.whiley @@ -2,8 +2,8 @@ type MyProc is &{bool flag} -method run(MyProc this) -> bool: - if this->flag: +method run(MyProc _this) -> bool: + if _this->flag: return true else: return false diff --git a/tests/valid/Process_Valid_6.whiley b/tests/valid/Process_Valid_6.whiley index 02b7c048ed..c5550c113c 100644 --- a/tests/valid/Process_Valid_6.whiley +++ b/tests/valid/Process_Valid_6.whiley @@ -1,7 +1,7 @@ type Actor is {int data} -method get(&Actor this) -> int: - return this->data +method get(&Actor _this) -> int: + return _this->data method createActor(int n) -> &Actor: return new {data: n} diff --git a/tests/valid/Process_Valid_7.whiley b/tests/valid/Process_Valid_7.whiley index 22af37a9e9..22b9b0fa35 100644 --- a/tests/valid/Process_Valid_7.whiley +++ b/tests/valid/Process_Valid_7.whiley @@ -2,11 +2,11 @@ type MyProc2 is &{any data} -method set(MyProc2 this, int d) : - this->data = (any)d +method set(MyProc2 _this, int d) : + _this->data = (any)d -method get(MyProc2 this) -> any: - return this->data +method get(MyProc2 _this) -> any: + return _this->data method create(any data) -> MyProc2: return new {data: data} diff --git a/tests/valid/Process_Valid_8.whiley b/tests/valid/Process_Valid_8.whiley index 09fa89bf9c..acc9944953 100644 --- a/tests/valid/Process_Valid_8.whiley +++ b/tests/valid/Process_Valid_8.whiley @@ -2,11 +2,11 @@ type MyProc2 is {any data} -method set(&MyProc2 this, int d) : - this->data = (any)d +method set(&MyProc2 _this, int d) : + _this->data = (any)d -method get(&MyProc2 this) -> any: - return this->data +method get(&MyProc2 _this) -> any: + return _this->data method create(any data) -> &MyProc2: return new {data: data} diff --git a/tests/valid/Process_Valid_9.whiley b/tests/valid/Process_Valid_9.whiley index 40116c4351..f2da3306ab 100644 --- a/tests/valid/Process_Valid_9.whiley +++ b/tests/valid/Process_Valid_9.whiley @@ -1,15 +1,15 @@ type Queue is {int[] items, int length} -method get(&Queue this) -> int: - this->length = this->length - 1 - return this->items[this->length] +method get(&Queue _this) -> int: + _this->length = _this->length - 1 + return _this->items[_this->length] -method put(&Queue this, int item) : - this->items[this->length] = item - this->length = this->length + 1 +method put(&Queue _this, int item) : + _this->items[_this->length] = item + _this->length = _this->length + 1 -method isEmpty(&Queue this) -> bool: - return this->length == 0 +method isEmpty(&Queue _this) -> bool: + return _this->length == 0 method Queue(int capacity) -> &Queue: int[] slots = [0; capacity] diff --git a/tests/valid/RecordAccess_Valid_1.whiley b/tests/valid/RecordAccess_Valid_1.whiley index 1ace8158aa..47386ff15e 100644 --- a/tests/valid/RecordAccess_Valid_1.whiley +++ b/tests/valid/RecordAccess_Valid_1.whiley @@ -4,9 +4,9 @@ type etype is {int mode, ...} type Ptype is &etype -method get(Ptype this) -> int: - this->mode = 1 - return this->mode +method get(Ptype _this) -> int: + _this->mode = 1 + return _this->mode public export method test() : assume get(new (etype) {mode:2}) == 1 diff --git a/tests/valid/RecordAssign_Valid_1.whiley b/tests/valid/RecordAssign_Valid_1.whiley index fa83a8f147..8986b1febe 100644 --- a/tests/valid/RecordAssign_Valid_1.whiley +++ b/tests/valid/RecordAssign_Valid_1.whiley @@ -1,6 +1,6 @@ -type tac1tup is ({int f1, int f2} this) where this.f1 < this.f2 +type tac1tup is ({int f1, int f2} _this) where _this.f1 < _this.f2 function f() -> tac1tup: return {f1: 2, f2: 3} diff --git a/tests/valid/RecordAssign_Valid_4.whiley b/tests/valid/RecordAssign_Valid_4.whiley index 70691fe9f6..f0d903b810 100644 --- a/tests/valid/RecordAssign_Valid_4.whiley +++ b/tests/valid/RecordAssign_Valid_4.whiley @@ -1,8 +1,8 @@ -type tac2ta is ({int f1, int f2} this) where this.f1 < this.f2 +type tac2ta is ({int f1, int f2} _this) where _this.f1 < _this.f2 -type tac2tb is ({int f1, int f2} this) where (this.f1 + 1) < this.f2 +type tac2tb is ({int f1, int f2} _this) where (_this.f1 + 1) < _this.f2 function f(tac2ta x) -> tac2tb: return {f1: x.f1 - 1, f2: x.f2} diff --git a/tests/valid/RecursiveType_Valid_11.whiley b/tests/valid/RecursiveType_Valid_11.whiley index dcc8a92fa2..bceb5b8a1d 100644 --- a/tests/valid/RecursiveType_Valid_11.whiley +++ b/tests/valid/RecursiveType_Valid_11.whiley @@ -5,8 +5,8 @@ constant SUB is 2 constant MUL is 3 constant DIV is 4 -type binop is ({int op, expr left, expr right} this) -where this.op == ADD || this.op == SUB || this.op == MUL || this.op == DIV +type binop is ({int op, expr left, expr right} _this) +where _this.op == ADD || _this.op == SUB || _this.op == MUL || _this.op == DIV type expr is int | binop diff --git a/tests/valid/RecursiveType_Valid_12.whiley b/tests/valid/RecursiveType_Valid_12.whiley index 0bbc51fe26..bc6f787770 100644 --- a/tests/valid/RecursiveType_Valid_12.whiley +++ b/tests/valid/RecursiveType_Valid_12.whiley @@ -2,8 +2,8 @@ type Tree is null | Node -type Node is ({int data, Tree rhs, Tree lhs} this) -where ((this.lhs == null) || (this.lhs.data < this.data)) && ((this.rhs == null) || (this.rhs.data > this.data)) +type Node is ({int data, Tree rhs, Tree lhs} _this) +where ((_this.lhs == null) || (_this.lhs.data < _this.data)) && ((_this.rhs == null) || (_this.rhs.data > _this.data)) function Tree(int data, Tree left, Tree right) -> Tree requires ((left == null) || (left.data < data)) && ((right == null) || (right.data > data)): diff --git a/tests/valid/RecursiveType_Valid_14.whiley b/tests/valid/RecursiveType_Valid_14.whiley index b7081a2a68..90b46251c9 100644 --- a/tests/valid/RecursiveType_Valid_14.whiley +++ b/tests/valid/RecursiveType_Valid_14.whiley @@ -8,9 +8,9 @@ constant MUL is 3 constant DIV is 4 -type binop is ({int op, Expr left, Expr right} this) where this.op == ADD || this.op == SUB || this.op == MUL || this.op == DIV +type binop is ({int op, Expr left, Expr right} _this) where _this.op == ADD || _this.op == SUB || _this.op == MUL || _this.op == DIV -type asbinop is ({int op, Expr left, Expr right} this) where this.op == ADD || this.op == SUB +type asbinop is ({int op, Expr left, Expr right} _this) where _this.op == ADD || _this.op == SUB type Expr is int | binop diff --git a/tests/valid/RecursiveType_Valid_22.whiley b/tests/valid/RecursiveType_Valid_22.whiley index 3370cc3146..cccf5b7b09 100644 --- a/tests/valid/RecursiveType_Valid_22.whiley +++ b/tests/valid/RecursiveType_Valid_22.whiley @@ -2,8 +2,8 @@ type SortedList is null | SortedListNode -type SortedListNode is ({SortedList next, int data} this) -where (this.next == null) || (this.data < this.next.data) +type SortedListNode is ({SortedList next, int data} _this) +where (_this.next == null) || (_this.data < _this.next.data) function SortedList(int head, SortedList tail) -> SortedList requires (tail == null) || (head < tail.data): diff --git a/tests/valid/UnionType_Valid_18.whiley b/tests/valid/UnionType_Valid_18.whiley index a4ee1305e8..a49db53a19 100644 --- a/tests/valid/UnionType_Valid_18.whiley +++ b/tests/valid/UnionType_Valid_18.whiley @@ -4,7 +4,7 @@ type utr12nat is (int x) where x >= 0 type intList is utr12nat | int[] -type tupper is ({int op, intList il} this) where (this.op >= 0) && (this.op <= 5) +type tupper is ({int op, intList il} _this) where (_this.op >= 0) && (_this.op <= 5) function f(tupper y) -> (int result) ensures result >= 0: From 8a3b4503ba1c06c56f9be3737b3da62938d4f971 Mon Sep 17 00:00:00 2001 From: Sebastian Schweizer Date: Tue, 26 Apr 2016 12:16:52 +1200 Subject: [PATCH 42/43] ignore failing test, see #641 --- modules/wyc/src/wyc/testing/AllValidTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/wyc/src/wyc/testing/AllValidTests.java b/modules/wyc/src/wyc/testing/AllValidTests.java index a9d4ca4e3b..cc4f165614 100644 --- a/modules/wyc/src/wyc/testing/AllValidTests.java +++ b/modules/wyc/src/wyc/testing/AllValidTests.java @@ -89,6 +89,7 @@ public class AllValidTests { IGNORED.put("Import_Valid_5", "#492"); IGNORED.put("Intersection_Valid_1", "Issue ???"); IGNORED.put("Intersection_Valid_2", "Issue ???"); + IGNORED.put("Lifetime_Lambda_Valid_4", "#641"); IGNORED.put("ListAccess_Valid_6", "Issue ???"); IGNORED.put("ListAccess_Valid_7", "Issue ???"); IGNORED.put("NegationType_Valid_3", "Issue ???"); From 34af413598608649a27946b540d3fb5966a4f124 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Sat, 28 May 2016 10:20:38 +1200 Subject: [PATCH 43/43] Updated version and tested next release version Updated version number to 0.3.40. This will be the last in the 0.3.X series (finally). This release is something of an interim solution, since it is taking a long time to work through changes to the WyIL Bytecode API. The main change in this release is the inclusion of lifetimes by Sebastian Schweizer. This release also contains quite a few other fixes from various people. --- config.xml | 2 +- modules/wyjc/src/wyjc/testing/RuntimeValidTests.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config.xml b/config.xml index 0d315a357a..cc74100436 100644 --- a/config.xml +++ b/config.xml @@ -2,7 +2,7 @@ - +