laser 0.7.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (319) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +14 -0
  4. data/LICENSE +661 -0
  5. data/README.md +158 -0
  6. data/Rakefile +104 -0
  7. data/VERSION +1 -0
  8. data/bin/laser +7 -0
  9. data/design_docs/goals.md +57 -0
  10. data/design_docs/object_regex.md +426 -0
  11. data/design_docs/type_annotations.md +80 -0
  12. data/ext/laser/BasicBlock.cpp +572 -0
  13. data/ext/laser/BasicBlock.h +118 -0
  14. data/ext/laser/extconf.rb +3 -0
  15. data/features/laser.feature +25 -0
  16. data/features/step_definitions/laser_steps.rb +39 -0
  17. data/features/support/env.rb +14 -0
  18. data/features/support/testdata/1_input +1 -0
  19. data/features/support/testdata/1_output +1 -0
  20. data/features/support/testdata/2_input +4 -0
  21. data/features/support/testdata/2_output +4 -0
  22. data/features/support/testdata/3_input +8 -0
  23. data/features/support/testdata/3_output +11 -0
  24. data/features/support/testdata/4_input +5 -0
  25. data/features/support/testdata/4_output +5 -0
  26. data/features/support/testdata/5_input +13 -0
  27. data/laser.gemspec +382 -0
  28. data/lib/laser.rb +98 -0
  29. data/lib/laser/analysis/annotations.rb +95 -0
  30. data/lib/laser/analysis/annotations/annotation_config.yaml +3 -0
  31. data/lib/laser/analysis/annotations/comment_attachment_annotation.rb +66 -0
  32. data/lib/laser/analysis/annotations/node_pointers_annotation.rb +36 -0
  33. data/lib/laser/analysis/annotations/runtime_annotation.rb +55 -0
  34. data/lib/laser/analysis/argument_expansion.rb +132 -0
  35. data/lib/laser/analysis/arity.rb +34 -0
  36. data/lib/laser/analysis/bindings.rb +144 -0
  37. data/lib/laser/analysis/bootstrap/bootstrap.rb +298 -0
  38. data/lib/laser/analysis/bootstrap/laser_class.rb +106 -0
  39. data/lib/laser/analysis/bootstrap/laser_method.rb +255 -0
  40. data/lib/laser/analysis/bootstrap/laser_module.rb +403 -0
  41. data/lib/laser/analysis/bootstrap/laser_module_copy.rb +74 -0
  42. data/lib/laser/analysis/bootstrap/laser_object.rb +69 -0
  43. data/lib/laser/analysis/bootstrap/laser_proc.rb +150 -0
  44. data/lib/laser/analysis/bootstrap/laser_singleton_class.rb +44 -0
  45. data/lib/laser/analysis/comments.rb +35 -0
  46. data/lib/laser/analysis/control_flow.rb +28 -0
  47. data/lib/laser/analysis/control_flow/alias_analysis.rb +31 -0
  48. data/lib/laser/analysis/control_flow/basic_block.rb +105 -0
  49. data/lib/laser/analysis/control_flow/cfg_builder.rb +2505 -0
  50. data/lib/laser/analysis/control_flow/cfg_instruction.rb +190 -0
  51. data/lib/laser/analysis/control_flow/constant_propagation.rb +742 -0
  52. data/lib/laser/analysis/control_flow/control_flow_graph.rb +370 -0
  53. data/lib/laser/analysis/control_flow/lifetime_analysis.rb +91 -0
  54. data/lib/laser/analysis/control_flow/method_call_search.rb +26 -0
  55. data/lib/laser/analysis/control_flow/raise_properties.rb +25 -0
  56. data/lib/laser/analysis/control_flow/simulation.rb +385 -0
  57. data/lib/laser/analysis/control_flow/static_single_assignment.rb +185 -0
  58. data/lib/laser/analysis/control_flow/unreachability_analysis.rb +57 -0
  59. data/lib/laser/analysis/control_flow/unused_variables.rb +91 -0
  60. data/lib/laser/analysis/control_flow/yield_properties.rb +103 -0
  61. data/lib/laser/analysis/errors.rb +131 -0
  62. data/lib/laser/analysis/laser_utils.rb +18 -0
  63. data/lib/laser/analysis/lexical_analysis.rb +172 -0
  64. data/lib/laser/analysis/method_call.rb +68 -0
  65. data/lib/laser/analysis/protocol_registry.rb +30 -0
  66. data/lib/laser/analysis/scope.rb +118 -0
  67. data/lib/laser/analysis/sexp.rb +159 -0
  68. data/lib/laser/analysis/sexp_analysis.rb +40 -0
  69. data/lib/laser/analysis/sexp_extensions/constant_extraction.rb +115 -0
  70. data/lib/laser/analysis/sexp_extensions/source_location.rb +164 -0
  71. data/lib/laser/analysis/sexp_extensions/type_inference.rb +47 -0
  72. data/lib/laser/analysis/signature.rb +76 -0
  73. data/lib/laser/analysis/special_methods/send.rb +67 -0
  74. data/lib/laser/analysis/unused_methods.rb +21 -0
  75. data/lib/laser/analysis/visitor.rb +141 -0
  76. data/lib/laser/annotation_parser/annotations.treetop +126 -0
  77. data/lib/laser/annotation_parser/annotations_parser.rb +748 -0
  78. data/lib/laser/annotation_parser/class_annotations.treetop +82 -0
  79. data/lib/laser/annotation_parser/class_annotations_parser.rb +654 -0
  80. data/lib/laser/annotation_parser/overload.treetop +24 -0
  81. data/lib/laser/annotation_parser/overload_parser.rb +167 -0
  82. data/lib/laser/annotation_parser/parsers.rb +6 -0
  83. data/lib/laser/annotation_parser/structural.treetop +37 -0
  84. data/lib/laser/annotation_parser/structural_parser.rb +406 -0
  85. data/lib/laser/annotation_parser/useful_parsers.treetop +47 -0
  86. data/lib/laser/annotation_parser/useful_parsers_parser.rb +674 -0
  87. data/lib/laser/rake/task.rb +46 -0
  88. data/lib/laser/runner.rb +189 -0
  89. data/lib/laser/scanner.rb +169 -0
  90. data/lib/laser/standard_library/_thread.rb +110 -0
  91. data/lib/laser/standard_library/abbrev.rb +103 -0
  92. data/lib/laser/standard_library/array.rb +418 -0
  93. data/lib/laser/standard_library/base64.rb +91 -0
  94. data/lib/laser/standard_library/basic_object.rb +55 -0
  95. data/lib/laser/standard_library/benchmark.rb +556 -0
  96. data/lib/laser/standard_library/bignum.rb +185 -0
  97. data/lib/laser/standard_library/cgi.rb +275 -0
  98. data/lib/laser/standard_library/cgi/cookie.rb +147 -0
  99. data/lib/laser/standard_library/cgi/core.rb +791 -0
  100. data/lib/laser/standard_library/cgi/html.rb +1021 -0
  101. data/lib/laser/standard_library/cgi/session.rb +537 -0
  102. data/lib/laser/standard_library/cgi/session/pstore.rb +111 -0
  103. data/lib/laser/standard_library/cgi/util.rb +188 -0
  104. data/lib/laser/standard_library/class_definitions.rb +333 -0
  105. data/lib/laser/standard_library/comparable.rb +125 -0
  106. data/lib/laser/standard_library/complex.rb +162 -0
  107. data/lib/laser/standard_library/enumerable.rb +178 -0
  108. data/lib/laser/standard_library/exceptions.rb +135 -0
  109. data/lib/laser/standard_library/fixnum.rb +188 -0
  110. data/lib/laser/standard_library/float.rb +180 -0
  111. data/lib/laser/standard_library/hash.rb +237 -0
  112. data/lib/laser/standard_library/integer.rb +123 -0
  113. data/lib/laser/standard_library/laser_magic.rb +7 -0
  114. data/lib/laser/standard_library/nil_false_true.rb +113 -0
  115. data/lib/laser/standard_library/numbers.rb +192 -0
  116. data/lib/laser/standard_library/proc.rb +31 -0
  117. data/lib/laser/standard_library/set.rb +1348 -0
  118. data/lib/laser/standard_library/string.rb +666 -0
  119. data/lib/laser/standard_library/stringio.rb +2 -0
  120. data/lib/laser/standard_library/symbol.rb +125 -0
  121. data/lib/laser/standard_library/tsort.rb +242 -0
  122. data/lib/laser/support/acts_as_struct.rb +66 -0
  123. data/lib/laser/support/frequency.rb +55 -0
  124. data/lib/laser/support/inheritable_attributes.rb +145 -0
  125. data/lib/laser/support/module_extensions.rb +94 -0
  126. data/lib/laser/support/placeholder_object.rb +13 -0
  127. data/lib/laser/third_party/rgl/adjacency.rb +221 -0
  128. data/lib/laser/third_party/rgl/base.rb +228 -0
  129. data/lib/laser/third_party/rgl/bidirectional.rb +39 -0
  130. data/lib/laser/third_party/rgl/condensation.rb +47 -0
  131. data/lib/laser/third_party/rgl/connected_components.rb +138 -0
  132. data/lib/laser/third_party/rgl/control_flow.rb +170 -0
  133. data/lib/laser/third_party/rgl/depth_first_spanning_tree.rb +37 -0
  134. data/lib/laser/third_party/rgl/dominators.rb +124 -0
  135. data/lib/laser/third_party/rgl/dot.rb +93 -0
  136. data/lib/laser/third_party/rgl/graphxml.rb +51 -0
  137. data/lib/laser/third_party/rgl/implicit.rb +174 -0
  138. data/lib/laser/third_party/rgl/mutable.rb +117 -0
  139. data/lib/laser/third_party/rgl/rdot.rb +445 -0
  140. data/lib/laser/third_party/rgl/topsort.rb +72 -0
  141. data/lib/laser/third_party/rgl/transitivity.rb +180 -0
  142. data/lib/laser/third_party/rgl/traversal.rb +348 -0
  143. data/lib/laser/types/types.rb +433 -0
  144. data/lib/laser/version.rb +14 -0
  145. data/lib/laser/warning.rb +149 -0
  146. data/lib/laser/warning_sets/default.yml +13 -0
  147. data/lib/laser/warnings/assignment_in_condition.rb +20 -0
  148. data/lib/laser/warnings/comment_spacing.rb +31 -0
  149. data/lib/laser/warnings/extra_blank_lines.rb +30 -0
  150. data/lib/laser/warnings/extra_whitespace.rb +16 -0
  151. data/lib/laser/warnings/hash_symbol_18_warning.rb +63 -0
  152. data/lib/laser/warnings/hash_symbol_19_warning.rb +29 -0
  153. data/lib/laser/warnings/line_length.rb +115 -0
  154. data/lib/laser/warnings/misaligned_unindentation.rb +17 -0
  155. data/lib/laser/warnings/operator_spacing.rb +68 -0
  156. data/lib/laser/warnings/parens_on_declaration.rb +30 -0
  157. data/lib/laser/warnings/rescue_exception.rb +42 -0
  158. data/lib/laser/warnings/semicolon.rb +25 -0
  159. data/lib/laser/warnings/sexp_errors.rb +24 -0
  160. data/lib/laser/warnings/uncalled_method_warning.rb +7 -0
  161. data/lib/laser/warnings/useless_double_quotes.rb +38 -0
  162. data/spec/analysis_specs/annotations_spec.rb +47 -0
  163. data/spec/analysis_specs/annotations_specs/comment_attachment_spec.rb +68 -0
  164. data/spec/analysis_specs/annotations_specs/node_pointers_annotation_spec.rb +90 -0
  165. data/spec/analysis_specs/annotations_specs/runtime_annotation_spec.rb +135 -0
  166. data/spec/analysis_specs/annotations_specs/spec_helper.rb +33 -0
  167. data/spec/analysis_specs/argument_expansion_spec.rb +113 -0
  168. data/spec/analysis_specs/bindings_spec.rb +36 -0
  169. data/spec/analysis_specs/comment_spec.rb +93 -0
  170. data/spec/analysis_specs/control_flow_specs/cfg_instruction_spec.rb +111 -0
  171. data/spec/analysis_specs/control_flow_specs/constant_propagation_spec.rb +560 -0
  172. data/spec/analysis_specs/control_flow_specs/control_flow_graph_spec.rb +5 -0
  173. data/spec/analysis_specs/control_flow_specs/raise_properties_spec.rb +310 -0
  174. data/spec/analysis_specs/control_flow_specs/raise_type_inference_spec.rb +301 -0
  175. data/spec/analysis_specs/control_flow_specs/return_type_inference_spec.rb +431 -0
  176. data/spec/analysis_specs/control_flow_specs/simulation_spec.rb +158 -0
  177. data/spec/analysis_specs/control_flow_specs/spec_helper.rb +110 -0
  178. data/spec/analysis_specs/control_flow_specs/tuple_misuse_inference_spec.rb +125 -0
  179. data/spec/analysis_specs/control_flow_specs/unreachability_analysis_spec.rb +76 -0
  180. data/spec/analysis_specs/control_flow_specs/unused_variable_spec.rb +99 -0
  181. data/spec/analysis_specs/control_flow_specs/yield_properties_spec.rb +372 -0
  182. data/spec/analysis_specs/error_spec.rb +30 -0
  183. data/spec/analysis_specs/laser_class_spec.rb +322 -0
  184. data/spec/analysis_specs/lexical_analysis_spec.rb +184 -0
  185. data/spec/analysis_specs/protocol_registry_spec.rb +63 -0
  186. data/spec/analysis_specs/scope_annotation_spec.rb +1013 -0
  187. data/spec/analysis_specs/scope_spec.rb +126 -0
  188. data/spec/analysis_specs/sexp_analysis_spec.rb +30 -0
  189. data/spec/analysis_specs/sexp_extension_specs/constant_extraction_spec.rb +309 -0
  190. data/spec/analysis_specs/sexp_extension_specs/source_location_spec.rb +231 -0
  191. data/spec/analysis_specs/sexp_extension_specs/spec_helper.rb +1 -0
  192. data/spec/analysis_specs/sexp_extension_specs/type_inference_spec.rb +252 -0
  193. data/spec/analysis_specs/sexp_spec.rb +167 -0
  194. data/spec/analysis_specs/spec_helper.rb +27 -0
  195. data/spec/analysis_specs/unused_methods_spec.rb +65 -0
  196. data/spec/analysis_specs/visitor_spec.rb +64 -0
  197. data/spec/annotation_parser_specs/annotations_parser_spec.rb +89 -0
  198. data/spec/annotation_parser_specs/class_annotation_parser_spec.rb +120 -0
  199. data/spec/annotation_parser_specs/overload_parser_spec.rb +39 -0
  200. data/spec/annotation_parser_specs/parsers_spec.rb +14 -0
  201. data/spec/annotation_parser_specs/spec_helper.rb +1 -0
  202. data/spec/annotation_parser_specs/structural_parser_spec.rb +67 -0
  203. data/spec/laser_spec.rb +14 -0
  204. data/spec/rake_specs/spec_helper.rb +1 -0
  205. data/spec/rake_specs/task_spec.rb +67 -0
  206. data/spec/runner_spec.rb +207 -0
  207. data/spec/scanner_spec.rb +75 -0
  208. data/spec/spec_helper.rb +121 -0
  209. data/spec/standard_library/exceptions_spec.rb +19 -0
  210. data/spec/standard_library/globals_spec.rb +14 -0
  211. data/spec/standard_library/set_spec.rb +31 -0
  212. data/spec/standard_library/spec_helper.rb +1 -0
  213. data/spec/standard_library/standard_library_spec.rb +302 -0
  214. data/spec/support_specs/acts_as_struct_spec.rb +94 -0
  215. data/spec/support_specs/frequency_spec.rb +23 -0
  216. data/spec/support_specs/module_extensions_spec.rb +117 -0
  217. data/spec/support_specs/spec_helper.rb +1 -0
  218. data/spec/type_specs/spec_helper.rb +1 -0
  219. data/spec/type_specs/types_spec.rb +133 -0
  220. data/spec/warning_spec.rb +95 -0
  221. data/spec/warning_specs/assignment_in_condition_spec.rb +68 -0
  222. data/spec/warning_specs/comment_spacing_spec.rb +65 -0
  223. data/spec/warning_specs/extra_blank_lines_spec.rb +70 -0
  224. data/spec/warning_specs/extra_whitespace_spec.rb +33 -0
  225. data/spec/warning_specs/hash_symbol_18_warning_spec.rb +89 -0
  226. data/spec/warning_specs/hash_symbol_19_warning_spec.rb +63 -0
  227. data/spec/warning_specs/line_length_spec.rb +173 -0
  228. data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
  229. data/spec/warning_specs/operator_spacing_spec.rb +104 -0
  230. data/spec/warning_specs/parens_on_declaration_spec.rb +57 -0
  231. data/spec/warning_specs/rescue_exception_spec.rb +105 -0
  232. data/spec/warning_specs/semicolon_spec.rb +58 -0
  233. data/spec/warning_specs/spec_helper.rb +1 -0
  234. data/spec/warning_specs/useless_double_quotes_spec.rb +74 -0
  235. data/status_reports/2010/12/2010-12-14.md +163 -0
  236. data/status_reports/2010/12/2010-12-23.md +298 -0
  237. data/status_reports/2010/12/2010-12-24.md +6 -0
  238. data/test/third_party_tests/rgl_tests/TestComponents.rb +65 -0
  239. data/test/third_party_tests/rgl_tests/TestCycles.rb +61 -0
  240. data/test/third_party_tests/rgl_tests/TestDirectedGraph.rb +125 -0
  241. data/test/third_party_tests/rgl_tests/TestDot.rb +18 -0
  242. data/test/third_party_tests/rgl_tests/TestEdge.rb +34 -0
  243. data/test/third_party_tests/rgl_tests/TestGraph.rb +71 -0
  244. data/test/third_party_tests/rgl_tests/TestGraphXML.rb +57 -0
  245. data/test/third_party_tests/rgl_tests/TestImplicit.rb +52 -0
  246. data/test/third_party_tests/rgl_tests/TestRdot.rb +863 -0
  247. data/test/third_party_tests/rgl_tests/TestTransitivity.rb +129 -0
  248. data/test/third_party_tests/rgl_tests/TestTraversal.rb +220 -0
  249. data/test/third_party_tests/rgl_tests/TestUnDirectedGraph.rb +102 -0
  250. data/test/third_party_tests/rgl_tests/examples/north/Graph.log +128 -0
  251. data/test/third_party_tests/rgl_tests/examples/north/g.10.0.graphml +28 -0
  252. data/test/third_party_tests/rgl_tests/examples/north/g.10.1.graphml +28 -0
  253. data/test/third_party_tests/rgl_tests/examples/north/g.10.11.graphml +31 -0
  254. data/test/third_party_tests/rgl_tests/examples/north/g.10.12.graphml +27 -0
  255. data/test/third_party_tests/rgl_tests/examples/north/g.10.13.graphml +27 -0
  256. data/test/third_party_tests/rgl_tests/examples/north/g.10.14.graphml +27 -0
  257. data/test/third_party_tests/rgl_tests/examples/north/g.10.15.graphml +26 -0
  258. data/test/third_party_tests/rgl_tests/examples/north/g.10.16.graphml +26 -0
  259. data/test/third_party_tests/rgl_tests/examples/north/g.10.17.graphml +26 -0
  260. data/test/third_party_tests/rgl_tests/examples/north/g.10.19.graphml +37 -0
  261. data/test/third_party_tests/rgl_tests/examples/north/g.10.2.graphml +28 -0
  262. data/test/third_party_tests/rgl_tests/examples/north/g.10.20.graphml +38 -0
  263. data/test/third_party_tests/rgl_tests/examples/north/g.10.22.graphml +43 -0
  264. data/test/third_party_tests/rgl_tests/examples/north/g.10.24.graphml +30 -0
  265. data/test/third_party_tests/rgl_tests/examples/north/g.10.25.graphml +45 -0
  266. data/test/third_party_tests/rgl_tests/examples/north/g.10.27.graphml +38 -0
  267. data/test/third_party_tests/rgl_tests/examples/north/g.10.28.graphml +30 -0
  268. data/test/third_party_tests/rgl_tests/examples/north/g.10.29.graphml +38 -0
  269. data/test/third_party_tests/rgl_tests/examples/north/g.10.3.graphml +26 -0
  270. data/test/third_party_tests/rgl_tests/examples/north/g.10.30.graphml +34 -0
  271. data/test/third_party_tests/rgl_tests/examples/north/g.10.31.graphml +42 -0
  272. data/test/third_party_tests/rgl_tests/examples/north/g.10.34.graphml +42 -0
  273. data/test/third_party_tests/rgl_tests/examples/north/g.10.37.graphml +28 -0
  274. data/test/third_party_tests/rgl_tests/examples/north/g.10.38.graphml +38 -0
  275. data/test/third_party_tests/rgl_tests/examples/north/g.10.39.graphml +36 -0
  276. data/test/third_party_tests/rgl_tests/examples/north/g.10.4.graphml +26 -0
  277. data/test/third_party_tests/rgl_tests/examples/north/g.10.40.graphml +37 -0
  278. data/test/third_party_tests/rgl_tests/examples/north/g.10.41.graphml +37 -0
  279. data/test/third_party_tests/rgl_tests/examples/north/g.10.42.graphml +26 -0
  280. data/test/third_party_tests/rgl_tests/examples/north/g.10.45.graphml +28 -0
  281. data/test/third_party_tests/rgl_tests/examples/north/g.10.46.graphml +32 -0
  282. data/test/third_party_tests/rgl_tests/examples/north/g.10.5.graphml +31 -0
  283. data/test/third_party_tests/rgl_tests/examples/north/g.10.50.graphml +30 -0
  284. data/test/third_party_tests/rgl_tests/examples/north/g.10.56.graphml +29 -0
  285. data/test/third_party_tests/rgl_tests/examples/north/g.10.57.graphml +32 -0
  286. data/test/third_party_tests/rgl_tests/examples/north/g.10.58.graphml +32 -0
  287. data/test/third_party_tests/rgl_tests/examples/north/g.10.6.graphml +26 -0
  288. data/test/third_party_tests/rgl_tests/examples/north/g.10.60.graphml +32 -0
  289. data/test/third_party_tests/rgl_tests/examples/north/g.10.61.graphml +34 -0
  290. data/test/third_party_tests/rgl_tests/examples/north/g.10.62.graphml +34 -0
  291. data/test/third_party_tests/rgl_tests/examples/north/g.10.68.graphml +30 -0
  292. data/test/third_party_tests/rgl_tests/examples/north/g.10.69.graphml +32 -0
  293. data/test/third_party_tests/rgl_tests/examples/north/g.10.7.graphml +29 -0
  294. data/test/third_party_tests/rgl_tests/examples/north/g.10.70.graphml +26 -0
  295. data/test/third_party_tests/rgl_tests/examples/north/g.10.71.graphml +27 -0
  296. data/test/third_party_tests/rgl_tests/examples/north/g.10.72.graphml +28 -0
  297. data/test/third_party_tests/rgl_tests/examples/north/g.10.74.graphml +29 -0
  298. data/test/third_party_tests/rgl_tests/examples/north/g.10.75.graphml +29 -0
  299. data/test/third_party_tests/rgl_tests/examples/north/g.10.78.graphml +27 -0
  300. data/test/third_party_tests/rgl_tests/examples/north/g.10.79.graphml +34 -0
  301. data/test/third_party_tests/rgl_tests/examples/north/g.10.8.graphml +29 -0
  302. data/test/third_party_tests/rgl_tests/examples/north/g.10.80.graphml +34 -0
  303. data/test/third_party_tests/rgl_tests/examples/north/g.10.82.graphml +35 -0
  304. data/test/third_party_tests/rgl_tests/examples/north/g.10.83.graphml +32 -0
  305. data/test/third_party_tests/rgl_tests/examples/north/g.10.85.graphml +34 -0
  306. data/test/third_party_tests/rgl_tests/examples/north/g.10.86.graphml +34 -0
  307. data/test/third_party_tests/rgl_tests/examples/north/g.10.88.graphml +37 -0
  308. data/test/third_party_tests/rgl_tests/examples/north/g.10.89.graphml +29 -0
  309. data/test/third_party_tests/rgl_tests/examples/north/g.10.9.graphml +26 -0
  310. data/test/third_party_tests/rgl_tests/examples/north/g.10.90.graphml +32 -0
  311. data/test/third_party_tests/rgl_tests/examples/north/g.10.91.graphml +31 -0
  312. data/test/third_party_tests/rgl_tests/examples/north/g.10.92.graphml +26 -0
  313. data/test/third_party_tests/rgl_tests/examples/north/g.10.93.graphml +32 -0
  314. data/test/third_party_tests/rgl_tests/examples/north/g.10.94.graphml +34 -0
  315. data/test/third_party_tests/rgl_tests/examples/north/g.12.8.graphml +40 -0
  316. data/test/third_party_tests/rgl_tests/examples/north/g.14.9.graphml +36 -0
  317. data/test/third_party_tests/rgl_tests/test_helper.rb +7 -0
  318. data/test/third_party_tests/test_inheritable_attributes.rb +187 -0
  319. metadata +470 -0
