laser 0.7.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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