LCOV - code coverage report
Current view: top level - tests - catch_optimizer.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 158 168 94.0 %
Date: 2023-07-29 22:00:24 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/as2js
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : // as2js
      20             : //
      21             : #include    <as2js/optimizer.h>
      22             : 
      23             : #include    <as2js/exception.h>
      24             : #include    <as2js/json.h>
      25             : #include    <as2js/message.h>
      26             : #include    <as2js/parser.h>
      27             : 
      28             : 
      29             : // self
      30             : //
      31             : #include    "catch_main.h"
      32             : 
      33             : 
      34             : // C++
      35             : //
      36             : #include    <cstring>
      37             : #include    <algorithm>
      38             : #include    <iomanip>
      39             : 
      40             : 
      41             : // C
      42             : //
      43             : #include    <unistd.h>
      44             : #include    <sys/stat.h>
      45             : 
      46             : 
      47             : // last include
      48             : //
      49             : #include    <snapdev/poison.h>
      50             : 
      51             : 
      52             : 
      53             : namespace
      54             : {
      55             : 
      56             : 
      57             : 
      58             : 
      59             : 
      60             : //
      61             : // JSON data used to test the optimizer, most of the work is in this table
      62             : // these are long JSON strings! It is actually generated using the
      63             : // json_to_string tool and the optimizer_data/*.json source files.
      64             : //
      65             : // Note: the top entries are arrays so we can execute programs in the
      66             : //       order we define them...
      67             : //
      68             : char const g_optimizer_additive[] =
      69             : #include "optimizer_data/additive.ci"
      70             : ;
      71             : char const g_optimizer_assignments[] =
      72             : #include "optimizer_data/assignments.ci"
      73             : ;
      74             : char const g_optimizer_bitwise[] =
      75             : #include "optimizer_data/bitwise.ci"
      76             : ;
      77             : char const g_optimizer_compare[] =
      78             : #include "optimizer_data/compare.ci"
      79             : ;
      80             : char const g_optimizer_conditional[] =
      81             : #include "optimizer_data/conditional.ci"
      82             : ;
      83             : char const g_optimizer_equality[] =
      84             : #include "optimizer_data/equality.ci"
      85             : ;
      86             : char const g_optimizer_logical[] =
      87             : #include "optimizer_data/logical.ci"
      88             : ;
      89             : char const g_optimizer_match[] =
      90             : #include "optimizer_data/match.ci"
      91             : ;
      92             : char const g_optimizer_multiplicative[] =
      93             : #include "optimizer_data/multiplicative.ci"
      94             : ;
      95             : char const g_optimizer_relational[] =
      96             : #include "optimizer_data/relational.ci"
      97             : ;
      98             : char const g_optimizer_statements[] =
      99             : #include "optimizer_data/statements.ci"
     100             : ;
     101             : 
     102             : 
     103             : 
     104             : 
     105             : 
     106             : 
     107             : 
     108             : 
     109             : // This function runs all the tests defined in the
     110             : // string 'data'
     111          11 : void run_tests(char const * input_data, char const * filename)
     112             : {
     113          11 :     if(SNAP_CATCH2_NAMESPACE::g_save_parser_tests)
     114             :     {
     115           0 :         std::ofstream json_file;
     116           0 :         json_file.open(filename);
     117           0 :         CATCH_REQUIRE(json_file.is_open());
     118             :         json_file
     119             :             << "// To properly indent this JSON you may use http://json-indent.appspot.com/\n"
     120             :             << input_data
     121           0 :             << "\n";
     122           0 :     }
     123             : 
     124          11 :     as2js::input_stream<std::stringstream>::pointer_t in(std::make_shared<as2js::input_stream<std::stringstream>>());
     125          11 :     *in << input_data;
     126          11 :     as2js::json::pointer_t json_data(std::make_shared<as2js::json>());
     127          22 :     as2js::json::json_value::pointer_t json(json_data->parse(in));
     128             : 
     129             :     // verify that the optimizer() did not fail
     130          11 :     CATCH_REQUIRE(!!json);
     131          11 :     CATCH_REQUIRE(json->get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
     132             : 
     133          22 :     std::string const name_string("name");
     134          22 :     std::string const program_string("program");
     135          22 :     std::string const verbose_string("verbose");
     136          22 :     std::string const slow_string("slow");
     137          22 :     std::string const parser_result_string("parser result");
     138          22 :     std::string const optimizer_result_string("optimizer result");
     139          22 :     std::string const expected_messages_string("expected messages");
     140             : 
     141          11 :     as2js::json::json_value::array_t const& array(json->get_array());
     142          11 :     size_t const max_programs(array.size());
     143         434 :     for(size_t idx(0); idx < max_programs; ++idx)
     144             :     {
     145         423 :         as2js::json::json_value::pointer_t prog_obj(array[idx]);
     146         423 :         CATCH_REQUIRE(prog_obj->get_type() == as2js::json::json_value::type_t::JSON_TYPE_OBJECT);
     147         423 :         as2js::json::json_value::object_t const& prog(prog_obj->get_object());
     148             : 
     149         423 :         bool verbose(false);
     150         423 :         as2js::json::json_value::object_t::const_iterator verbose_it(prog.find(verbose_string));
     151         423 :         if(verbose_it != prog.end())
     152             :         {
     153           0 :             verbose = verbose_it->second->get_type() == as2js::json::json_value::type_t::JSON_TYPE_TRUE;
     154             :         }
     155             : 
     156         423 :         bool slow(false);
     157         423 :         as2js::json::json_value::object_t::const_iterator slow_it(prog.find(slow_string));
     158         423 :         if(slow_it != prog.end())
     159             :         {
     160           0 :             slow = slow_it->second->get_type() == as2js::json::json_value::type_t::JSON_TYPE_TRUE;
     161             :         }
     162             : 
     163             :         // got a program, try to compile it with all the possible options
     164         423 :         as2js::json::json_value::pointer_t name(prog.find(name_string)->second);
     165         423 :         std::cout << "  -- working on \"" << name->get_string() << "\" " << (slow ? "" : "...") << std::flush;
     166             : 
     167             :         {
     168         423 :             as2js::json::json_value::pointer_t program_value(prog.find(program_string)->second);
     169             : //std::cerr << "prog = [" << program_value->get_string() << "]\n";
     170         423 :             as2js::input_stream<std::stringstream>::pointer_t prog_text(std::make_shared<as2js::input_stream<std::stringstream>>());
     171         423 :             *prog_text << program_value->get_string();
     172         423 :             as2js::options::pointer_t options(std::make_shared<as2js::options>());
     173         423 :             as2js::parser::pointer_t parser(std::make_shared<as2js::parser>(prog_text, options));
     174             : 
     175         423 :             SNAP_CATCH2_NAMESPACE::test_callback tc(verbose);
     176             : 
     177             :             // no errors expected while parsing (if you want to test errors
     178             :             // in the parser, use the catch_parser.cpp test instead)
     179             :             //
     180         423 :             as2js::node::pointer_t root(parser->parse());
     181             : 
     182             :             // verify the parser result, that way we can make sure we are
     183             :             // testing the tree we want to test in the optimizer
     184             :             //
     185         423 :             SNAP_CATCH2_NAMESPACE::verify_result(parser_result_string, prog.find(parser_result_string)->second, root, verbose, false);
     186             : 
     187             :             // now the optimizer may end up generating messages...
     188             :             // (there are not many, mainly things like division by zero
     189             :             // and illegal operation.)
     190             :             //
     191         423 :             as2js::json::json_value::object_t::const_iterator expected_msg_it(prog.find(expected_messages_string));
     192         423 :             if(expected_msg_it != prog.end())
     193             :             {
     194             :                 // the expected messages value must be an array
     195             :                 //
     196          14 :                 as2js::message_level_t message_level(as2js::message_level_t::MESSAGE_LEVEL_INFO);
     197          14 :                 as2js::json::json_value::array_t const& msg_array(expected_msg_it->second->get_array());
     198          14 :                 size_t const max_msgs(msg_array.size());
     199          28 :                 for(size_t j(0); j < max_msgs; ++j)
     200             :                 {
     201          14 :                     as2js::json::json_value::pointer_t message_value(msg_array[j]);
     202          14 :                     as2js::json::json_value::object_t const& message(message_value->get_object());
     203             : 
     204          14 :                     as2js::json::json_value::object_t::const_iterator const message_options_iterator(message.find("options"));
     205          14 :                     if(message_options_iterator != message.end())
     206             :                     {
     207             : //{
     208             : //as2js::json::json_value::object_t::const_iterator line_it(message.find("line #"));
     209             : //if(line_it != message.end())
     210             : //{
     211             : //    int64_t lines(line_it->second->get_int64().get());
     212             : //std::cerr << "_________\nLine #" << lines << "\n";
     213             : //}
     214             : //else
     215             : //std::cerr << "_________\nLine #<undefined>\n";
     216             : //}
     217          14 :                         SNAP_CATCH2_NAMESPACE::test_callback::expected_t expected;
     218          14 :                         expected.f_message_level = static_cast<as2js::message_level_t>(message.find("message level")->second->get_integer().get());
     219          14 :                         expected.f_error_code = SNAP_CATCH2_NAMESPACE::str_to_error_code(message.find("error code")->second->get_string());
     220          14 :                         expected.f_pos.set_filename("unknown-file");
     221          14 :                         as2js::json::json_value::object_t::const_iterator func_it(message.find("function name"));
     222          14 :                         if(func_it == message.end())
     223             :                         {
     224          14 :                             expected.f_pos.set_function("unknown-func");
     225             :                         }
     226             :                         else
     227             :                         {
     228           0 :                             expected.f_pos.set_function(func_it->second->get_string());
     229             :                         }
     230          14 :                         as2js::json::json_value::object_t::const_iterator line_it(message.find("line #"));
     231          14 :                         if(line_it != message.end())
     232             :                         {
     233          14 :                             std::int64_t lines(line_it->second->get_integer().get());
     234          14 :                             for(std::int64_t l(1); l < lines; ++l)
     235             :                             {
     236           0 :                                 expected.f_pos.new_line();
     237             :                             }
     238             :                         }
     239          14 :                         expected.f_message = message.find("message")->second->get_string();
     240          14 :                         tc.f_expected.push_back(expected);
     241             : 
     242          14 :                         message_level = std::min(message_level, expected.f_message_level);
     243          14 :                     }
     244          14 :                 }
     245             : 
     246             :                 // the default message level is INFO, don't change if we have
     247             :                 // a higher level here; however, if we have a lower level,
     248             :                 // change the message level in the as2js library
     249             :                 //
     250          14 :                 if(message_level < as2js::message_level_t::MESSAGE_LEVEL_INFO)
     251             :                 {
     252           0 :                     as2js::set_message_level(message_level);
     253             :                 }
     254             :             }
     255             : 
     256             :             // run the optimizer
     257         423 :             as2js::optimizer::optimize(root);
     258             : 
     259             :             // the result is object which can have children
     260             :             // which are represented by an array of objects
     261             :             //
     262         423 :             SNAP_CATCH2_NAMESPACE::verify_result(optimizer_result_string, prog.find(optimizer_result_string)->second, root, verbose, false);
     263             : 
     264         423 :             tc.got_called();
     265         423 :         }
     266             : 
     267         423 :         std::cout << " OK\n";
     268         423 :     }
     269             : 
     270          11 :     std::cout << "\n";
     271          22 : }
     272             : 
     273             : 
     274             : }
     275             : // no name namespace
     276             : 
     277             : 
     278             : 
     279             : 
     280             : 
     281           1 : CATCH_TEST_CASE("optimizer_invalid_nodes", "[optimizer][invalid]")
     282             : {
     283             :     // empty node does nothing, return false
     284             :     {
     285           1 :         as2js::node::pointer_t node;
     286           1 :         CATCH_REQUIRE(!as2js::optimizer::optimize(node));
     287           1 :     }
     288             : 
     289             :     // unknown node does nothing, return false
     290             :     {
     291           1 :         as2js::node::pointer_t node(std::make_shared<as2js::node>(as2js::node_t::NODE_UNKNOWN));
     292           1 :         CATCH_REQUIRE(!as2js::optimizer::optimize(node));
     293           1 :         CATCH_REQUIRE(node->get_type() == as2js::node_t::NODE_UNKNOWN);
     294           1 :         CATCH_REQUIRE(node->get_children_size() == 0);
     295           1 :     }
     296             : 
     297             :     // a special case where an optimization occurs on a node without a parent
     298             :     // (something that should not occur in a real tree)
     299             :     {
     300             :         // ADD
     301             :         //   INTEGER = 3
     302             :         //   INTEGER = 20
     303           1 :         as2js::node::pointer_t node_add(std::make_shared<as2js::node>(as2js::node_t::NODE_ADD));
     304             : 
     305           1 :         as2js::node::pointer_t node_three(std::make_shared<as2js::node>(as2js::node_t::NODE_INTEGER));
     306           1 :         as2js::integer three;
     307           1 :         three.set(3);
     308           1 :         node_three->set_integer(three);
     309           1 :         node_add->append_child(node_three);
     310             : 
     311           1 :         as2js::node::pointer_t node_twenty(std::make_shared<as2js::node>(as2js::node_t::NODE_INTEGER));
     312           1 :         as2js::integer twenty;
     313           1 :         twenty.set(20);
     314           1 :         node_twenty->set_integer(twenty);
     315           1 :         node_add->append_child(node_twenty);
     316             : 
     317             :         // optimization does not happen
     318           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     319             :               as2js::optimizer::optimize(node_add)
     320             :             , as2js::internal_error
     321             :             , Catch::Matchers::ExceptionMessage(
     322             :                       "internal_error: somehow the optimizer is optimizing a node without a parent."));
     323             : 
     324             :         // verify that nothing changed
     325           1 :         CATCH_REQUIRE(node_add->get_type() == as2js::node_t::NODE_ADD);
     326           1 :         CATCH_REQUIRE(node_add->get_children_size() == 2);
     327           1 :         CATCH_REQUIRE(node_three->get_type() == as2js::node_t::NODE_INTEGER);
     328           1 :         CATCH_REQUIRE(node_three->get_children_size() == 0);
     329           1 :         CATCH_REQUIRE(node_three->get_integer().compare(three) == as2js::compare_t::COMPARE_EQUAL);
     330           1 :         CATCH_REQUIRE(node_twenty->get_type() == as2js::node_t::NODE_INTEGER);
     331           1 :         CATCH_REQUIRE(node_twenty->get_children_size() == 0);
     332           1 :         CATCH_REQUIRE(node_twenty->get_integer().compare(twenty) == as2js::compare_t::COMPARE_EQUAL);
     333           1 :     }
     334           1 : }
     335             : 
     336             : 
     337           1 : CATCH_TEST_CASE("optimizer_additive", "[optimizer]")
     338             : {
     339           1 :     CATCH_START_SECTION("optimizer_additive: additive (+, -)")
     340             :     {
     341           1 :         run_tests(g_optimizer_additive, "optimizer/additive.json");
     342             :     }
     343           1 :     CATCH_END_SECTION()
     344           1 : }
     345             : 
     346             : 
     347           1 : CATCH_TEST_CASE("optimizer_assignments", "[optimizer]")
     348             : {
     349           1 :     CATCH_START_SECTION("parser_array: assignments (=, +=, -=, etc.)")
     350             :     {
     351           1 :         run_tests(g_optimizer_assignments, "optimizer/assignments.json");
     352             :     }
     353           1 :     CATCH_END_SECTION()
     354           1 : }
     355             : 
     356             : 
     357           1 : CATCH_TEST_CASE("optimizer_bitwise", "[optimizer]")
     358             : {
     359           1 :     CATCH_START_SECTION("optimizer_bitwise: bitwise (&, |, ^)")
     360             :     {
     361           1 :         run_tests(g_optimizer_bitwise, "optimizer/bitwise.json");
     362             :     }
     363           1 :     CATCH_END_SECTION()
     364           1 : }
     365             : 
     366             : 
     367           1 : CATCH_TEST_CASE("optimizer_compare", "[optimizer]")
     368             : {
     369           1 :     CATCH_START_SECTION("optimizer_compare: compare (<=>)")
     370             :     {
     371           1 :         run_tests(g_optimizer_compare, "optimizer/compare.json");
     372             :     }
     373           1 :     CATCH_END_SECTION()
     374           1 : }
     375             : 
     376             : 
     377           1 : CATCH_TEST_CASE("optimizer_conditional", "[optimizer]")
     378             : {
     379           1 :     CATCH_START_SECTION("optimizer_conditional: conditional (?:, <?, >?)")
     380             :     {
     381           1 :         run_tests(g_optimizer_conditional, "optimizer/conditional.json");
     382             :     }
     383           1 :     CATCH_END_SECTION()
     384           1 : }
     385             : 
     386             : 
     387           1 : CATCH_TEST_CASE("optimizer_equality", "[optimizer]")
     388             : {
     389           1 :     CATCH_START_SECTION("optimizer_equality: equality (==, !=)")
     390             :     {
     391           1 :         run_tests(g_optimizer_equality, "optimizer/equality.json");
     392             :     }
     393           1 :     CATCH_END_SECTION()
     394           1 : }
     395             : 
     396             : 
     397           1 : CATCH_TEST_CASE("optimizer_logical", "[optimizer]")
     398             : {
     399           1 :     CATCH_START_SECTION("optimizer_logical: logical (&&, ||, ^^)")
     400             :     {
     401           1 :         run_tests(g_optimizer_logical, "optimizer/logical.json");
     402             :     }
     403           1 :     CATCH_END_SECTION()
     404           1 : }
     405             : 
     406             : 
     407           1 : CATCH_TEST_CASE("optimizer_match", "[optimizer]")
     408             : {
     409           1 :     CATCH_START_SECTION("optimizer_match: match (~=)")
     410             :     {
     411           1 :         run_tests(g_optimizer_match, "optimizer/match.json");
     412             :     }
     413           1 :     CATCH_END_SECTION()
     414           1 : }
     415             : 
     416             : 
     417           1 : CATCH_TEST_CASE("optimizer_multiplicative", "[optimizer]")
     418             : {
     419           1 :     CATCH_START_SECTION("optimizer_multiplicative: multiplicative (*, /, %)")
     420             :     {
     421           1 :         run_tests(g_optimizer_multiplicative, "optimizer/multiplicative.json");
     422             :     }
     423           1 :     CATCH_END_SECTION()
     424           1 : }
     425             : 
     426             : 
     427           1 : CATCH_TEST_CASE("optimizer_relational", "[optimizer]")
     428             : {
     429           1 :     CATCH_START_SECTION("optimizer_relational: relational (<, <=, >, >=)")
     430             :     {
     431           1 :         run_tests(g_optimizer_relational, "optimizer_relational.json");
     432             :     }
     433           1 :     CATCH_END_SECTION()
     434           1 : }
     435             : 
     436             : 
     437           1 : CATCH_TEST_CASE("optimizer_statements", "[optimizer]")
     438             : {
     439           1 :     CATCH_START_SECTION("optimizer_statements: statement")
     440             :     {
     441           1 :         run_tests(g_optimizer_statements, "optimizer_statements.json");
     442             :     }
     443           1 :     CATCH_END_SECTION()
     444           1 : }
     445             : 
     446             : 
     447             : 
     448             : 
     449             : 
     450             : 
     451             : 
     452             : 
     453             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14