@@ -0,0 +1,2505 @@
1
+ module Laser
2
+ module Analysis
3
+ module ControlFlow
4
+ # This class builds a control flow graph. The algorithm used is
5
+ # derived from Robert Morgan's "Building an Optimizing Compiler".
6
+ class GraphBuilder
7
+ attr_reader :graph, :enter, :exit, :temporary_counter, :current_block, :sexp
8
+ attr_reader :self_register
9
+
10
+ def initialize(sexp, formals=[], scope=Scope::GlobalScope)
11
+ @sexp = sexp
12
+ @formals = formals
13
+ @graph = @enter = @exit = nil
14
+ @scope_stack = [scope]
15
+ @self_stack = []
16
+ @temporary_counter = 0
17
+ @temporary_table = Hash.new do |hash, keys|
18
+ @temporary_counter += 1
19
+ hash[keys] = Bindings::TemporaryBinding.new("%t#{@temporary_counter}", nil)
20
+ end
21
+ end
22
+
23
+ def build
24
+ initialize_graph
25
+ @current_node = @sexp
26
+ @self_register = Bindings::TemporaryBinding.new('self', current_scope.self_ptr)
27
+ build_prologue
28
+ result = walk_node @sexp, value: true
29
+ if @sexp.type == :program
30
+ uncond_instruct @current_return
31
+ else
32
+ return_uncond_jump_instruct result
33
+ end
34
+
35
+ @graph.prune_totally_useless_blocks
36
+ @graph
37
+ end
38
+
39
+ def current_namespace
40
+ scope = current_scope
41
+ scope = scope.parent if @scope_stack.size == 1 && @sexp.type != :program
42
+ scope.lexical_target
43
+ end
44
+
45
+ def push_scope(scope)
46
+ @scope_stack.push scope
47
+ end
48
+
49
+ def current_scope
50
+ @scope_stack.last
51
+ end
52
+
53
+ def pop_scope
54
+ @scope_stack.pop
55
+ end
56
+
57
+ def with_scope(scope)
58
+ push_scope scope
59
+ yield
60
+ ensure
61
+ pop_scope
62
+ end
63
+
64
+ def push_self(obj)
65
+ copy_instruct(@self_register, obj)
66
+ @self_stack.push obj
67
+ end
68
+
69
+ def current_self
70
+ @self_stack.last
71
+ end
72
+
73
+ def pop_self
74
+ @self_stack.pop
75
+ copy_instruct(@self_register, current_self)
76
+ end
77
+
78
+ def with_self(namespace)
79
+ push_self namespace
80
+ yield
81
+ ensure
82
+ pop_self
83
+ end
84
+
85
+ def query_self
86
+ call_instruct(ClassRegistry['Laser#Magic'].binding,
87
+ :current_self, value: true, raise: false)
88
+ end
89
+
90
+ def reobserve_current_exception
91
+ cur_exception = call_instruct(ClassRegistry['Laser#Magic'].binding,
92
+ :current_exception, value: true, raise: false)
93
+ copy_instruct(@exception_register, cur_exception)
94
+ end
95
+
96
+ def observe_just_raised_exception
97
+ cur_exception = call_instruct(ClassRegistry['Laser#Magic'].binding,
98
+ :get_just_raised_exception, value: true, raise: false)
99
+ copy_instruct(@exception_register, cur_exception)
100
+ end
101
+
102
+ def build_exception_blocks
103
+ @return_register = create_temporary('t#return_value')
104
+ @graph.final_return = create_temporary('t#final_return')
105
+ @exception_register = create_temporary('t#exception_value')
106
+ @graph.final_exception = create_temporary('t#exit_exception')
107
+ @current_return = create_block(ControlFlowGraph::RETURN_POSTDOMINATOR_NAME)
108
+ @current_rescue = create_block(ControlFlowGraph::EXCEPTION_POSTDOMINATOR_NAME)
109
+ @current_yield_fail = create_block(ControlFlowGraph::YIELD_POSTDOMINATOR_NAME)
110
+ joined = create_block(ControlFlowGraph::FAILURE_POSTDOMINATOR_NAME)
111
+ with_current_basic_block(@current_rescue) do
112
+ uncond_instruct joined, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
113
+ end
114
+ with_current_basic_block(@current_yield_fail) do
115
+ uncond_instruct joined, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
116
+ end
117
+ with_current_basic_block(joined) do
118
+ copy_instruct(@graph.final_exception, @exception_register)
119
+ add_instruction(:raise, @graph.final_exception)
120
+ uncond_instruct @exit, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL, jump_instruct: false
121
+ end
122
+ with_current_basic_block(@current_return) do
123
+ copy_instruct(@graph.final_return, @return_register)
124
+ add_instruction(:return, @graph.final_return)
125
+ uncond_instruct @exit, jump_instruct: false
126
+ end
127
+ end
128
+
129
+ def reset_visibility_stack
130
+ initial = call_instruct(ClassRegistry['Array'].binding, :[], const_instruct(:private), value: true, raise: false)
131
+ copy_instruct(Bootstrap::VISIBILITY_STACK, initial)
132
+ end
133
+
134
+ def build_prologue
135
+ uncond_instruct create_block
136
+ if @sexp.type == :program
137
+ push_self(Scope::GlobalScope.self_ptr)
138
+ else
139
+ push_self(query_self)
140
+ end
141
+ reset_visibility_stack
142
+ build_exception_blocks
143
+ if @sexp.type != :program
144
+ dynamic_context = ClosedScope.new(current_scope, current_self)
145
+ @scope_stack = [dynamic_context]
146
+ @block_arg = call_instruct(ClassRegistry['Laser#Magic'].binding,
147
+ :current_block, value: true, raise: false)
148
+ @block_arg.name = 't#current_block'
149
+ @graph.block_register = @block_arg
150
+ reobserve_current_exception
151
+ build_formal_args(@formals, block_arg: @block_arg) unless @formals.empty?
152
+ end
153
+ end
154
+
155
+ def formal_arg_range(start, size)
156
+ call_instruct(ClassRegistry['Laser#Magic'].binding, :current_argument_range,
157
+ start, size, value: true, raise: false)
158
+ end
159
+
160
+ def formal_arg_at(idx)
161
+ call_instruct(ClassRegistry['Laser#Magic'].binding, :current_argument, idx,
162
+ value: true, raise: false)
163
+ end
164
+
165
+ def copy_positionals(args)
166
+ args.each_with_index do |pos, idx|
167
+ copy_instruct(pos, formal_arg_at(const_instruct(idx)))
168
+ end
169
+ end
170
+
171
+ def copy_positionals_with_offset(args, offset)
172
+ args.each_with_index do |pos, idx|
173
+ dynamic_idx = idx.zero? ? offset : call_instruct(const_instruct(idx), :+, offset,
174
+ value: true, raise: false)
175
+ copy_instruct(pos, formal_arg_at(dynamic_idx))
176
+ end
177
+ end
178
+
179
+ def build_formal_args(formals, opts={})
180
+ formals.each { |formal| current_scope.add_binding!(formal) }
181
+
182
+ nb_formals = formals.last.is_block? ? formals[0..-2] : formals
183
+ has_rest = rest_arg = nb_formals.find(&:is_rest?)
184
+ min_arity = nb_formals.count(&:is_positional?)
185
+ optionals = nb_formals.select(&:is_optional?)
186
+ num_optionals = optionals.size
187
+ min_nonrest_arity = min_arity + num_optionals
188
+ # zero dynamic args = easy and more efficient case
189
+ if !rest_arg && optionals.empty?
190
+ copy_positionals(nb_formals)
191
+ else
192
+ # pre-dynamic positionals
193
+ cur_arity = call_instruct(ClassRegistry['Laser#Magic'].binding,
194
+ :current_arity, value: true, raise: false)
195
+ pre_dynamic_positional = nb_formals.take_while(&:is_positional?)
196
+ num_pre_dynamics = pre_dynamic_positional.size
197
+ copy_positionals(pre_dynamic_positional)
198
+ # optional args
199
+ previous_optional_block = nil
200
+ optionals.each_with_index do |argument, index|
201
+ max_arity_indicating_missing = const_instruct(index + min_arity)
202
+ has_arg, no_arg = create_blocks 2
203
+ if previous_optional_block
204
+ with_current_basic_block(previous_optional_block) do
205
+ uncond_instruct no_arg
206
+ end
207
+ end
208
+ cond_result = call_instruct(cur_arity, :<, max_arity_indicating_missing,
209
+ value: true, raise: false)
210
+ cond_instruct cond_result, no_arg, has_arg
211
+
212
+ start_block no_arg
213
+ arg_value = walk_node(argument.default_value_sexp, value: true)
214
+ copy_instruct(argument, arg_value)
215
+ previous_optional_block = @current_block
216
+
217
+ start_block has_arg
218
+ copy_instruct(argument, formal_arg_at(const_instruct(index + num_pre_dynamics)))
219
+ end
220
+
221
+ optionals_done = create_block
222
+ # rest args
223
+ if has_rest
224
+ rest_start = const_instruct(num_pre_dynamics + num_optionals)
225
+ rest_size = call_instruct(cur_arity, :-, const_instruct(min_nonrest_arity), value: true, raise: false)
226
+ copy_instruct(rest_arg, formal_arg_range(rest_start, rest_size))
227
+ end
228
+ uncond_instruct optionals_done
229
+
230
+ if previous_optional_block
231
+ with_current_basic_block(previous_optional_block) do
232
+ # at this point, if there was a rest arg, it's empty.
233
+ if has_rest
234
+ empty_rest = call_instruct(ClassRegistry['Array'].binding, :[], value: true, raise: false)
235
+ copy_instruct(rest_arg, empty_rest)
236
+ end
237
+ uncond_instruct optionals_done
238
+ end
239
+ end
240
+ start_block optionals_done
241
+
242
+ # post-dynamic conditionals
243
+ post_dynamic_positional = nb_formals[num_pre_dynamics..-1].select(&:is_positional?)
244
+ post_dynamic_start = call_instruct(cur_arity, :-,
245
+ const_instruct(post_dynamic_positional.size), value: true, raise: false)
246
+ copy_positionals_with_offset(post_dynamic_positional, post_dynamic_start)
247
+ end
248
+ block_arg = formals.find(&:is_block?)
249
+ if block_arg
250
+ if opts[:block_arg]
251
+ the_block = opts[:block_arg]
252
+ else
253
+ the_block = call_instruct(ClassRegistry['Laser#Magic'].binding,
254
+ :current_block, value: true, raise: false)
255
+ end
256
+ copy_instruct(block_arg, the_block)
257
+ end
258
+ end
259
+
260
+ # Creates a new block that jumps to the given target upon completion.
261
+ # Very useful for building branches.
262
+ def build_block_with_jump(target = nil, name = nil)
263
+ new_block = name ? create_block(name) : create_block
264
+ with_current_basic_block(new_block) do
265
+ yield
266
+ uncond_instruct target if target
267
+ end
268
+ new_block
269
+ end
270
+
271
+ # yields with the current basic block set to the provided basic block.
272
+ # useful for quickly adding an edge without directly touching the
273
+ # graph object.
274
+ def with_current_basic_block(basic_block)
275
+ old_block, @current_block = @current_block, basic_block
276
+ yield
277
+ ensure
278
+ @current_block = old_block
279
+ end
280
+
281
+ def with_current_node(node)
282
+ old_node, @current_node = @current_node, node
283
+ @current_node.scope = current_scope
284
+ yield
285
+ ensure
286
+ @current_node = old_node
287
+ end
288
+
289
+ # Walks the node differently based on whether the value is needed.
290
+ def walk_node(node, opts={})
291
+ with_current_node(node) do
292
+ case node.type
293
+ when :bodystmt
294
+ bodystmt_walk node
295
+ when :class
296
+ class_name, superclass, body = node.children
297
+ class_instruct(class_name, superclass, body, opts)
298
+ when :module
299
+ module_name, body = node.children
300
+ module_instruct(module_name, body, opts)
301
+ when :sclass
302
+ receiver, body = node.children
303
+ singleton_class_instruct receiver, body, opts
304
+ when :def
305
+ name, args, body = node.children
306
+ name = const_instruct(name.expanded_identifier.to_sym)
307
+ parsed_args = Signature.arg_list_for_arglist(args)
308
+ def_instruct(current_namespace, name, parsed_args, body, opts)
309
+ when :defs
310
+ recv, _, name, args, body = node.children
311
+ name = const_instruct(name.expanded_identifier)
312
+ receiver = walk_node(recv, value: true)
313
+ singleton = call_instruct(receiver, :singleton_class, value: true)
314
+ parsed_args = Signature.arg_list_for_arglist(args)
315
+ def_instruct(singleton, name, parsed_args, body, opts)
316
+ when :alias
317
+ lhs, rhs = node.children
318
+ lhs_val = const_instruct(lhs[1].expanded_identifier.to_sym)
319
+ rhs_val = const_instruct(rhs[1].expanded_identifier.to_sym)
320
+ call_instruct(current_namespace, :alias_method, lhs_val, rhs_val,
321
+ value: false, ignore_privacy: true)
322
+ when :undef
323
+ undeffed = node.children.first
324
+ undeffed.each do |method|
325
+ method_name = const_instruct(method[1].expanded_identifier.to_sym)
326
+ call_instruct(current_namespace, :undef_method, method_name,
327
+ value: false, raise: true)
328
+ end
329
+ nil
330
+ when :assign
331
+ lhs, rhs = node.children
332
+ single_assign_instruct(lhs, rhs, opts)
333
+ when :massign
334
+ lhs, rhs = node.children
335
+ multiple_assign_instruct(lhs, rhs, opts)
336
+ when :begin
337
+ walk_node node[1], opts
338
+ when :paren
339
+ walk_body node[1], opts
340
+ when :while
341
+ condition, body = node.children
342
+ while_instruct(condition, body, opts)
343
+ when :while_mod
344
+ condition, body_stmt = node.children
345
+ while_instruct(condition, [body_stmt], opts)
346
+ when :until
347
+ condition, body = node.children
348
+ until_instruct(condition, body, opts)
349
+ when :until_mod
350
+ condition, body_stmt = node.children
351
+ until_instruct(condition, [body_stmt], opts)
352
+ when :if
353
+ if_instruct(node, false, opts)
354
+ when :unless
355
+ condition, body, else_block = node.children
356
+ unless_instruct(condition, body, else_block, opts)
357
+ when :if_mod
358
+ if_instruct(node, true, opts)
359
+ when :unless_mod
360
+ condition, body = node.children
361
+ unless_instruct(condition, [body], nil, opts)
362
+ when :rescue_mod
363
+ rescue_expr, guarded_expr = node.children
364
+ rescue_mod_instruct(rescue_expr, guarded_expr, opts)
365
+ when :unary
366
+ op, receiver = node.children
367
+ receiver = walk_node(receiver, value: true)
368
+ call_instruct(receiver, op, opts)
369
+ when :binary
370
+ # If someone makes an overloaded operator that mutates something....
371
+ # we have to run it (maybe), even if we hate them.
372
+ lhs, op, rhs = node.children
373
+ binary_instruct(lhs, op, rhs, opts)
374
+ when :ifop
375
+ cond, if_true, if_false = node.children
376
+ ternary_instruct(cond, if_true, if_false, opts)
377
+ when :const_path_ref
378
+ lhs, const = node.children
379
+ lhs_value = walk_node lhs, value: true
380
+ ident = const_instruct(const.expanded_identifier)
381
+ call_instruct(lhs_value, :const_get, ident, opts)
382
+ when :call, :command, :command_call, :aref, :method_add_arg, :vcall
383
+ issue_call node, opts
384
+ when :method_add_block
385
+ # need: the receiver, the method name, the arguments, and the block body
386
+ method_call = node.method_call
387
+ receiver = if method_call.receiver_node
388
+ then walk_node(method_call.receiver_node, value: true)
389
+ else self_instruct
390
+ end
391
+ arg_node = method_call.arg_node
392
+ arg_node = arg_node[1] if arg_node && arg_node.type == :arg_paren
393
+ block_arg_bindings = node[2][1] ? Signature.arg_list_for_arglist(node[2][1][1]) : []
394
+ body_sexp = node[2][2]
395
+ case node[1].type
396
+ when :super
397
+ arg_node = arg_node[1] if arg_node.type == :args_add_block
398
+ call_method_with_block(
399
+ receiver, method_call.method_name, arg_node,
400
+ block_arg_bindings, body_sexp, opts)
401
+ when :zsuper
402
+ call_zsuper_with_block(node[1], block_arg_bindings, body_sexp, opts)
403
+ else
404
+ opts = opts.merge(ignore_privacy: true) if method_call.implicit_receiver?
405
+ call_method_with_block(
406
+ receiver, method_call.method_name, arg_node, block_arg_bindings, body_sexp, opts)
407
+ end
408
+ when :super
409
+ issue_super_call(node)
410
+ when :zsuper
411
+ # TODO(adgar): blocks in args & style
412
+ block = rb_check_convert_type(@block_arg, ClassRegistry['Proc'].binding, :to_proc)
413
+ invoke_super_with_block(*compute_zsuper_arguments, block, opts)
414
+ when :yield
415
+ yield_instruct(node[1], opts)
416
+ when :yield0
417
+ yield_instruct(nil, opts)
418
+ when :return
419
+ return_instruct node
420
+ const_instruct(nil) if opts[:value]
421
+ when :return0
422
+ return0_instruct
423
+ const_instruct(nil) if opts[:value]
424
+ when :break
425
+ break_instruct(node[1])
426
+ const_instruct(nil) if opts[:value]
427
+ when :next
428
+ next_instruct(node[1])
429
+ const_instruct(nil) if opts[:value]
430
+ when :redo
431
+ redo_instruct
432
+ const_instruct(nil) if opts[:value]
433
+ when :void_stmt
434
+ const_instruct(nil) if opts[:value]
435
+ when :program
436
+ uncond_instruct create_block
437
+ walk_body node[1], value: false
438
+ when :dot3
439
+ start, stop = node.children
440
+ start_val = walk_node(start, value: true)
441
+ stop_val = walk_node(stop, value: true)
442
+ true_val = const_instruct(true)
443
+ call_instruct(ClassRegistry['Range'].binding, :new, start_val, stop_val, true_val, opts)
444
+ when :dot2
445
+ start, stop = node.children
446
+ start_val = walk_node(start, value: true)
447
+ stop_val = walk_node(stop, value: true)
448
+ false_val = const_instruct(false)
449
+ call_instruct(ClassRegistry['Range'].binding, :new, start_val, stop_val, false_val, opts)
450
+ else
451
+ opts[:value] ? value_walk(node) : novalue_walk(node)
452
+ end
453
+ end
454
+ end
455
+
456
+ # Walks the node expecting that the expression's return value will be discarded.
457
+ # Since everything is an expression in Ruby, knowing when to ignore return
458
+ # values is nice.
459
+ def novalue_walk(node)
460
+ with_current_node(node) do
461
+ case node.type
462
+ when :void_stmt
463
+ # Do nothing.
464
+ when :massign
465
+ lhs, rhs = node.children
466
+ multiple_assign_instruct(lhs, rhs, value: false)
467
+ when :opassign
468
+ lhs, op, rhs = node.children
469
+ op = op.expanded_identifier[0..-2].to_sym
470
+ if lhs.type == :field
471
+ receiver = walk_node lhs[1], value: true
472
+ method_name = lhs[3].expanded_identifier
473
+ # Receiver is ONLY EVALUATED ONCE
474
+ # (on ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0])
475
+ current_val = call_instruct(receiver, method_name.to_sym, block: false, value: true)
476
+ if op == :"||"
477
+ false_block, after = create_blocks 2
478
+ cond_instruct(current_val, after, false_block)
479
+
480
+ start_block false_block
481
+ rhs_value = walk_node rhs, value: true
482
+ call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
483
+ uncond_instruct after
484
+
485
+ start_block after
486
+ elsif op == :"&&"
487
+ true_block, after = create_blocks 2
488
+ cond_instruct(current_val, true_block, after)
489
+
490
+ start_block true_block
491
+ rhs_value = walk_node rhs, value: true
492
+ call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
493
+ uncond_instruct after
494
+
495
+ start_block after
496
+ else
497
+ rhs_value = walk_node rhs, value: true
498
+ temp_result = call_instruct(current_val, op, rhs_value, block: false, value: true)
499
+ call_instruct(receiver, "#{method_name}=".to_sym, temp_result, block: false, value: false)
500
+ end
501
+ # TODO(adgar): aref_field
502
+ else
503
+ result = binary_instruct(lhs, op, rhs, value: true)
504
+ single_assign_instruct(lhs, result)
505
+ end
506
+ when :case
507
+ after = create_block
508
+ argument, body = node.children
509
+ argument_value = walk_node argument, value: true
510
+
511
+ while body && body.type == :when
512
+ when_opts, when_body, body = body.children
513
+ when_body_block = create_block
514
+ when_opts.each do |opt|
515
+ after_fail = create_block
516
+ condition_result = call_instruct(walk_node(opt, value: true), :===, argument_value, value: true)
517
+ cond_instruct(condition_result, when_body_block, after_fail)
518
+ start_block after_fail
519
+ end
520
+ all_fail = @current_block
521
+
522
+ start_block when_body_block
523
+ walk_body when_body, value: false
524
+ uncond_instruct after
525
+
526
+ start_block all_fail
527
+ end
528
+ if body && body.type == :else
529
+ walk_body body[1], value: false
530
+ end
531
+ uncond_instruct after
532
+ when :var_ref
533
+ nil
534
+ when :for
535
+ lhs, receiver, body = node.children
536
+ receiver_value = walk_node receiver, value: true
537
+ if Symbol === lhs[0]
538
+ # field or var_ref/const_ref
539
+ case lhs.type
540
+ when :field
541
+ # TODO(adgar): generate calls
542
+ else
543
+ # just get the value
544
+ arg_bindings = [lhs.binding]
545
+ call_method_with_block(receiver_value, :each, [], arg_bindings, body, value: false)
546
+ end
547
+ else
548
+ # TODO(adgar): multiple assign
549
+ end
550
+ when :string_embexpr
551
+ node[1].each { |elt| walk_node(elt, value: false) }
552
+ when :@CHAR, :@tstring_content, :@int, :@float, :@regexp_end, :symbol,
553
+ :@label, :symbol_literal, :defined
554
+ # do nothing
555
+ when :string_literal
556
+ content_nodes = node[1].children
557
+ content_nodes.each do |node|
558
+ walk_node node, value: false
559
+ end
560
+ when :xstring_literal
561
+ body = build_string_instruct(node[1])
562
+ call_instruct(self_register, :`, body, value: false)
563
+ when :regexp_literal
564
+ node[1].each { |part| walk_node node, value: false }
565
+ when :dyna_symbol
566
+ content_nodes = node[1].children
567
+ content_nodes.each { |node| walk_node node, value: false }
568
+ when :array
569
+ receiver = ClassRegistry['Array'].binding
570
+ generic_call_instruct(receiver, :[], node[1], false, value: false)
571
+ when :hash
572
+ if node[1]
573
+ walk_node node[1], value: false
574
+ else
575
+ const_instruct({}, value: false)
576
+ end
577
+ when :assoclist_from_args, :bare_assoc_hash
578
+ pairs = node[1]
579
+ key_value_paired = pairs.map {|a, b| [walk_node(a, value: true), walk_node(b, value: true)] }.flatten
580
+ receiver = ClassRegistry['Hash'].binding
581
+ call_instruct(receiver, :[], *key_value_paired, block: false, value: false, raise: false)
582
+ else
583
+ raise ArgumentError.new("Unknown AST node type #{node.type.inspect}")
584
+ end
585
+ end
586
+ end
587
+
588
+ # Walks the node with the expectation that the return value will be used.
589
+ def value_walk(node)
590
+ with_current_node(node) do
591
+ case node.type
592
+ when :opassign
593
+ lhs, op, rhs = node.children
594
+ op = op.expanded_identifier[0..-2].to_sym
595
+ if lhs.type == :field
596
+ receiver = walk_node lhs[1], value: true
597
+ method_name = lhs[3].expanded_identifier
598
+ # Receiver is ONLY EVALUATED ONCE
599
+ # (on ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0])
600
+ current_val = call_instruct(receiver, method_name.to_sym, block: false, value: true)
601
+ if op == :"||"
602
+ result = create_temporary
603
+ true_block, false_block, after = create_blocks 3
604
+ cond_instruct(current_val, true_block, false_block)
605
+
606
+ start_block true_block
607
+ copy_instruct result, current_val
608
+ uncond_instruct after
609
+
610
+ start_block false_block
611
+ rhs_value = walk_node rhs, value: true
612
+ call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
613
+ copy_instruct result, rhs_value
614
+ uncond_instruct after
615
+
616
+ start_block after
617
+ result
618
+ elsif op == :"&&"
619
+ result = create_temporary
620
+ true_block, false_block, after = create_blocks 3
621
+ cond_instruct(current_val, true_block, false_block)
622
+
623
+ start_block true_block
624
+ rhs_value = walk_node rhs, value: true
625
+ call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false)
626
+ copy_instruct result, rhs_value
627
+ uncond_instruct after
628
+
629
+ start_block false_block
630
+ copy_instruct result, current_val
631
+ uncond_instruct after
632
+
633
+ start_block after
634
+ result
635
+ else
636
+ rhs_value = walk_node rhs, value: true
637
+ temp_result = call_instruct(current_val, op, rhs_value, block: false, value: true)
638
+ call_instruct(receiver, "#{method_name}=".to_sym, temp_result, block: false, value: false)
639
+ temp_result
640
+ end
641
+ # TODO(adgar): aref_field
642
+ else
643
+ result = binary_instruct(lhs, op, rhs, value: true)
644
+ single_assign_instruct(lhs, result)
645
+ result
646
+ end
647
+ when :var_field
648
+ variable_instruct(node)
649
+ when :var_ref
650
+ if node[1].type == :@const
651
+ const_lookup(node[1].expanded_identifier)
652
+ elsif node[1].type == :@ident || node[1].expanded_identifier == 'self'
653
+ variable_instruct(node)
654
+ elsif node[1].type == :@kw
655
+ const_instruct(node.constant_value)
656
+ elsif node[1].type == :@ivar
657
+ call_instruct(current_self, :instance_variable_get,
658
+ const_instruct(node.expanded_identifier), value: true, ignore_privacy: true)
659
+ elsif node[1].type == :@gvar
660
+ call_instruct(ClassRegistry['Laser#Magic'].binding, :get_global,
661
+ const_instruct(node.expanded_identifier), raise: false, value: true)
662
+ end
663
+ when :top_const_ref
664
+ const = node[1]
665
+ ident = const_instruct(const.expanded_identifier)
666
+ call_instruct(ClassRegistry['Object'].binding,
667
+ :const_get, ident, value: true)
668
+ when :for
669
+ lhs, receiver, body = node.children
670
+ receiver_value = walk_node receiver, value: true
671
+ if Symbol === lhs[0]
672
+ # field or var_ref/const_ref
673
+ case lhs.type
674
+ when :field
675
+ # call
676
+ else
677
+ # just get the value
678
+ arg_bindings = [lhs.binding]
679
+ call_method_with_block(receiver_value, :each, [], arg_bindings, body, value: true)
680
+ end
681
+ # TODO(adgar): aref_field
682
+ else
683
+ # TODO(adgar): multiple assign
684
+ end
685
+ when :case
686
+ after = create_block
687
+ result = create_temporary
688
+ argument, body = node.children
689
+ argument_value = walk_node argument, value: true
690
+
691
+ while body && body.type == :when
692
+ when_opts, when_body, body = body.children
693
+ when_body_block = create_block
694
+ when_opts.each do |opt|
695
+ after_fail = create_block
696
+ condition_result = call_instruct(walk_node(opt, value: true), :===, argument_value, value: true)
697
+ cond_instruct(condition_result, when_body_block, after_fail)
698
+ start_block after_fail
699
+ end
700
+ all_fail = @current_block
701
+
702
+ start_block when_body_block
703
+ when_body_result = walk_body when_body, value: true
704
+ copy_instruct(result, when_body_result)
705
+ uncond_instruct after
706
+
707
+ start_block all_fail
708
+ end
709
+ if body.nil?
710
+ copy_instruct(result, nil)
711
+ uncond_instruct after
712
+ elsif body.type == :else
713
+ else_body_result = walk_body body[1], value: true
714
+ copy_instruct(result, else_body_result)
715
+ uncond_instruct after
716
+ end
717
+
718
+ start_block after
719
+ result
720
+ when :@CHAR, :@tstring_content, :@int, :@float, :@regexp_end, :symbol,
721
+ :@label, :symbol_literal
722
+ const_instruct(node.constant_value)
723
+ when :string_literal
724
+ content_nodes = node[1].children
725
+ build_string_instruct(content_nodes)
726
+ when :string_embexpr
727
+ final = walk_body node[1], value: true
728
+ call_instruct(final, :to_s, value: true)
729
+ when :xstring_literal
730
+ body = build_string_instruct(node[1])
731
+ call_instruct(self_register, :`, body, value: true)
732
+ when :regexp_literal
733
+ body = build_string_instruct(node[1])
734
+ options = const_instruct(node[2].constant_value)
735
+ receiver = ClassRegistry['Regexp'].binding
736
+ call_instruct(receiver, :new, body, options, value: true)
737
+ when :dyna_symbol
738
+ content_nodes = node[1].children
739
+ string_version = build_string_instruct(content_nodes)
740
+ call_instruct(string_version, :to_sym, value: true, raise: false)
741
+ when :array
742
+ receiver = ClassRegistry['Array'].binding
743
+ generic_call_instruct(receiver, :[], node[1], false, value: true)
744
+ when :hash
745
+ if node[1]
746
+ walk_node node[1], value: true
747
+ else
748
+ const_instruct({})
749
+ end
750
+ when :assoclist_from_args, :bare_assoc_hash
751
+ pairs = node[1].map { |_, k, v| [k, v] }
752
+ key_value_paired = pairs.map {|a, b| [walk_node(a, value: true), walk_node(b, value: true)] }.flatten
753
+ receiver = ClassRegistry['Hash'].binding
754
+ call_instruct(receiver, :[], *key_value_paired, block: false, value: true)
755
+ when :defined
756
+ defined_op_instruct(node[1])
757
+ else
758
+ raise ArgumentError.new("Unknown AST node type #{node.type.inspect}")
759
+ end
760
+ end
761
+ end
762
+
763
+ private
764
+
765
+ def initialize_graph
766
+ @graph = ControlFlowGraph.new(@formals)
767
+ @graph.root = @sexp
768
+ @block_counter = 0
769
+ @enter = @graph.enter
770
+ @exit = @graph.exit
771
+ @temporary_counter = 0
772
+ @current_break = @current_next = @current_redo = @current_return = @current_rescue = nil
773
+ start_block @enter
774
+ end
775
+
776
+ # Redirects break, next, redo, and return to the given Sexp for each
777
+ # target to redirect.
778
+ def with_jumps_redirected(targets={})
779
+ new_targets = targets.merge(targets) do |key, redirect|
780
+ current = send("current_#{key}")
781
+ next nil unless current
782
+ build_block_with_jump(current) do
783
+ walk_body redirect, value: false
784
+ end
785
+ end.delete_if { |k, v| v.nil? }
786
+ with_jump_targets(new_targets) do
787
+ yield
788
+ end
789
+ end
790
+
791
+ # Yields with jump targets specified. Since a number of jump targets
792
+ # require temporary specification in a stack-like fashion during CFG construction,
793
+ # I use the call stack to simulate the explicit one suggested by Morgan.
794
+ def with_jump_targets(targets={})
795
+ old_break, old_next, old_redo, old_return, old_rescue, old_yield_fail =
796
+ @current_break, @current_next, @current_redo, @current_return, @current_rescue, @current_yield_fail
797
+ @current_break = targets[:break] if targets.has_key?(:break)
798
+ @current_next = targets[:next] if targets.has_key?(:next)
799
+ @current_redo = targets[:redo] if targets.has_key?(:redo)
800
+ @current_return = targets[:return] if targets.has_key?(:return)
801
+ @current_rescue = targets[:rescue] if targets.has_key?(:rescue)
802
+ @current_yield_fail = targets[:yield_fail] if targets.has_key?(:yield_fail)
803
+ yield
804
+ ensure
805
+ @current_break, @current_next, @current_redo, @current_return, @current_rescue, @current_yield_fail =
806
+ old_break, old_next, old_redo, old_return, old_rescue, old_yield_fail
807
+ end
808
+
809
+ # Walks over a series of statements, ignoring the return value of
810
+ # everything except the last statement. Stores the result of the
811
+ # last statement in the result parameter.
812
+ def walk_body(body, opts={})
813
+ opts = {value: true}.merge(opts)
814
+ if opts[:value]
815
+ body[0..-2].each { |elt| walk_node(elt, value: false) }
816
+ if body.any?
817
+ walk_node(body.last, value: true)
818
+ else
819
+ const_instruct(nil)
820
+ end
821
+ else
822
+ body.each { |node| walk_node node, value: false }
823
+ end
824
+ end
825
+
826
+ def raise_instruct(arg, opts)
827
+ target = opts[:target]
828
+ call_instruct(ClassRegistry['Laser#Magic'].binding, :push_exception, arg, raise: false)
829
+ copy_instruct(@exception_register, arg)
830
+ uncond_instruct target, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
831
+ start_block target
832
+ end
833
+
834
+ def raise_instance_of_instruct(klass, *args)
835
+ opts = {target: current_rescue}
836
+ opts.merge!(args.pop) if Hash === args.last
837
+ instance = call_instruct(klass, :new, *args, value: true, raise: false)
838
+ raise_instruct instance, opts
839
+ end
840
+
841
+ # Yields with an explicit block being wrapped around the execution of the
842
+ # user's block. The basic block object created is provided as a parameter to the
843
+ # caller's operations which have the possibility of invoking the block.
844
+ def call_with_explicit_block(block_arg_bindings, block_sexp)
845
+ body_block = create_block
846
+ body_value, body_proc = create_block_temporary block_arg_bindings, block_sexp, @current_block
847
+ call_instruct(body_value, :lexical_self=, self_instruct, raise: false, value: false)
848
+ @graph.add_edge(@current_block, body_block,
849
+ ControlFlowGraph::EDGE_BLOCK_TAKEN | ControlFlowGraph::EDGE_ABNORMAL)
850
+ result = yield(body_value)
851
+ after = @current_block # new block caused by method call
852
+ block_exit = build_block_exit_block body_block, after
853
+ body_proc.exit_block = block_exit
854
+ walk_block_body block_arg_bindings, body_block, block_exit, block_sexp, after
855
+ start_block after
856
+ result
857
+ end
858
+
859
+ def build_block_exit_block(body_block, after)
860
+ build_block_with_jump(nil, body_block.name + '-Exit') do
861
+ @graph.add_edge(@current_block, body_block,
862
+ RGL::ControlFlowGraph::EDGE_ABNORMAL | RGL::ControlFlowGraph::EDGE_BLOCK_TAKEN)
863
+ @graph.add_edge(@current_block, after)
864
+ add_potential_raise_edge(false)
865
+ end
866
+ end
867
+
868
+ def call_zsuper_with_block(node, block_arg_bindings, block_sexp, opts={})
869
+ opts = {value: true, raise: true}.merge(opts)
870
+ call_with_explicit_block(block_arg_bindings, block_sexp) do |body_value|
871
+ invoke_super_with_block *compute_zsuper_arguments, body_value, opts
872
+ end
873
+ end
874
+
875
+ def call_method_with_block(receiver, method, args, block_arg_bindings, block_sexp, opts={})
876
+ opts = {value: true, raise: true}.merge(opts)
877
+ call_with_explicit_block(block_arg_bindings, block_sexp) do |body_value|
878
+ generic_call_instruct receiver, method, args, body_value, opts
879
+ end
880
+ end
881
+
882
+ def invoke_super_with_block(args, is_vararg, body_block, opts={})
883
+ opts = {value: true, raise: true}.merge(opts)
884
+ # TODO(adgar): blocks in args & style
885
+ if is_vararg
886
+ then super_vararg_instruct(args, {block: body_block}.merge(opts))
887
+ else super_instruct(*args, {block: body_block}.merge(opts))
888
+ end
889
+ end
890
+
891
+ # Walks the block with it's new next/etc. boundaries set based on the block's
892
+ # scope
893
+ def walk_block_body(block_arg_bindings, body_block, block_exit, body, after)
894
+ block_exception = create_temporary(body_block.name + '_final_exception')
895
+ rescue_block = build_block_with_jump(nil, body_block.name + '-Failure') do
896
+ copy_instruct(block_exception, @exception_register)
897
+ add_instruction(:raise, block_exception)
898
+ uncond_instruct block_exit, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL, jump_instruct: false
899
+ end
900
+ with_jump_targets(rescue: rescue_block) do
901
+ with_current_basic_block(body_block) do
902
+ body_result = nil
903
+ with_self(query_self) do
904
+ build_formal_args(block_arg_bindings)
905
+ body_result = walk_body body, value: true
906
+ end
907
+ add_instruction(:return, body_result)
908
+ uncond_instruct(block_exit, jump_instruct: false)
909
+ end
910
+ end
911
+ end
912
+
913
+ # Terminates the current block with a jump to the target block.
914
+ def uncond_instruct(target, opts = {})
915
+ opts = {jump_instruct: true, flags: RGL::ControlFlowGraph::EDGE_NORMAL}.merge(opts)
916
+ add_instruction(:jump, target.name) if opts[:jump_instruct]
917
+ @graph.add_edge(@current_block, target, opts[:flags])
918
+ start_block target
919
+ end
920
+
921
+ # Creates an unconditional branch from the current block, based on the given
922
+ # value, to either the true block or the false block.
923
+ def cond_instruct(val, true_block, false_block, opts = {branch_instruct: true})
924
+ if opts[:branch_instruct]
925
+ add_instruction(:branch, val, true_block.name, false_block.name)
926
+ end
927
+ @graph.add_edge(@current_block, true_block)
928
+ @graph.add_edge(@current_block, false_block)
929
+ end
930
+
931
+ # Performs a no-arg return.
932
+ def return0_instruct
933
+ return_uncond_jump_instruct(nil)
934
+ end
935
+
936
+ def return_instruct(node)
937
+ result = evaluate_args_into_array node[1]
938
+ return_uncond_jump_instruct result
939
+ end
940
+
941
+ def return_uncond_jump_instruct(result)
942
+ copy_instruct(@return_register, result)
943
+ uncond_instruct @current_return
944
+ start_block create_block
945
+ result
946
+ end
947
+
948
+ # Performs a yield of the given value, capturing the return
949
+ # value.
950
+ def yield_instruct(arg_node, opts={})
951
+ opts = {raise: true, value: true}.merge(opts)
952
+ # this is: if @block_arg; @block_arg.call(args)
953
+ # else raise LocalJumpError.new(...)
954
+ if_block, no_block = create_blocks 2
955
+ cond_instruct(@block_arg, if_block, no_block)
956
+
957
+ start_block no_block
958
+ message = const_instruct('no block given (yield)')
959
+ file_name = const_instruct(@current_node.file_name)
960
+ line_number = const_instruct(@current_node.line_number || 0)
961
+ raise_instance_of_instruct(
962
+ ClassRegistry['LocalJumpError'].binding, message, file_name, line_number,
963
+ target: current_yield_fail)
964
+
965
+ start_block if_block
966
+ if arg_node.nil?
967
+ result = call_instruct(@block_arg, :call, opts)
968
+ else
969
+ arg_node = arg_node[1] if arg_node[0] == :paren
970
+ result = generic_call_instruct(@block_arg, :call, arg_node[1], nil, opts)
971
+ end
972
+ result
973
+ end
974
+
975
+ def yield_instruct_with_arg(node, opts={})
976
+ opts = {raise: true, value: true}.merge(opts)
977
+ result = evaluate_args_into_array node[1]
978
+ yield_instruct result, opts
979
+ end
980
+
981
+ # Takes an argument node and evaluates it into an array. used by
982
+ # return and yield, as they always pass along 1 argument.
983
+ def evaluate_args_into_array(args)
984
+ if args[0] == :args_add_star
985
+ # if there's a splat, always return an actual array object of all the arguments.
986
+ compute_varargs(args)
987
+ elsif args[0] == :paren
988
+ evaluate_args_into_array(args[1])
989
+ elsif args[0] == :args_add_block
990
+ evaluate_args_into_array(args[1])
991
+ elsif args.size > 1
992
+ # if there's more than 1 argument, but no splats, then we just pack
993
+ # them into an array and return that array.
994
+ arg_temps = args.map { |arg| walk_node arg, value: true }
995
+ result = call_instruct(ClassRegistry['Array'].binding, :[], *arg_temps,
996
+ value: true, raise: false)
997
+ else
998
+ # Otherwise, just 1 simple argument: return it.
999
+ walk_node args[0], value: true
1000
+ end
1001
+ end
1002
+
1003
+ attr_reader :current_break, :current_next, :current_redo
1004
+ attr_reader :current_return, :current_rescue, :current_yield_fail
1005
+
1006
+ # TODO(adgar): ARGUMENTS
1007
+ def break_instruct(args)
1008
+ uncond_instruct @current_break
1009
+ start_block create_block
1010
+ end
1011
+
1012
+ # TODO(adgar): ARGUMENTS
1013
+ def next_instruct(args)
1014
+ uncond_instruct @current_next
1015
+ start_block create_block
1016
+ end
1017
+
1018
+ def redo_instruct
1019
+ add_fake_edge @current_block, @graph.exit
1020
+ uncond_instruct @current_redo
1021
+ start_block create_block
1022
+ add_fake_edge @graph.enter, @current_block
1023
+ end
1024
+
1025
+ # Walks a body statement.
1026
+ def bodystmt_walk(node)
1027
+ # Pretty fucking compact encapsulation of the :bodystmt block. Damn, mother
1028
+ # fucker.
1029
+ result = create_temporary
1030
+
1031
+ body, rescue_body, else_body, ensure_body = node.children
1032
+ body_block, after = create_blocks 2
1033
+ uncond_instruct body_block
1034
+
1035
+ if ensure_body
1036
+ ensure_block = create_block
1037
+
1038
+ # Generate the body with redirects to the ensure block, so no jumps get away without
1039
+ # running the ensure block
1040
+ with_jumps_redirected(break: ensure_body[1], redo: ensure_body[1], next: ensure_body[1],
1041
+ return: ensure_body[1], rescue: ensure_body[1],
1042
+ yield_fail: ensure_body[1]) do
1043
+ rescue_target, yield_fail_target =
1044
+ build_rescue_target(node, result, rescue_body, ensure_block,
1045
+ current_rescue, current_yield_fail)
1046
+ walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target)
1047
+ end
1048
+ uncond_instruct ensure_block
1049
+ walk_body(ensure_body[1], value: false)
1050
+ uncond_instruct after
1051
+ else
1052
+ # Generate the body with redirects to the ensure block, so no jumps get away without
1053
+ # running the ensure block
1054
+ rescue_target, yield_fail_target =
1055
+ build_rescue_target(node, result, rescue_body, after,
1056
+ current_rescue, current_yield_fail)
1057
+ walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target)
1058
+ uncond_instruct after
1059
+ end
1060
+ result
1061
+ end
1062
+
1063
+ # Builds the rescue block(s) for the given rescue_body, if there is one,
1064
+ # and returns the block to jump to when an exception is raised.
1065
+ def build_rescue_target(node, result, rescue_body, destination, rescue_fail, yield_fail_target)
1066
+ if rescue_body
1067
+ then rescue_instruct(node, result, rescue_body, destination, rescue_fail, yield_fail_target)
1068
+ else [rescue_fail, yield_fail_target]
1069
+ end
1070
+ end
1071
+
1072
+ # Walks the body of code with its result copied and its rescue target set.
1073
+ def walk_body_with_rescue_target(result, body, body_block, rescue_target, yield_fail_target)
1074
+ with_jump_targets(rescue: rescue_target, yield_fail: yield_fail_target) do
1075
+ start_block body_block
1076
+ body_result = walk_body body, value: true
1077
+ copy_instruct(result, body_result)
1078
+ end
1079
+ end
1080
+
1081
+ def rescue_instruct(node, enclosing_body_result, rescue_body, ensure_block, rescue_fail, yield_fail)
1082
+ rescue_target, yield_fail_target = create_blocks 2
1083
+ catchers = [rescue_target, yield_fail_target]
1084
+ start_block rescue_target
1085
+ while rescue_body
1086
+ rhs, exception_name, handler_body, rescue_body = rescue_body.children
1087
+ rhs ||= Sexp.new([[:var_ref, [:@const, 'StandardError', [0, 0]]]])
1088
+ handler_block = create_block
1089
+
1090
+ # for everything in rescue_body[1]
1091
+ # check if === $!, if so, go to handler_block, if not, keep checking.
1092
+ catchers.map! do |catcher|
1093
+ with_current_basic_block(catcher) do
1094
+ failure_block = nil
1095
+ foreach_on_rhs(rhs) do |temp|
1096
+ result = call_instruct(temp, :===, @exception_register, value: true)
1097
+ failure_block = create_block
1098
+ cond_instruct(result, handler_block, failure_block)
1099
+ start_block failure_block
1100
+ end
1101
+ @current_block
1102
+ end
1103
+ end
1104
+
1105
+ # Build the handler block.
1106
+ start_block handler_block
1107
+ # Assign to $! if there is a requested name for the exception
1108
+ if exception_name
1109
+ var_name = exception_name.expanded_identifier
1110
+ copy_instruct(current_scope.lookup_or_create_local(var_name), @exception_register)
1111
+ end
1112
+ body_result = walk_body handler_body, value: true
1113
+ copy_instruct(enclosing_body_result, body_result)
1114
+ call_instruct(ClassRegistry['Laser#Magic'].binding, :pop_exception, raise: false)
1115
+ uncond_instruct ensure_block
1116
+ end
1117
+ # All rescues failed.
1118
+ else_body = node[3]
1119
+ rescue_else_instruct(else_body, catchers, [rescue_fail, yield_fail]) # else_body
1120
+ [rescue_target, yield_fail_target]
1121
+ end
1122
+
1123
+ # Builds a rescue-else body.
1124
+ def rescue_else_instruct(else_body, catchers, fail_targets)
1125
+ catchers.zip(fail_targets).each do |catcher, fail_target|
1126
+ with_current_basic_block(catcher) do
1127
+ if else_body
1128
+ else_block = create_block
1129
+ uncond_instruct else_block
1130
+ start_block else_block
1131
+ walk_body else_body[1], value: false
1132
+ end
1133
+ uncond_instruct fail_target, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
1134
+ end
1135
+ end
1136
+ end
1137
+
1138
+ # Generates the code for the 'expr rescue expr' construct, which
1139
+ # catches StandardError exceptions. Offers a nice optimization opportunity
1140
+ # for yield-failure.
1141
+ def rescue_mod_instruct(guarded_expr, rescue_expr, opts={value: true})
1142
+ # first off, yield failure ALWAYS caught, so that's optimized
1143
+ # no ensure, else, etc
1144
+ # StandardError is the caught exception
1145
+ result_temporary = create_temporary if opts[:value]
1146
+ rescue_check_block, after = create_blocks 2
1147
+ is_caught_block = build_block_with_jump(after) do
1148
+ caught_result = walk_node(rescue_expr, opts)
1149
+ copy_instruct(result_temporary, caught_result) if opts[:value]
1150
+ end
1151
+ with_current_basic_block(rescue_check_block) do
1152
+ caught = call_instruct(ClassRegistry['StandardError'].binding, :===,
1153
+ @exception_register, value: true, raise: false)
1154
+ cond_instruct(caught, is_caught_block, current_rescue)
1155
+ end
1156
+ with_jump_targets(rescue: rescue_check_block, yield_fail: is_caught_block) do
1157
+ normal_result = walk_node(guarded_expr, opts)
1158
+ copy_instruct(result_temporary, normal_result) if opts[:value]
1159
+ uncond_instruct(after)
1160
+ end
1161
+ result_temporary
1162
+ end
1163
+
1164
+ def class_instruct(class_name, superclass, body, opts={value: true})
1165
+ # first: calculate receiver to perform a check if
1166
+ # the class already exists
1167
+ the_class_holder = create_temporary
1168
+ case class_name.type
1169
+ when :const_ref
1170
+ receiver_val = current_namespace
1171
+ name_as_string = class_name.expanded_identifier
1172
+ when :const_path_ref
1173
+ receiver_val = walk_node(class_name[1], value: true)
1174
+ name_as_string = class_name[2].expanded_identifier
1175
+ when :top_const_ref
1176
+ receiver_val = ClassRegistry['Object'].binding
1177
+ name_as_string = class_name[1].expanded_identifier
1178
+ end
1179
+ actual_name = const_instruct(name_as_string)
1180
+
1181
+ if superclass
1182
+ superclass_val = walk_node(superclass, value: true)
1183
+ need_confirm_superclass = true
1184
+ else
1185
+ superclass_val = lookup_or_create_temporary(:var, '::Object')
1186
+ copy_instruct(superclass_val, ClassRegistry['Object'])
1187
+ need_confirm_superclass = false
1188
+ end
1189
+
1190
+ already_exists = call_instruct(receiver_val, :const_defined?, actual_name, const_instruct(false), value: true, raise: false)
1191
+ if_exists_block, if_noexists_block, after_exists_check = create_blocks 3
1192
+ cond_instruct(already_exists, if_exists_block, if_noexists_block)
1193
+
1194
+ ############### LOOKING UP AND VERIFYING CLASS BRANCH ###########
1195
+
1196
+ start_block if_exists_block
1197
+ the_class = call_instruct(receiver_val, :const_get, actual_name, const_instruct(false), value: true, raise: false)
1198
+ copy_instruct(the_class_holder, the_class)
1199
+ # check if it's actually a module
1200
+ is_module_block, after_conflict_check = create_blocks 2
1201
+ is_class_cond_val = call_instruct(ClassRegistry['Class'].binding, :===, the_class, value: true, raise: false)
1202
+ cond_instruct(is_class_cond_val, after_conflict_check, is_module_block)
1203
+
1204
+ # Unconditionally raise if it is not a class! The error is a TypeError
1205
+ start_block is_module_block
1206
+ raise_instance_of_instruct(ClassRegistry['LaserReopenedModuleAsClassError'].binding,
1207
+ const_instruct("#{name_as_string} is not a class"))
1208
+
1209
+ start_block after_conflict_check
1210
+ # Now, compare superclasses if provided superclass is not Object
1211
+ if need_confirm_superclass
1212
+ should_not_confirm_superclass = call_instruct(superclass_val, :equal?,
1213
+ ClassRegistry['Object'].binding, value: true, raise: false)
1214
+ validate_superclass_mismatch_block = create_block
1215
+ cond_instruct(should_not_confirm_superclass, after_exists_check, validate_superclass_mismatch_block)
1216
+
1217
+ start_block validate_superclass_mismatch_block
1218
+ old_superclass_val = call_instruct(the_class, :superclass, value: true, raise: false)
1219
+ superclass_is_equal_cond = call_instruct(old_superclass_val, :equal?, superclass_val, value: true, raise: false)
1220
+ superclass_conflict_block = create_block
1221
+ cond_instruct(superclass_is_equal_cond, after_exists_check, superclass_conflict_block)
1222
+
1223
+ start_block superclass_conflict_block
1224
+ raise_instance_of_instruct(ClassRegistry['LaserSuperclassMismatchError'].binding,
1225
+ const_instruct("superclass mismatch for class #{name_as_string}"))
1226
+ else
1227
+ uncond_instruct(after_exists_check)
1228
+ end
1229
+
1230
+ ############### CREATING CLASS BRANCH ###############################
1231
+
1232
+ start_block if_noexists_block
1233
+ # only confirm superclass if it's not defaulting to Object!
1234
+ if need_confirm_superclass
1235
+ is_not_class_block, after_is_class_check = create_blocks 2
1236
+ is_class_cond_val = call_instruct(ClassRegistry['Class'].binding, :===, superclass_val, value: true, raise: false)
1237
+ cond_instruct(is_class_cond_val, after_is_class_check, is_not_class_block)
1238
+
1239
+ start_block is_not_class_block
1240
+ raise_instance_of_instruct ClassRegistry['TypeError'].binding
1241
+
1242
+ start_block after_is_class_check
1243
+ end
1244
+ # create the class and assign
1245
+ the_class = call_instruct(ClassRegistry['Class'].binding, :new, superclass_val, value: true, raise: false)
1246
+ call_instruct(receiver_val, :const_set, actual_name, the_class, value: false, raise: false)
1247
+ copy_instruct(the_class_holder, the_class)
1248
+ uncond_instruct after_exists_check
1249
+
1250
+ start_block after_exists_check
1251
+ call_instruct(Bootstrap::VISIBILITY_STACK, :push, const_instruct(:public), raise: false, value: false)
1252
+ # use this namespace!
1253
+ module_eval_instruct(the_class_holder, body, opts)
1254
+ call_instruct(Bootstrap::VISIBILITY_STACK, :pop, raise: false, value: false)
1255
+ end
1256
+
1257
+ def module_instruct(module_name, body, opts={value: true})
1258
+ # first: calculate receiver to perform a check if
1259
+ # the class already exists
1260
+ the_module_holder = create_temporary
1261
+ case module_name.type
1262
+ when :const_ref
1263
+ receiver_val = current_namespace
1264
+ name_as_string = module_name.expanded_identifier
1265
+ when :const_path_ref
1266
+ receiver_val = walk_node(module_name[1], value: true)
1267
+ name_as_string = module_name[2].expanded_identifier
1268
+ when :top_const_ref
1269
+ receiver_val = ClassRegistry['Object'].binding
1270
+ name_as_string = module_name[1].expanded_identifier
1271
+ end
1272
+ actual_name = const_instruct(name_as_string)
1273
+
1274
+ already_exists = call_instruct(receiver_val, :const_defined?, actual_name, const_instruct(false), value: true, raise: false)
1275
+ if_exists_block, if_noexists_block, after_exists_check = create_blocks 3
1276
+ cond_instruct(already_exists, if_exists_block, if_noexists_block)
1277
+
1278
+ start_block if_exists_block
1279
+ the_module = call_instruct(receiver_val, :const_get, actual_name, const_instruct(false), value: true, raise: false)
1280
+ copy_instruct(the_module_holder, the_module)
1281
+ # check if it's actually a class
1282
+ is_class_block, after_conflict_check = create_blocks 2
1283
+ is_class_cond_val = call_instruct(ClassRegistry['Class'].binding, :===, the_module, value: true, raise: false)
1284
+ cond_instruct(is_class_cond_val, is_class_block, after_exists_check)
1285
+
1286
+ # Unconditionally raise if it is a class! The error is a TypeError
1287
+ start_block is_class_block
1288
+ raise_instance_of_instruct(ClassRegistry['LaserReopenedClassAsModuleError'].binding,
1289
+ const_instruct("#{name_as_string} is not a module"))
1290
+
1291
+ start_block if_noexists_block
1292
+ # create the class and assign
1293
+ the_module = call_instruct(ClassRegistry['Module'].binding, :new, value: true, raise: false)
1294
+ call_instruct(receiver_val, :const_set, actual_name, the_module, value: false, raise: false)
1295
+ copy_instruct(the_module_holder, the_module)
1296
+ uncond_instruct after_exists_check
1297
+
1298
+ start_block after_exists_check
1299
+
1300
+ call_instruct(Bootstrap::VISIBILITY_STACK, :push, const_instruct(:public), raise: false, value: false)
1301
+ module_eval_instruct(the_module_holder, body, opts)
1302
+ call_instruct(Bootstrap::VISIBILITY_STACK, :pop, raise: false, value: false)
1303
+ end
1304
+
1305
+ def singleton_class_instruct(receiver, body, opts={value: false})
1306
+ receiver_val = walk_node receiver, value: true
1307
+
1308
+ maybe_symbol, no_singleton, has_singleton = create_blocks 3
1309
+ cond_result = call_instruct(ClassRegistry['Fixnum'].binding, :===, receiver_val, value: true)
1310
+ cond_instruct(cond_result, no_singleton, maybe_symbol)
1311
+
1312
+ start_block maybe_symbol
1313
+ cond_result = call_instruct(ClassRegistry['Symbol'].binding, :===, receiver_val, value: true)
1314
+ cond_instruct(cond_result, no_singleton, has_singleton)
1315
+
1316
+ start_block no_singleton
1317
+ raise_instance_of_instruct ClassRegistry['TypeError'].binding
1318
+
1319
+ start_block has_singleton
1320
+ singleton = call_instruct(receiver_val, :singleton_class, value: true, raise: false)
1321
+ module_eval_instruct(singleton, body, opts)
1322
+ end
1323
+
1324
+ def module_eval_instruct(new_self, body, opts)
1325
+ with_scope(ClosedScope.new(current_scope, new_self)) do
1326
+ with_self new_self do
1327
+ walk_node body, opts
1328
+ end
1329
+ end
1330
+ end
1331
+
1332
+ # Defines a method on the current lexically-enclosing class/module.
1333
+ def def_instruct(receiver, name, args, body, opts = {})
1334
+ opts = {value: false}.merge(opts)
1335
+ body.scope = current_scope
1336
+ block, new_proc = create_block_temporary(args, body)
1337
+ new_proc.annotations = body.parent.comment.annotation_map if body.parent.comment
1338
+ call_instruct(receiver, 'define_method', name, block, raise: false)
1339
+ const_instruct(nil) if opts[:value]
1340
+ end
1341
+
1342
+ # Creates a temporary, assigns it a constant value, and returns it.
1343
+ def const_instruct(val)
1344
+ result = lookup_or_create_temporary(:const, val)
1345
+ copy_instruct result, val
1346
+ result
1347
+ end
1348
+
1349
+ def self_instruct
1350
+ @self_register
1351
+ end
1352
+
1353
+ # Copies one register to another.
1354
+ def copy_instruct(lhs, rhs)
1355
+ add_instruction(:assign, lhs, rhs)
1356
+ end
1357
+
1358
+ def evaluate_if_needed(node, opts={})
1359
+ if Bindings::Base === node
1360
+ node
1361
+ else
1362
+ walk_node(node, opts)
1363
+ end
1364
+ end
1365
+
1366
+ # Does a single assignment between an LHS node and an RHS node, both unevaluated.
1367
+ # This can be used by massign calls! In fact, it's very important to structure
1368
+ # this code to handle such cases.
1369
+ def single_assign_instruct(lhs, rhs, opts={})
1370
+ opts = {value: true}.merge(opts)
1371
+ case lhs.type
1372
+ when :field
1373
+ # In 1.9.2, receiver is evaulated first, then the arguments
1374
+ receiver = walk_node lhs[1], value: true
1375
+ method_name = lhs[3].expanded_identifier
1376
+ rhs_val = evaluate_if_needed(rhs, value: true)
1377
+ call_instruct(receiver, "#{method_name}=".to_sym, rhs_val, {block: false}.merge(opts))
1378
+ rhs_val
1379
+ when :aref_field
1380
+ generic_aref_instruct(walk_node(lhs[1], value: true), lhs[2][1], rhs, opts)
1381
+ when :const_path_field
1382
+ receiver, const = lhs.children
1383
+ receiver_val = walk_node(receiver, value: true)
1384
+ const_name_val = const_instruct(const.expanded_identifier)
1385
+ rhs_val = evaluate_if_needed(rhs, value: true)
1386
+ # never raises!
1387
+ call_instruct(receiver_val, :const_set, const_name_val, rhs_val, value: false, raise: false)
1388
+ rhs_val
1389
+ when :mlhs_paren
1390
+ # rhs may or may not be evaluated, and we're okay with that
1391
+ multiple_assign_instruct(lhs[1], rhs, opts)
1392
+ else
1393
+ if Bindings::Base === rhs
1394
+ rhs_val = rhs
1395
+ elsif rhs.type == :mrhs_new_from_args || rhs.type == :args_add_star ||
1396
+ rhs.type == :mrhs_add_star
1397
+ fixed, varying = compute_fixed_and_varying_rhs(rhs)
1398
+ rhs_val = combine_fixed_and_varying(fixed, varying)
1399
+ else
1400
+ rhs_val = walk_node rhs, value: true
1401
+ end
1402
+
1403
+ var_name = lhs.expanded_identifier
1404
+ if lhs.type == :@ident || lhs[1].type == :@ident
1405
+ lhs.binding = current_scope.lookup_or_create_local(var_name)
1406
+ copy_instruct lhs.binding, rhs_val
1407
+ elsif lhs[1].type == :@const
1408
+ call_instruct(current_namespace, :const_set, const_instruct(var_name), rhs_val, value: false, raise: false)
1409
+ elsif lhs[1].type == :@ivar
1410
+ call_instruct(current_self, :instance_variable_set, const_instruct(var_name),
1411
+ rhs_val, value: true, ignore_privacy: true)
1412
+ elsif lhs[1].type == :@gvar
1413
+ call_instruct(ClassRegistry['Laser#Magic'].binding, :set_global,
1414
+ const_instruct(var_name), rhs_val, raise: false, value: true)
1415
+ end
1416
+ rhs_val
1417
+ end
1418
+ end
1419
+
1420
+ def wasted_val(val)
1421
+ val.add_error(DiscardedRHSError.new("RHS value being discarded", val))
1422
+ end
1423
+
1424
+ def wasted_var(var)
1425
+ if var.type == :field || var.type == :aref_field || var.type == :mlhs_paren
1426
+ var.add_error(UnassignedLHSError.new("LHS never assigned - defaults to nil", var))
1427
+ else
1428
+ var.add_error(UnassignedLHSError.new("LHS (#{var.expanded_identifier}) never assigned - defaults to nil", var))
1429
+ end
1430
+ end
1431
+
1432
+ def wasted_lhs_splat(var, attachment_node)
1433
+ if var.nil?
1434
+ attachment_node.add_error(UnassignedLHSError.new("Unnamed LHS splat is always empty ([]) - not useful", attachment_node))
1435
+ elsif var.type == :field || var.type == :aref_field || var.type == :mlhs_paren
1436
+ var.add_error(UnassignedLHSError.new("LHS splat is always empty ([]) - not useful", var))
1437
+ else
1438
+ var.add_error(UnassignedLHSError.new("LHS splat (#{var.expanded_identifier}) is always empty ([]) - not useful", var))
1439
+ end
1440
+ end
1441
+
1442
+ def wasted_rhs_splat(val)
1443
+ val.add_error(DiscardedRHSError.new("RHS splat expanded and then discarded", val))
1444
+ end
1445
+
1446
+ def issue_wasted_val_warnings(list)
1447
+ list.each { |val| wasted_val(val) }
1448
+ end
1449
+
1450
+ def issue_wasted_var_warnings(list)
1451
+ list.each { |val| wasted_var(val) }
1452
+ end
1453
+
1454
+ # Expands a multiple-assignment as optimally as possible. Without any
1455
+ # splats, will expand to sequential assignments.
1456
+ def multiple_assign_instruct(lhs, rhs, opts={})
1457
+ rhs_has_star = rhs.find_type(:mrhs_add_star) || rhs.find_type(:args_add_star)
1458
+ # a, b = c, d, e
1459
+ if lhs.type != :mlhs_add_star && !rhs_has_star
1460
+ # a, b = c, d, e
1461
+ # tries to generate maximally efficient code: computes
1462
+ # precise assignments to improve analysis.
1463
+ if Sexp === lhs[0] && rhs.type == :mrhs_new_from_args
1464
+ rhs = rhs[1] + [rhs[2]] # i assume some parser silliness does this
1465
+ # issue warnings
1466
+ if lhs.size < rhs.size
1467
+ issue_wasted_val_warnings(rhs[lhs.size..-1])
1468
+ elsif lhs.size > rhs.size
1469
+ issue_wasted_var_warnings(lhs[rhs.size..-1])
1470
+ end
1471
+ # pair them up. Enumerable#zip in 1.9.2 doesn't support unmatched lengths
1472
+
1473
+ pairs = (0...[lhs.size, rhs.size].max).map do |idx|
1474
+ need_temp = opts[:value] || (lhs[idx] && rhs[idx])
1475
+ { lhs: lhs[idx], rhs: rhs[idx], need_temp: need_temp }
1476
+ end
1477
+ # compute the rhs node
1478
+ pairs_with_vals = pairs.map do |hash|
1479
+ # only walk rhs if it exists
1480
+ result = hash[:rhs] && walk_node(hash[:rhs], value: hash[:need_temp])
1481
+ hash.merge(value: result)
1482
+ end
1483
+ # perform necessary assignments
1484
+ pairs_with_vals.each do |hash|
1485
+ if hash[:lhs]
1486
+ hash[:value] ||= const_instruct(nil)
1487
+ new_value = hash[:value]
1488
+ single_assign_instruct(hash[:lhs], new_value, value: false)
1489
+ end
1490
+ end
1491
+ # if we need the value, we need to return all the RHS in an array.
1492
+ if opts[:value]
1493
+ result_temps = pairs_with_vals.map { |hash| hash[:value] }
1494
+ call_instruct(ClassRegistry['Array'].binding, :[], *result_temps,
1495
+ value: true, raise: false)
1496
+ end
1497
+ # a, b, c = foo
1498
+ # no star on RHS means implicit conversion: to_ary.
1499
+ elsif Sexp === lhs[0] && rhs.type != :mrhs_new_from_args
1500
+ rhs_val = walk_node(rhs, value: true)
1501
+ rhs_array = rb_ary_to_ary(rhs_val)
1502
+ declare_instruct(:expect_tuple_size, :==, lhs.size, rhs_array)
1503
+ lhs.each_with_index do |node, idx|
1504
+ assigned_val = call_instruct(rhs_array, :[], const_instruct(idx),
1505
+ value: true, raise: false)
1506
+ single_assign_instruct(node, assigned_val)
1507
+ end
1508
+ rhs_array
1509
+ else
1510
+ raise ArgumentError.new("Unexpected non-starred massign node:\n" +
1511
+ " lhs = #{lhs.inspect}\n rhs = #{rhs.inspect}")
1512
+ end
1513
+ # a, *b, c = [1, 2, 3]
1514
+ # implicit #to_ary
1515
+ elsif lhs.type == :mlhs_add_star && !rhs_has_star && rhs.type != :mrhs_new_from_args
1516
+ # calculate RHS: array of unknown length
1517
+ rhs_val = walk_node(rhs, value: true)
1518
+ rhs_array = rb_ary_to_ary(rhs_val)
1519
+ assign_splat_mlhs_to_varying(lhs[1], lhs[2], lhs[3] || [], rhs_array)
1520
+ # a, *b, c = 1, 2, 3, 4, 5
1521
+ # also easy/precise
1522
+ elsif lhs.type == :mlhs_add_star && !rhs_has_star
1523
+ # RHS = :mrhs_new_from_args
1524
+ # single_val not handled yet.
1525
+ rhs_arr = rhs[1] + [rhs[2]]
1526
+
1527
+ # Calculate pre-star, star, and post-star bindings and boundaries
1528
+ pre_star = lhs[1] # [], if empty
1529
+ star_node = lhs[2] # could be nil
1530
+ post_star = lhs[3] || []
1531
+
1532
+ star_start = pre_star.size
1533
+ star_end = [star_start, rhs_arr.size - post_star.size].max
1534
+ star_range = star_start...star_end
1535
+
1536
+ if star_range.entries.empty?
1537
+ wasted_lhs_splat(star_node, lhs)
1538
+ end
1539
+
1540
+ # all RHS are ALWAYS consumed. But if the star is unnamed, star values can be
1541
+ # discarded.
1542
+ rhs_vals = rhs_arr.map.with_index do |node, idx|
1543
+ if !opts[:value] && star_node.nil? && star_range.include?(idx)
1544
+ walk_node(node, value: false)
1545
+ nil
1546
+ else
1547
+ walk_node(node, value: true)
1548
+ end
1549
+ end
1550
+
1551
+ # do pre-star nodes
1552
+ pre_star.each_with_index do |lhs_node, idx|
1553
+ single_assign_instruct(lhs_node, rhs_vals[idx] || const_instruct(nil))
1554
+ end
1555
+ # do star assignment if star_node != nil
1556
+ if star_node
1557
+ star_parts = rhs_vals[star_range]
1558
+ star_arr = call_instruct(ClassRegistry['Array'].binding, :[], *star_parts,
1559
+ value: true, raise: false)
1560
+ single_assign_instruct star_node, star_arr
1561
+ end
1562
+ # do post-star nodes
1563
+ post_star.each_with_index do |lhs_node, idx|
1564
+ single_assign_instruct(lhs_node, rhs_vals[star_end + idx] || const_instruct(nil))
1565
+ end
1566
+ if opts[:value]
1567
+ call_instruct(ClassRegistry['Array'].binding, :[], *rhs_vals,
1568
+ value: true, raise: false)
1569
+ end
1570
+ # a, b, c = 1, *foo
1571
+ elsif lhs.type != :mlhs_add_star && rhs_has_star
1572
+ # for building the final array
1573
+ lhs_size = lhs.size
1574
+ fixed, varying = compute_fixed_and_varying_rhs(rhs)
1575
+ fixed_size = fixed.size
1576
+ if fixed_size >= lhs_size
1577
+ wasted_rhs_splat(rhs)
1578
+ else
1579
+ declare_instruct(:expect_tuple_size, :==, lhs_size - fixed_size, varying)
1580
+ end
1581
+ fixed[0...lhs_size].each_with_index do |val, idx|
1582
+ single_assign_instruct(lhs[idx], val)
1583
+ end
1584
+ fixed_size.upto(lhs_size - 1) do |idx|
1585
+ looked_up = call_instruct(varying, :[], const_instruct(idx - fixed_size), value: true, raise: false)
1586
+ single_assign_instruct(lhs[idx], looked_up)
1587
+ end
1588
+ if fixed.empty?
1589
+ result = varying
1590
+ else
1591
+ fixed_as_arr = call_instruct(ClassRegistry['Array'].binding, :[], *fixed, value: true, raise: false)
1592
+ result = call_instruct(fixed_as_arr, :+, varying, value: true, raise: false)
1593
+ end
1594
+ result
1595
+ # a, *b, c = d, *e
1596
+ elsif lhs.type == :mlhs_add_star && rhs_has_star
1597
+ # Calculate pre-star, star, and post-star bindings and boundaries
1598
+ fixed, varying = compute_fixed_and_varying_rhs(rhs)
1599
+ # inefficient: ignore fixed stuff
1600
+ # TODO(adgar): optimize this
1601
+ rhs_array = combine_fixed_and_varying(fixed, varying)
1602
+ assign_splat_mlhs_to_varying(lhs[1], lhs[2], lhs[3] || [], rhs_array)
1603
+ else
1604
+ raise ArgumentError.new("Unexpected :massign node:\n " +
1605
+ "lhs = #{lhs.inspect}\n rhs = #{rhs.inspect}")
1606
+ end
1607
+ end
1608
+
1609
+ def assign_splat_mlhs_to_varying(pre_star, star_node, post_star, rhs_array)
1610
+ # pre-star is easy: how they are extracted is deterministic
1611
+ pre_star.each_with_index do |lhs_node, idx|
1612
+ assigned_val = call_instruct(rhs_array, :[], const_instruct(idx),
1613
+ value: true, raise: false)
1614
+ single_assign_instruct(lhs_node, assigned_val)
1615
+ end
1616
+
1617
+ # next, extract star_node. run-time version of below
1618
+ star_start = const_instruct(pre_star.size)
1619
+ fixed_size = const_instruct(pre_star.size + post_star.size)
1620
+ declare_instruct(:expect_tuple_size, :>, pre_star.size + post_star.size, rhs_array)
1621
+ # calculate star_end at runtime without loops
1622
+ rhs_arr_size = call_instruct(rhs_array, :size,
1623
+ value: true, raise: false)
1624
+ star_size = call_instruct(rhs_arr_size, :-, fixed_size,
1625
+ value: true, raise: false)
1626
+
1627
+ after = create_block
1628
+
1629
+ if_nonempty = build_block_with_jump(after) do
1630
+ if star_node
1631
+ star_subarray = call_instruct(rhs_array, :[], star_start, star_size,
1632
+ value: true, raise: false)
1633
+ single_assign_instruct(star_node, star_subarray)
1634
+ end
1635
+ if post_star.any?
1636
+ post_star_start = call_instruct(star_start, :+, star_size,
1637
+ value: true, raise: false)
1638
+ post_star_array = call_instruct(rhs_array, :[], post_star_start,
1639
+ const_instruct(post_star.size), value: true, raise: false)
1640
+ post_star.each_with_index do |lhs_node, idx|
1641
+ assigned_val = call_instruct(post_star_array, :[], const_instruct(idx),
1642
+ value: true, raise: false)
1643
+ single_assign_instruct(lhs_node, assigned_val)
1644
+ end
1645
+ end
1646
+ end
1647
+
1648
+ if_empty = build_block_with_jump(after) do
1649
+ single_assign_instruct(star_node, const_instruct([])) if star_node
1650
+ post_star.each_with_index do |lhs_node, idx|
1651
+ assigned_val = call_instruct(rhs_array, :[], const_instruct(pre_star.size + idx),
1652
+ value: true, raise: false)
1653
+ single_assign_instruct(lhs_node, assigned_val)
1654
+ end
1655
+ end
1656
+
1657
+ cond_value = call_instruct(star_size, :>, const_instruct(0),
1658
+ value: true, raise: false)
1659
+ cond_instruct(cond_value, if_nonempty, if_empty)
1660
+
1661
+ start_block after
1662
+ rhs_array
1663
+ end
1664
+
1665
+ # Computes the fixed portion of the RHS, and the varying portion of the
1666
+ # RHS. The fixed portion will be an array of temporaries, the varying
1667
+ # portion will be an array temporary.
1668
+ def compute_fixed_and_varying_rhs(node)
1669
+ case node[0]
1670
+ when :mrhs_add_star, :args_add_star
1671
+ pre_star, star = node[1], node[2]
1672
+ post_star = node[3..-1]
1673
+ # pre_star could have more stars!
1674
+ fixed, varying = compute_fixed_and_varying_rhs(pre_star)
1675
+ # varying had better be []
1676
+ star_pre_conv = walk_node(star, value: true)
1677
+ star_vals = rb_check_convert_type(star_pre_conv, ClassRegistry['Array'].binding, :to_a)
1678
+ # if we had varying parts, then we append the star, otherwise, the star IS
1679
+ # the varying part
1680
+ if varying
1681
+ varying = call_instruct(varying, :+, star_vals, value: true, raise: false)
1682
+ else
1683
+ varying = star_vals
1684
+ end
1685
+ # if we have a post-star section, append to varying
1686
+ if post_star && !post_star.empty?
1687
+ post_star_vals = build_array_instruct(post_star)
1688
+ varying = call_instruct(varying, :+, post_star_vals, value: true, raise: false)
1689
+ end
1690
+ when :mrhs_new_from_args
1691
+ # random spare node when mrhs is just used for fixed rhs
1692
+ fixed, varying = compute_fixed_and_varying_rhs(node[1])
1693
+ if node[2]
1694
+ if varying.nil?
1695
+ then fixed << walk_node(node[2], value: true)
1696
+ else varying = call_instruct(varying, :+, build_array_instruct([node[2]]), value: true, raise: false)
1697
+ end
1698
+ end
1699
+ when Sexp, NilClass
1700
+ fixed = node.map { |val| walk_node(val, value: true) }
1701
+ varying = nil
1702
+ end
1703
+ [fixed, varying]
1704
+ end
1705
+
1706
+ # Combines an array of fixed temporaries and an array temporary that contains
1707
+ # an unknown number of temporaries.
1708
+ def combine_fixed_and_varying(fixed, varying)
1709
+ if fixed.empty?
1710
+ varying || const_instruct([])
1711
+ else
1712
+ fixed_ary = call_instruct(ClassRegistry['Array'].binding, :[], *fixed, value: true, raise: false)
1713
+ if varying
1714
+ call_instruct(fixed_ary, :+, varying, value: true, raise: false)
1715
+ else
1716
+ fixed_ary
1717
+ end
1718
+ end
1719
+ end
1720
+
1721
+ def foreach_on_rhs(node, &blk)
1722
+ case node[0]
1723
+ when :mrhs_add_star, :args_add_star
1724
+ foreach_on_rhs(node[1], &blk)
1725
+ array_to_iterate = walk_node node[2], value: true
1726
+ counter = lookup_or_create_temporary(:rescue_iterator)
1727
+ copy_instruct(counter, 0)
1728
+ max = call_instruct(array_to_iterate, :size, value: true, raise: false)
1729
+
1730
+ loop_start_block, after = create_blocks 2
1731
+
1732
+ uncond_instruct loop_start_block
1733
+ cond_result = call_instruct(counter, :<, max, value: true, raise: false)
1734
+
1735
+ check_block = build_block_with_jump(loop_start_block) do
1736
+ current_val = call_instruct(array_to_iterate, :[], counter, value: true, raise: false)
1737
+ yield current_val
1738
+ next_counter = call_instruct(counter, :+, 1, value: true, raise: false)
1739
+ copy_instruct(counter, next_counter)
1740
+ end
1741
+
1742
+ cond_instruct(cond_result, check_block, after)
1743
+ start_block after
1744
+ when :mrhs_new_from_args
1745
+ foreach_on_rhs(node[1], &blk)
1746
+ yield walk_node(node[2], value: true) if node[2]
1747
+ when Sexp
1748
+ node.each { |val_node| yield walk_node(val_node, value: true) }
1749
+ end
1750
+ end
1751
+
1752
+ # Implicit conversion protocol to an array.
1753
+ def rb_ary_to_ary(value)
1754
+ result = lookup_or_create_temporary(:ary_to_ary, value)
1755
+ try_conv = rb_check_convert_type(value, ClassRegistry['Array'].binding, :to_ary)
1756
+
1757
+ after = create_block
1758
+ if_conv_succ = build_block_with_jump(after) do
1759
+ copy_instruct(result, try_conv)
1760
+ end
1761
+ if_conv_fail = build_block_with_jump(after) do
1762
+ new_result = call_instruct(ClassRegistry['Array'].binding, :[], value,
1763
+ value: true, raise: false)
1764
+ copy_instruct(result, new_result)
1765
+ end
1766
+
1767
+ cond_instruct(try_conv, if_conv_succ, if_conv_fail)
1768
+
1769
+ start_block after
1770
+ result
1771
+ end
1772
+
1773
+ #TODO(adgar): RAISES HERE!
1774
+ def rb_check_convert_type(value, klass, method)
1775
+ result = lookup_or_create_temporary(:convert, value, klass, method)
1776
+ after = create_block
1777
+
1778
+ comparison_result = call_instruct(klass, :===, value, value: true)
1779
+
1780
+ if_not_klass_block = build_block_with_jump do
1781
+ # TODO(adgar): if method does not exist, return nil.
1782
+ has_method = call_instruct(ClassRegistry['Laser#Magic'].binding, :responds?,
1783
+ value, const_instruct(method), value: true, raise: false)
1784
+
1785
+ if_has_method_block = build_block_with_jump(after) do
1786
+ conversion_result = call_instruct(value, method, value: true)
1787
+ copy_instruct result, conversion_result
1788
+ end
1789
+ if_no_method_block = build_block_with_jump(after) do
1790
+ copy_instruct result, const_instruct(nil)
1791
+ declare_instruct(:alias, result, value)
1792
+ end
1793
+ cond_instruct(has_method, if_has_method_block, if_no_method_block)
1794
+ end
1795
+ if_klass_block = build_block_with_jump(after) do
1796
+ copy_instruct(result, value)
1797
+ end
1798
+
1799
+ cond_instruct(comparison_result, if_klass_block, if_not_klass_block)
1800
+
1801
+ start_block after
1802
+ result
1803
+ end
1804
+
1805
+ def explicit_block_arg(method_call)
1806
+ if (to_proc = method_call.arguments.block_arg)
1807
+ to_proc_val = walk_node(to_proc, value: true)
1808
+ rb_check_convert_type(to_proc_val, ClassRegistry['Proc'].binding, :to_proc)
1809
+ end
1810
+ end
1811
+
1812
+ def issue_call(node, opts={})
1813
+ opts = {value: true}.merge(opts)
1814
+ method_call = node.method_call
1815
+ opts = opts.merge(ignore_privacy: true) if method_call.implicit_receiver?
1816
+ receiver = receiver_instruct node
1817
+ block = explicit_block_arg(method_call)
1818
+ generic_call_instruct(receiver, method_call.method_name,
1819
+ method_call.arg_node, block, opts)
1820
+ end
1821
+
1822
+ def issue_super_call(node, opts={})
1823
+ method_call = node.method_call
1824
+ opts = opts.merge(ignore_privacy: true)
1825
+ block = explicit_block_arg(method_call)
1826
+ generic_super_instruct(method_call.arg_node, block, opts)
1827
+ end
1828
+
1829
+ def receiver_instruct(node)
1830
+ method_call = node.method_call
1831
+ if method_call.receiver_node
1832
+ then walk_node method_call.receiver_node, value: true
1833
+ else self_instruct
1834
+ end
1835
+ end
1836
+
1837
+ # Given a receiver, a method, a method_add_arg node, and a block value,
1838
+ # issue a call instruction. This will involve computing the arguments,
1839
+ # potentially issuing a vararg call (if splats are used). The return
1840
+ # value is captured and returned to the caller of this method.
1841
+ def generic_call_instruct(receiver, method, args, block, opts={})
1842
+ opts = {value: true}.merge(opts)
1843
+ args = [] if args.nil?
1844
+ args = args[1] if args[0] == :args_add_block
1845
+ if args[0] == :args_add_star
1846
+ arg_array = compute_varargs(args)
1847
+ call_vararg_instruct(receiver, method, arg_array, {block: block}.merge(opts))
1848
+ else
1849
+ arg_temps = args.map { |arg| walk_node(arg, value: true) }
1850
+ call_instruct(receiver, method, *arg_temps, {block: block}.merge(opts))
1851
+ end
1852
+ end
1853
+
1854
+ # Given a receiver, a method, a method_add_arg node, and a block value,
1855
+ # issue a super instruction. This will involve computing the arguments,
1856
+ # potentially issuing a vararg super (if splats are used). The return
1857
+ # value is captured and returned to the superer of this method.
1858
+ def generic_super_instruct(args, block, opts={})
1859
+ opts = {value: true, raise: true}.merge(opts)
1860
+ args = args[1] if args[0] == :args_add_block
1861
+ if args[0] == :args_add_star
1862
+ arg_array = compute_varargs(args)
1863
+ super_vararg_instruct(arg_array, {block: block}.merge(opts))
1864
+ else
1865
+ arg_temps = args.map { |arg| walk_node(arg, value: true) }
1866
+ super_instruct(*arg_temps, {block: block}.merge(opts))
1867
+ end
1868
+ end
1869
+
1870
+ # Given a receiver, a method, a method_add_arg node, and a block value,
1871
+ # issue a call instruction. This will involve computing the arguments,
1872
+ # potentially issuing a vararg call (if splats are used). The return
1873
+ # value is captured and returned to the caller of this method.
1874
+ def generic_aref_instruct(receiver, args, val, opts={})
1875
+ opts = {value: true, raise: true}.merge(opts)
1876
+ args = [] if args.nil?
1877
+ args = args[1] if args[0] == :args_add_block
1878
+ if args[0] == :args_add_star
1879
+ arg_array = compute_varargs(args)
1880
+ call_instruct(arg_array, :<<, walk_node(val, value: true), value: false)
1881
+ call_vararg_instruct(receiver, :[]=, arg_array, false, opts)
1882
+ else
1883
+ arg_temps = (args + [val]).map { |arg| evaluate_if_needed(arg, value: true) }
1884
+ call_instruct(receiver, :[]=, *arg_temps, {block: false}.merge(opts))
1885
+ end
1886
+ end
1887
+
1888
+ # Computes the arguments to a zsuper call at the given node. Also returns
1889
+ # whether the resulting argument expansion is of variable arity.
1890
+ # This is different from normal splatting because we are computing based
1891
+ # upon the argument list of the method, not a normal arg_ node.
1892
+ #
1893
+ # returns: (Bindings::Base | [Bindings::Base], Boolean)
1894
+ def compute_zsuper_arguments
1895
+ args_to_walk = @formals
1896
+ is_vararg = args_to_walk.any? { |arg| arg.kind == :rest }
1897
+ if is_vararg
1898
+ index_of_star = args_to_walk.index { |arg| arg.kind == :rest }
1899
+ # re-splatting vararg super call. assholes
1900
+
1901
+ pre_star = args_to_walk[0...index_of_star]
1902
+ result = call_instruct(ClassRegistry['Array'].binding, :[], *pre_star,
1903
+ value: true, raise: false)
1904
+ result = call_instruct(result, :+, args_to_walk[index_of_star],
1905
+ value: true, raise: false)
1906
+
1907
+ post_star = args_to_walk[index_of_star+1 .. -1]
1908
+ post_star_temp = call_instruct(ClassRegistry['Array'].binding, :[], *post_star,
1909
+ value: true, raise: false)
1910
+ result = call_instruct(result, :+, post_star_temp, value: true, raise: false)
1911
+ [result, is_vararg]
1912
+ else
1913
+ [args_to_walk, is_vararg]
1914
+ end
1915
+ end
1916
+
1917
+ # Computes a splatting node (:args_add_star)
1918
+ def compute_varargs(args)
1919
+ result = call_instruct(ClassRegistry['Array'].binding, :new, value: true, raise: false)
1920
+ if args[1][0] == :args_add_star || args[1].children.any?
1921
+ prefix = if args[1][0] == :args_add_star
1922
+ then compute_varargs(args[1])
1923
+ else prefix = build_array_instruct(args[1].children)
1924
+ end
1925
+ result = call_instruct(result, :+, prefix, value: true, raise: false)
1926
+ end
1927
+ starred = walk_node args[2], value: true
1928
+ starred_converted = rb_check_convert_type(starred, ClassRegistry['Array'].binding, :to_a)
1929
+ result = call_instruct(result, :+, starred_converted, value: true, raise: false)
1930
+ if args[3..-1].any?
1931
+ suffix = build_array_instruct(args[3..-1])
1932
+ result = call_instruct(result, :+, suffix, value: true, raise: false)
1933
+ end
1934
+ result
1935
+ end
1936
+
1937
+ # Adds a generic method call instruction.
1938
+ def call_instruct(receiver, method, *args)
1939
+ opts = {raise: true}
1940
+ opts.merge!(args.last) if Hash === args.last
1941
+ result = create_result_if_needed opts
1942
+ call_opts = { ignore_privacy: opts[:ignore_privacy] }
1943
+ add_instruction_with_opts(:call, result, receiver, method.to_sym, *args, call_opts)
1944
+ add_potential_raise_edge if opts[:raise]
1945
+ result
1946
+ end
1947
+
1948
+ # Adds a generic method call instruction.
1949
+ def call_vararg_instruct(receiver, method, args, opts={})
1950
+ opts = {raise: true, value: true}.merge(opts)
1951
+ result = create_result_if_needed opts
1952
+ call_opts = { ignore_privacy: opts[:ignore_privacy] }
1953
+ add_instruction_with_opts(:call_vararg, result, receiver, method.to_sym, args, opts, call_opts)
1954
+ add_potential_raise_edge if opts[:raise]
1955
+ result
1956
+ end
1957
+
1958
+ # Adds a generic method super instruction.
1959
+ def super_instruct(*args)
1960
+ opts = (Hash === args.last) ? args.last : {}
1961
+ result = create_result_if_needed opts
1962
+
1963
+ add_instruction(:super, result, *args)
1964
+ add_potential_raise_edge
1965
+ result
1966
+ end
1967
+
1968
+ # Adds a generic method super instruction.
1969
+ def super_vararg_instruct(args, block, opts={})
1970
+ opts = {raise: true, value: true}.merge(opts)
1971
+ result = create_result_if_needed opts
1972
+ add_instruction(:super_vararg, result, args, block)
1973
+ add_potential_raise_edge if opts[:raise]
1974
+ result
1975
+ end
1976
+
1977
+ # Adds an edge from the current block to the current rescue target,
1978
+ # while creating a new block for the "natural" exit.
1979
+ def add_potential_raise_edge(add_success_branch = true)
1980
+ fail_block = create_block
1981
+ @graph.add_edge(@current_block, fail_block, RGL::ControlFlowGraph::EDGE_ABNORMAL)
1982
+ with_current_basic_block(fail_block) do
1983
+ observe_just_raised_exception
1984
+ uncond_instruct current_rescue, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL
1985
+ end
1986
+ uncond_instruct create_block, jump_instruct: false if add_success_branch
1987
+ end
1988
+
1989
+ # Looks up the value of a variable and assigns it to a new temporary
1990
+ def variable_instruct(var_ref)
1991
+ return self_register if var_ref.expanded_identifier == 'self'
1992
+ var_named_instruct var_ref.expanded_identifier
1993
+ end
1994
+
1995
+ def var_named_instruct(var_name)
1996
+ binding = current_scope.lookup_or_create_local(var_name)
1997
+ result = lookup_or_create_temporary(:var, binding)
1998
+ copy_instruct result, binding
1999
+ result
2000
+ end
2001
+
2002
+ # Performs constant lookup.
2003
+ # TODO(adgar): FULL CONSTANT LOOKUP w/ SUPERCLASS CHECKING ETC
2004
+ def const_lookup(const_name)
2005
+ ident = const_instruct(const_name)
2006
+ call_instruct(current_namespace,
2007
+ :const_get, ident, value: true, ignore_privacy: true)
2008
+ end
2009
+
2010
+ # Performs constant definition checking.
2011
+ # TODO(adgar): FULL CONSTANT LOOKUP w/ SUPERCLASS CHECKING ETC
2012
+ def const_defined?(const_name)
2013
+ ident = const_instruct(const_name)
2014
+ call_instruct(current_namespace,
2015
+ :const_defined?, ident, value: true, ignore_privacy: true)
2016
+ end
2017
+
2018
+ def binary_instruct(lhs, op, rhs, opts={})
2019
+ opts = {value: true}.merge(opts)
2020
+ if op == :or || op == :"||"
2021
+ return or_instruct(lhs, rhs, opts)
2022
+ elsif op == :and || op == :"&&"
2023
+ return and_instruct(lhs, rhs, opts)
2024
+ end
2025
+
2026
+ lhs_result = walk_node lhs, value: true
2027
+ rhs_result = walk_node rhs, value: true
2028
+ call_instruct(lhs_result, op, rhs_result, opts)
2029
+ end
2030
+
2031
+ def ternary_instruct(cond, if_true, if_false, opts={})
2032
+ opts = {value: true}.merge(opts)
2033
+ if_true_block, if_false_block, after = create_blocks 3
2034
+ cond_result = walk_node cond, value: true
2035
+ cond_instruct(cond_result, if_true_block, if_false_block)
2036
+
2037
+ start_block if_true_block
2038
+ if_true_result = walk_node if_true, opts
2039
+ if_true_block = @current_block
2040
+
2041
+ start_block if_false_block
2042
+ if_false_result = walk_node if_false, opts
2043
+ if_false_block = @current_block
2044
+
2045
+ # generate temporary if necessary
2046
+ result = opts[:value] ?
2047
+ lookup_or_create_temporary(:ternary, cond_result, if_true_result, if_false_result) :
2048
+ nil
2049
+
2050
+ start_block if_true_block
2051
+ copy_instruct(result, if_true_result) if opts[:value]
2052
+ uncond_instruct(after)
2053
+
2054
+ start_block if_false_block
2055
+ copy_instruct(result, if_false_result) if opts[:value]
2056
+ uncond_instruct(after)
2057
+
2058
+ start_block after
2059
+ result
2060
+ end
2061
+
2062
+ # Runs the list of operations in body while the condition is true.
2063
+ # Then returns nil.
2064
+ def while_instruct(condition, body, opts={})
2065
+ opts = {value: true}.merge(opts)
2066
+ body_block, after_block, precond_block = create_blocks 3
2067
+
2068
+ with_jump_targets(break: after_block, redo: body_block, next: precond_block) do
2069
+ uncond_instruct precond_block
2070
+ start_block precond_block
2071
+
2072
+ cond_result = walk_node condition, value: true
2073
+ cond_instruct(cond_result, body_block, after_block)
2074
+
2075
+ start_block body_block
2076
+ walk_body body, value: false
2077
+ cond_result = walk_node condition, value: true
2078
+ cond_instruct(cond_result, body_block, after_block)
2079
+ end
2080
+
2081
+ start_block after_block
2082
+ const_instruct(nil) if opts[:value]
2083
+ end
2084
+
2085
+ # Runs the list of operations in body until the condition is true.
2086
+ # Then returns nil.
2087
+ def until_instruct(condition, body, opts={})
2088
+ opts = {value: true}.merge(opts)
2089
+ body_block, after_block, precond_block = create_blocks 3
2090
+
2091
+ with_jump_targets(break: after_block, redo: body_block, next: precond_block) do
2092
+ uncond_instruct precond_block
2093
+ start_block precond_block
2094
+
2095
+ cond_result = walk_node condition, value: true
2096
+ cond_instruct(cond_result, after_block, body_block)
2097
+
2098
+ start_block body_block
2099
+
2100
+ walk_body body, value: false
2101
+ cond_result = walk_node condition, value: true
2102
+ cond_instruct(cond_result, after_block, body_block)
2103
+ end
2104
+
2105
+ start_block after_block
2106
+ const_instruct(nil) if opts[:value]
2107
+ end
2108
+
2109
+ # Performs an OR operation, with short circuiting that must save
2110
+ # the result of the operation.
2111
+ def or_instruct(lhs, rhs, opts={})
2112
+ opts = {value: true}.merge(opts)
2113
+ if opts[:value]
2114
+ then or_instruct_value(lhs, rhs)
2115
+ else or_instruct_novalue(lhs, rhs)
2116
+ end
2117
+ end
2118
+
2119
+ # Performs a short-circuit OR operation while retaining the resulting value.
2120
+ def or_instruct_value(lhs, rhs)
2121
+ after = create_block
2122
+
2123
+ lhs_result = walk_node lhs, value: true
2124
+
2125
+ result = nil
2126
+ false_block = build_block_with_jump(after) do
2127
+ rhs_result = walk_node rhs, value: true
2128
+ result = lookup_or_create_temporary(:or_short_circuit, lhs_result, rhs_result)
2129
+ copy_instruct(result, rhs_result)
2130
+ end
2131
+ true_block = build_block_with_jump(after) do
2132
+ copy_instruct(result, lhs_result)
2133
+ end
2134
+
2135
+ cond_instruct(lhs_result, true_block, false_block)
2136
+
2137
+ start_block(after)
2138
+ result
2139
+ end
2140
+
2141
+ # Performs a short-circuit OR operation while discarding the resulting value.
2142
+ def or_instruct_novalue(lhs, rhs)
2143
+ after = create_block
2144
+
2145
+ lhs_result = walk_node lhs, value: true
2146
+
2147
+ false_block = build_block_with_jump(after) do
2148
+ walk_node rhs, value: false
2149
+ end
2150
+ cond_instruct(lhs_result, after, false_block)
2151
+
2152
+ start_block(after)
2153
+ end
2154
+
2155
+ # Performs an AND operation, with short circuiting, that must save
2156
+ # the result of the operation.
2157
+ def and_instruct(lhs, rhs, opts={})
2158
+ opts = {value: true}.merge(opts)
2159
+ if opts[:value]
2160
+ then and_instruct_value(lhs, rhs)
2161
+ else and_instruct_novalue(lhs, rhs)
2162
+ end
2163
+ end
2164
+
2165
+ # Performs a short-circuit AND operation while retaining the resulting value.
2166
+ def and_instruct_value(lhs, rhs)
2167
+ after = create_block
2168
+
2169
+ lhs_result = walk_node lhs, value: true
2170
+
2171
+ result = nil
2172
+ true_block = build_block_with_jump(after) do
2173
+ rhs_result = walk_node rhs, value: true
2174
+ result = lookup_or_create_temporary(:and_short_circuit, lhs_result, rhs_result)
2175
+ copy_instruct(result, rhs_result)
2176
+ end
2177
+ false_block = build_block_with_jump(after) do
2178
+ copy_instruct(result, lhs_result)
2179
+ end
2180
+ cond_instruct(lhs_result, true_block, false_block)
2181
+
2182
+ start_block(after)
2183
+ result
2184
+ end
2185
+
2186
+ # Performs a short-circuit AND operation while discarding the resulting value.
2187
+ def and_instruct_novalue(lhs, rhs)
2188
+ after = create_block
2189
+
2190
+ lhs_result = walk_node lhs, value: true
2191
+
2192
+ true_block = build_block_with_jump(after) do
2193
+ walk_node rhs, value: false
2194
+ end
2195
+ cond_instruct(lhs_result, true_block, after)
2196
+ start_block(after)
2197
+ end
2198
+
2199
+ # Performs a value-capturing if instruction, with unlimited else-ifs
2200
+ # and a potential else block.
2201
+ #
2202
+ # condition: Sexp
2203
+ # body: [Sexp]
2204
+ # else_block: Sexp | NilClass
2205
+ def if_instruct(node, is_mod=false, opts={})
2206
+ opts = {value: true}.merge(opts)
2207
+ if opts[:value]
2208
+ then if_instruct_value(node, is_mod)
2209
+ else if_instruct_novalue(node, is_mod)
2210
+ end
2211
+ end
2212
+
2213
+ def if_instruct_value(node, is_mod=false)
2214
+ result = create_temporary
2215
+ after = create_block
2216
+ current = node
2217
+ next_block = nil
2218
+
2219
+ while current
2220
+ if current.type == :else
2221
+ true_block = next_block
2222
+ body, next_block, else_block = current[1], after, nil
2223
+ else
2224
+ true_block = create_block
2225
+ condition, body, else_block = current.children
2226
+ next_block = create_block
2227
+
2228
+ cond_result = walk_node condition, value: true
2229
+ cond_instruct(cond_result, true_block, next_block)
2230
+ end
2231
+
2232
+ start_block true_block
2233
+ body = [body] if is_mod
2234
+ body_result = walk_body body, value: true
2235
+ copy_instruct(result, body_result)
2236
+ uncond_instruct(after)
2237
+
2238
+ start_block next_block
2239
+ # check: is there no else at all, and we're about to break out of the loop?
2240
+ if current.type != :else && else_block.nil?
2241
+ copy_instruct(result, nil)
2242
+ uncond_instruct(after)
2243
+ start_block after
2244
+ end
2245
+ current = else_block
2246
+ end
2247
+ result
2248
+ end
2249
+
2250
+ # Performs an if instruction that ignores result values, with unlimited else-ifs
2251
+ # and a potential else block.
2252
+ #
2253
+ # condition: Sexp
2254
+ # body: [Sexp]
2255
+ # else_block: Sexp | NilClass
2256
+ def if_instruct_novalue(node, is_mod=false)
2257
+ current = node
2258
+ after = create_block
2259
+ next_block = nil
2260
+
2261
+ while current
2262
+ if current.type == :else
2263
+ true_block = next_block
2264
+ body, next_block, else_block = current[1], after, nil
2265
+ else
2266
+ true_block = create_block
2267
+ condition, body, else_block = current.children
2268
+ next_block = else_block ? create_block : after
2269
+
2270
+ cond_result = walk_node condition, value: true
2271
+ cond_instruct(cond_result, true_block, next_block)
2272
+ end
2273
+
2274
+ start_block true_block
2275
+ body = [body] if is_mod
2276
+ walk_body body, value: false
2277
+ uncond_instruct(after)
2278
+
2279
+ start_block next_block
2280
+ current = else_block
2281
+ end
2282
+ end
2283
+
2284
+ def unless_instruct(condition, body, else_block, opts={})
2285
+ opts = {value: true}.merge(opts)
2286
+ if opts[:value]
2287
+ then unless_instruct_value(condition, body, else_block)
2288
+ else unless_instruct_novalue(condition, body, else_block)
2289
+ end
2290
+ end
2291
+
2292
+ # Performs a value-capturing unless instruction.
2293
+ #
2294
+ # condition: Sexp
2295
+ # body: [Sexp]
2296
+ # else_block: Sexp | NilClass
2297
+ def unless_instruct_value(condition, body, else_block)
2298
+ result = create_temporary
2299
+ after = create_block
2300
+
2301
+ cond_result = walk_node condition, value: true
2302
+
2303
+ true_block = build_block_with_jump(after) do
2304
+ body_result = walk_body body, value: true
2305
+ copy_instruct(result, body_result)
2306
+ end
2307
+
2308
+ next_block = build_block_with_jump(after) do
2309
+ body_result = if else_block
2310
+ then walk_body else_block[1], value: true
2311
+ else const_instruct nil
2312
+ end
2313
+ copy_instruct result, body_result
2314
+ end
2315
+
2316
+ cond_instruct(cond_result, next_block, true_block)
2317
+
2318
+ start_block after
2319
+ result
2320
+ end
2321
+
2322
+ # Performs an unless instruction, ignoring the potential that its value
2323
+ # is saved.
2324
+ #
2325
+ # condition: Sexp
2326
+ # body: [Sexp]
2327
+ # else_block: Sexp | NilClass
2328
+ def unless_instruct_novalue(condition, body, else_block)
2329
+ after, true_block = create_blocks 2
2330
+ next_block = else_block ? create_block : after
2331
+
2332
+ cond_result = walk_node condition, value: true
2333
+ cond_instruct(cond_result, next_block, true_block)
2334
+
2335
+ start_block true_block
2336
+ walk_body body, value: false
2337
+ uncond_instruct(after)
2338
+
2339
+ if else_block
2340
+ start_block next_block
2341
+ else_block[1].each { |elt| walk_node(elt, value: false) }
2342
+ uncond_instruct(after)
2343
+ end
2344
+
2345
+ start_block after
2346
+ end
2347
+
2348
+ def defined_op_instruct(expression)
2349
+ case expression.type
2350
+ when :yield0, :yield
2351
+ # give 'yield' if there is a block, otherwise nil
2352
+ result = lookup_or_create_temporary(:defined, :yield)
2353
+ after = create_block
2354
+ with_block_block = build_block_with_jump(after) do
2355
+ copy_instruct(result, const_instruct('yield'))
2356
+ end
2357
+ without_block_block = build_block_with_jump(after) do
2358
+ copy_instruct(result, const_instruct(nil))
2359
+ end
2360
+ cond_instruct(@block_arg, with_block_block, without_block_block)
2361
+ start_block after
2362
+ result
2363
+ when :assign, :massign
2364
+ const_instruct('assignment')
2365
+ when :var_ref
2366
+ case expression[1].type
2367
+ when :@ident
2368
+ const_instruct('local-variable')
2369
+ when :@kw
2370
+ case expression[1][1]
2371
+ when 'nil' then const_instruct('nil')
2372
+ when 'self' then const_instruct('self')
2373
+ when 'true' then const_instruct('true')
2374
+ when 'false' then const_instruct('false')
2375
+ when '__FILE__', '__LINE__', '__ENCODING__' then const_instruct('expression')
2376
+ else
2377
+ raise NotImplementedError.new("defined? on keyword #{expression[1][1]}")
2378
+ end
2379
+ when :@const
2380
+ const_defined?(expression.expanded_identifier)
2381
+ when :@ivar
2382
+
2383
+ when :@cvar
2384
+
2385
+ when :@gvar
2386
+ end
2387
+ when :string_literal, :and, :or, :@int, :@float, :symbol_literal
2388
+ const_instruct('expression')
2389
+ else
2390
+ raise NotImplementedError.new("Unimplemented defined? case: #{expression.type}")
2391
+ end
2392
+ end
2393
+
2394
+ # Takes a set of either :@tstring_content or :string_embexpr nodes
2395
+ # and constructs a string out of them. (In other words, this computes
2396
+ # the contents of possibly-interpolated strings).
2397
+ def build_string_instruct(components)
2398
+ temp = const_instruct('')
2399
+ components.each do |node|
2400
+ as_string = walk_node node, value: true
2401
+ temp = call_instruct(temp, :+, as_string, value: true, raise: false)
2402
+ end
2403
+ temp
2404
+ end
2405
+
2406
+ # Takes a set of nodes, finds their values, and builds a temporary holding
2407
+ # the array containing them.
2408
+ def build_array_instruct(components)
2409
+ args = components.map { |arg| walk_node(arg, value: true) }
2410
+ call_instruct(ClassRegistry['Array'].binding, :[], *args, value: true, raise: false)
2411
+ end
2412
+
2413
+ # CL-style compiler hint generation.
2414
+ def declare_instruct(kind, *args)
2415
+ verify_declare_instruction(kind, args)
2416
+ add_instruction(:declare, kind, *args)
2417
+ end
2418
+
2419
+ def verify_declare_instruction(kind, args)
2420
+ case kind
2421
+ when :alias
2422
+ if args.size != 2
2423
+ raise ArgumentError.new("declare :alias requires 2 args")
2424
+ end
2425
+ when :expect_tuple_size
2426
+ if args.size != 3
2427
+ raise ArgumentError.new('declare :expect_tuple_size requires 3 args')
2428
+ end
2429
+ end
2430
+ end
2431
+
2432
+ def create_result_if_needed(opts)
2433
+ value = opts.delete(:value)
2434
+ value ? create_temporary : nil
2435
+ end
2436
+
2437
+ def add_fake_edge(from, to)
2438
+ @graph.add_edge(from, to, RGL::ControlFlowGraph::EDGE_FAKE)
2439
+ end
2440
+
2441
+ # Returns the name of the current temporary.
2442
+ def current_temporary(prefix='t')
2443
+ "%#{prefix}#{@temporary_counter}"
2444
+ end
2445
+
2446
+ # Creates a temporary variable with an unused name.
2447
+ def create_temporary(name = nil)
2448
+ unless name
2449
+ @temporary_counter += 1
2450
+ name = current_temporary
2451
+ end
2452
+ Bindings::TemporaryBinding.new(name, nil)
2453
+ end
2454
+
2455
+ # Creates a block temporary variable with an unused name.
2456
+ def create_block_temporary(args, body, callsite_block=nil)
2457
+ @temporary_counter += 1
2458
+ name = current_temporary('B-')
2459
+ if callsite_block
2460
+ new_proc = LaserProc.new(args, body, @graph, callsite_block)
2461
+ else
2462
+ new_proc = LaserProc.new(args, body)
2463
+ end
2464
+ binding = Bindings::BlockBinding.new(name, nil)
2465
+ add_instruction(:assign, binding, new_proc)
2466
+ [binding, new_proc]
2467
+ end
2468
+
2469
+ def lookup_or_create_temporary(*keys)
2470
+ @temporary_table[keys]
2471
+ end
2472
+
2473
+ # Adds a simple instruction to the current basic block.
2474
+ def add_instruction(*args)
2475
+ @current_block.instructions << Instruction.new(args, node: @current_node,
2476
+ block: @current_block)
2477
+ end
2478
+
2479
+ # Adds a simple instruction to the current basic block.
2480
+ def add_instruction_with_opts(*args, opts)
2481
+ opts = {node: @current_node, block: @current_block}.merge(opts)
2482
+ i = Instruction.new(args, opts)
2483
+ @current_block.instructions << Instruction.new(args, opts)
2484
+ end
2485
+
2486
+ # Creates the given number of blocks.
2487
+ def create_blocks(count)
2488
+ (0...count).to_a.map { create_block }
2489
+ end
2490
+
2491
+ # Creates a new basic block for flow analysis.
2492
+ def create_block(name = 'B' + (@block_counter += 1).to_s)
2493
+ result = BasicBlock.new(name)
2494
+ @graph.add_vertex result
2495
+ result
2496
+ end
2497
+
2498
+ # Sets the current block to be the given block.
2499
+ def start_block(block)
2500
+ @current_block = block
2501
+ end
2502
+ end
2503
+ end
2504
+ end
2505
+ end