codegen.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* ScummVM Tools
  2. *
  3. * ScummVM Tools is the legal property of its developers, whose
  4. * names are too numerous to list here. Please refer to the
  5. * COPYRIGHT file distributed with this source distribution.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. */
  21. #include "decompiler/scummv6/engine.h"
  22. #include <vector>
  23. #include "../CodeGenerator.h"
  24. #include "../ControlFlow.h"
  25. #include "../Disassembler.h"
  26. #include "../Graph.h"
  27. #define GET(vertex) (boost::get(boost::vertex_name, g, vertex))
  28. #include <streambuf>
  29. #include <ostream>
  30. #include <gmock/gmock.h>
  31. #include "util.h"
  32. std::string removeSpaces(std::string s) {
  33. size_t found;
  34. while ((found = s.find(' ')) != std::string::npos)
  35. s = s.erase(found, 1);
  36. return s;
  37. }
  38. typedef std::vector<std::string>::iterator CodeIterator;
  39. TEST(CodeGen, testContinue) {
  40. InstVec insts;
  41. auto engine = std::make_unique<Scumm::v6::Scummv6Engine>();
  42. auto d = engine->GetDisassembler(insts);
  43. d->open("decompiler/test/continue-do-while.dmp");
  44. d->disassemble();
  45. auto c = std::make_unique<ControlFlow>(insts, *engine);
  46. c->createGroups();
  47. Graph g = c->analyze();
  48. onullstream ns;
  49. auto cg = engine->GetCodeGenerator(insts, ns);
  50. cg->generate(insts, g);
  51. VertexIterator v = boost::vertices(g).first;
  52. std::vector<std::string> output, expected;
  53. expected.push_back("do{");
  54. expected.push_back("if(18 != var321) {");
  55. expected.push_back("continue;");
  56. expected.push_back("}");
  57. expected.push_back("VAR_CHARSET_MASK--;");
  58. expected.push_back("} while (42 == VAR_CHARSET_MASK)");
  59. expected.push_back("stopObjectCodeA();");
  60. GroupPtr gr = GET(*v);
  61. // Find first node
  62. while (gr->_prev != NULL)
  63. gr = gr->_prev;
  64. // Copy out all lines of code
  65. while (gr != NULL) {
  66. for (std::vector<CodeLine>::iterator it = gr->_code.begin(); it != gr->_code.end(); ++it)
  67. output.push_back(it->_line);
  68. gr = gr->_next;
  69. }
  70. ASSERT_TRUE(output.size() == expected.size());
  71. CodeIterator it, it2;
  72. for (it = output.begin(), it2 = expected.begin(); it != output.end() && it2 != expected.end(); ++it, ++it2) {
  73. ASSERT_TRUE(removeSpaces(*it).compare(removeSpaces(*it2)) == 0);
  74. }
  75. }
  76. TEST(CodeGen, testBreak) {
  77. InstVec insts;
  78. auto engine = std::make_unique<Scumm::v6::Scummv6Engine>();
  79. auto d = engine->GetDisassembler(insts);
  80. d->open("decompiler/test/break-while.dmp");
  81. d->disassemble();
  82. auto c = std::make_unique<ControlFlow>(insts, *engine);
  83. c->createGroups();
  84. Graph g = c->analyze();
  85. onullstream ns;
  86. auto cg = engine->GetCodeGenerator(insts, ns);
  87. cg->generate(insts, g);
  88. VertexIterator v = boost::vertices(g).first;
  89. std::vector<std::string> output, expected;
  90. expected.push_back("while (42 != VAR_CHARSET_MASK) {");
  91. expected.push_back("if (18 != var321) {");
  92. expected.push_back("break;");
  93. expected.push_back("}");
  94. expected.push_back("VAR_CHARSET_MASK--;");
  95. expected.push_back("}");
  96. expected.push_back("stopObjectCodeA();");
  97. GroupPtr gr = GET(*v);
  98. // Find first node
  99. while (gr->_prev != NULL)
  100. gr = gr->_prev;
  101. // Copy out all lines of code
  102. while (gr != NULL) {
  103. for (std::vector<CodeLine>::iterator it = gr->_code.begin(); it != gr->_code.end(); ++it)
  104. output.push_back(it->_line);
  105. gr = gr->_next;
  106. }
  107. ASSERT_TRUE(output.size() == expected.size());
  108. CodeIterator it, it2;
  109. for (it = output.begin(), it2 = expected.begin(); it != output.end() && it2 != expected.end(); ++it, ++it2) {
  110. ASSERT_TRUE(removeSpaces(*it).compare(removeSpaces(*it2)) == 0);
  111. }
  112. }
  113. TEST(CodeGen, testElse) {
  114. InstVec insts;
  115. auto engine = std::make_unique<Scumm::v6::Scummv6Engine>();
  116. auto d = engine->GetDisassembler(insts);
  117. d->open("decompiler/test/if-else.dmp");
  118. d->disassemble();
  119. auto c = std::make_unique<ControlFlow>(insts, *engine);
  120. c->createGroups();
  121. Graph g = c->analyze();
  122. onullstream ns;
  123. auto cg = engine->GetCodeGenerator(insts, ns);
  124. cg->generate(insts, g);
  125. VertexIterator v = boost::vertices(g).first;
  126. std::vector<std::string> output, expected;
  127. expected.push_back("if (42 != VAR_CHARSET_MASK) {");
  128. expected.push_back("VAR_CHARSET_MASK--;");
  129. expected.push_back("} else {");
  130. expected.push_back("VAR_CHARSET_MASK++;");
  131. expected.push_back("}");
  132. expected.push_back("stopObjectCodeA();");
  133. GroupPtr gr = GET(*v);
  134. // Find first node
  135. while (gr->_prev != NULL)
  136. gr = gr->_prev;
  137. // Copy out all lines of code
  138. while (gr != NULL) {
  139. for (std::vector<CodeLine>::iterator it = gr->_code.begin(); it != gr->_code.end(); ++it)
  140. output.push_back(it->_line);
  141. gr = gr->_next;
  142. }
  143. ASSERT_TRUE(output.size() == expected.size());
  144. CodeIterator it, it2;
  145. for (it = output.begin(), it2 = expected.begin(); it != output.end() && it2 != expected.end(); ++it, ++it2) {
  146. ASSERT_TRUE(removeSpaces(*it).compare(removeSpaces(*it2)) == 0);
  147. }
  148. }
  149. // This test requires script-30 and script-48.dmp from Sam & Max: Hit The Road.
  150. // 6e48faca13e1f6df9341567608962744 *script-30.dmp
  151. // afd7dc5d377894b3b9d0504927adf1b1 *script-48.dmp
  152. // Disabled as mentioned file is copyrighted
  153. TEST(CodeGen, DISABLED_testCoalescing) {
  154. InstVec insts;
  155. auto engine = std::make_unique<Scumm::v6::Scummv6Engine>();
  156. auto d = engine->GetDisassembler(insts);
  157. d->open("decompiler/test/script-30.dmp");
  158. d->disassemble();
  159. auto c = std::make_unique<ControlFlow>(insts, *engine);
  160. c->createGroups();
  161. Graph g = c->analyze();
  162. onullstream ns;
  163. auto cg = engine->GetCodeGenerator(insts, ns);
  164. cg->generate(insts, g);
  165. VertexIterator v = boost::vertices(g).first;
  166. GroupPtr gr = GET(*v);
  167. // Find first node
  168. while (gr->_prev != NULL)
  169. gr = gr->_prev;
  170. // Find vertex to test
  171. while ((*gr->_start)->_address != 0x91)
  172. gr = gr->_next;
  173. ASSERT_TRUE(gr->_code.size() == 2);
  174. ASSERT_TRUE(removeSpaces(gr->_code[0]._line).compare("}else{") == 0);
  175. ASSERT_TRUE(removeSpaces(gr->_code[1]._line).substr(0, 2).compare("if") == 0);
  176. insts.clear();
  177. engine = std::make_unique<Scumm::v6::Scummv6Engine>();
  178. d = engine->GetDisassembler(insts);
  179. d->open("decompiler/test/script-48.dmp");
  180. d->disassemble();
  181. c = std::make_unique<ControlFlow>(insts, *engine);
  182. c->createGroups();
  183. g = c->analyze();
  184. cg = engine->GetCodeGenerator(insts, ns);
  185. cg->generate(insts, g);
  186. v = boost::vertices(g).first;
  187. gr = GET(*v);
  188. // Find first node
  189. while (gr->_prev != NULL)
  190. gr = gr->_prev;
  191. // Find vertex to test
  192. while ((*gr->_start)->_address != 0x191)
  193. gr = gr->_next;
  194. ASSERT_TRUE(gr->_code.size() == 1);
  195. ASSERT_TRUE(removeSpaces(gr->_code[0]._line).substr(0, 7).compare("}elseif") == 0);
  196. }