opal 1.3.1 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -0
  3. data/.github/workflows/build.yml +20 -21
  4. data/.rubocop.yml +5 -1
  5. data/CHANGELOG.md +119 -11
  6. data/UNRELEASED.md +4 -9
  7. data/benchmark-ips/bm_truthy.rb +30 -0
  8. data/bin/opal-mspec +1 -3
  9. data/bin/opal-repl +1 -2
  10. data/bin/remove-filters +1 -4
  11. data/docs/compiled_ruby.md +37 -22
  12. data/docs/faq.md +1 -1
  13. data/docs/headless_chrome.md +11 -21
  14. data/docs/jquery.md +1 -6
  15. data/docs/opal_parser.md +3 -1
  16. data/docs/promises.md +2 -0
  17. data/docs/releasing.md +3 -0
  18. data/docs/roda-sprockets.md +0 -1
  19. data/docs/source_maps.md +10 -11
  20. data/docs/static_applications.md +2 -2
  21. data/docs/unsupported_features.md +4 -0
  22. data/exe/opal-repl +1 -3
  23. data/lib/opal/ast/builder.rb +1 -1
  24. data/lib/opal/cli.rb +2 -2
  25. data/lib/opal/cli_runners/nodejs.rb +9 -2
  26. data/lib/opal/cli_runners/source-map-support-browser.js +80 -216
  27. data/lib/opal/cli_runners/source-map-support-node.js +80 -216
  28. data/lib/opal/cli_runners/source-map-support.js +5 -1
  29. data/lib/opal/cli_runners/system_runner.rb +10 -4
  30. data/lib/opal/compiler.rb +3 -5
  31. data/lib/opal/fragment.rb +5 -1
  32. data/lib/opal/nodes/args/extract_block_arg.rb +1 -8
  33. data/lib/opal/nodes/args/extract_kwoptarg.rb +1 -3
  34. data/lib/opal/nodes/args/extract_optarg.rb +1 -3
  35. data/lib/opal/nodes/args/extract_post_arg.rb +2 -5
  36. data/lib/opal/nodes/args/extract_post_optarg.rb +2 -7
  37. data/lib/opal/nodes/args/initialize_iterarg.rb +1 -3
  38. data/lib/opal/nodes/args/prepare_post_args.rb +5 -1
  39. data/lib/opal/nodes/base.rb +3 -2
  40. data/lib/opal/nodes/call.rb +20 -9
  41. data/lib/opal/nodes/call_special.rb +50 -0
  42. data/lib/opal/nodes/class.rb +24 -15
  43. data/lib/opal/nodes/constants.rb +23 -5
  44. data/lib/opal/nodes/def.rb +20 -23
  45. data/lib/opal/nodes/defined.rb +5 -5
  46. data/lib/opal/nodes/definitions.rb +2 -2
  47. data/lib/opal/nodes/defs.rb +2 -5
  48. data/lib/opal/nodes/helpers.rb +48 -18
  49. data/lib/opal/nodes/if.rb +113 -8
  50. data/lib/opal/nodes/iter.rb +23 -16
  51. data/lib/opal/nodes/literal.rb +18 -4
  52. data/lib/opal/nodes/logic.rb +2 -1
  53. data/lib/opal/nodes/masgn.rb +4 -9
  54. data/lib/opal/nodes/module.rb +29 -19
  55. data/lib/opal/nodes/node_with_args.rb +1 -7
  56. data/lib/opal/nodes/scope.rb +54 -15
  57. data/lib/opal/nodes/singleton_class.rb +5 -3
  58. data/lib/opal/nodes/super.rb +12 -12
  59. data/lib/opal/nodes/top.rb +34 -31
  60. data/lib/opal/nodes/variables.rb +2 -2
  61. data/lib/opal/nodes/x_string.rb +30 -28
  62. data/lib/opal/nodes.rb +0 -1
  63. data/lib/opal/parser/patch.rb +75 -0
  64. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  65. data/lib/opal/regexp_anchors.rb +7 -7
  66. data/lib/opal/requires.rb +19 -0
  67. data/lib/opal/rewriters/pattern_matching.rb +1 -1
  68. data/lib/opal/rewriters/returnable_logic.rb +102 -4
  69. data/lib/opal/util.rb +2 -2
  70. data/lib/opal/version.rb +1 -1
  71. data/lib/opal.rb +1 -17
  72. data/opal/corelib/array/pack.rb +11 -11
  73. data/opal/corelib/array.rb +193 -152
  74. data/opal/corelib/basic_object.rb +19 -15
  75. data/opal/corelib/binding.rb +7 -7
  76. data/opal/corelib/boolean.rb +12 -15
  77. data/opal/corelib/class.rb +23 -1
  78. data/opal/corelib/comparable.rb +8 -8
  79. data/opal/corelib/complex/base.rb +2 -2
  80. data/opal/corelib/complex.rb +79 -88
  81. data/opal/corelib/constants.rb +9 -9
  82. data/opal/corelib/dir.rb +4 -3
  83. data/opal/corelib/enumerable.rb +140 -127
  84. data/opal/corelib/enumerator/arithmetic_sequence.rb +177 -0
  85. data/opal/corelib/enumerator/chain.rb +42 -0
  86. data/opal/corelib/enumerator/generator.rb +35 -0
  87. data/opal/corelib/enumerator/lazy.rb +243 -0
  88. data/opal/corelib/enumerator/yielder.rb +36 -0
  89. data/opal/corelib/enumerator.rb +45 -300
  90. data/opal/corelib/error/errno.rb +47 -0
  91. data/opal/corelib/error.rb +62 -60
  92. data/opal/corelib/file.rb +26 -12
  93. data/opal/corelib/hash.rb +98 -107
  94. data/opal/corelib/helpers.rb +62 -13
  95. data/opal/corelib/io.rb +48 -35
  96. data/opal/corelib/kernel/format.rb +29 -29
  97. data/opal/corelib/kernel.rb +86 -83
  98. data/opal/corelib/main.rb +14 -12
  99. data/opal/corelib/marshal/read_buffer.rb +15 -15
  100. data/opal/corelib/marshal/write_buffer.rb +45 -44
  101. data/opal/corelib/marshal.rb +3 -3
  102. data/opal/corelib/math.rb +50 -50
  103. data/opal/corelib/method.rb +12 -8
  104. data/opal/corelib/module.rb +79 -75
  105. data/opal/corelib/nil.rb +9 -11
  106. data/opal/corelib/number.rb +113 -118
  107. data/opal/corelib/numeric.rb +37 -33
  108. data/opal/corelib/object_space.rb +11 -10
  109. data/opal/corelib/pack_unpack/format_string_parser.rb +3 -3
  110. data/opal/corelib/pattern_matching/base.rb +7 -7
  111. data/opal/corelib/pattern_matching.rb +1 -1
  112. data/opal/corelib/proc.rb +15 -16
  113. data/opal/corelib/process/base.rb +2 -2
  114. data/opal/corelib/process/status.rb +21 -0
  115. data/opal/corelib/process.rb +5 -5
  116. data/opal/corelib/random/formatter.rb +11 -11
  117. data/opal/corelib/random/math_random.js.rb +1 -1
  118. data/opal/corelib/random/mersenne_twister.rb +3 -3
  119. data/opal/corelib/random/seedrandom.js.rb +3 -3
  120. data/opal/corelib/random.rb +17 -17
  121. data/opal/corelib/range.rb +51 -35
  122. data/opal/corelib/rational/base.rb +4 -4
  123. data/opal/corelib/rational.rb +61 -62
  124. data/opal/corelib/regexp.rb +54 -45
  125. data/opal/corelib/runtime.js +247 -141
  126. data/opal/corelib/string/encoding.rb +21 -21
  127. data/opal/corelib/string/unpack.rb +19 -14
  128. data/opal/corelib/string.rb +137 -130
  129. data/opal/corelib/struct.rb +59 -46
  130. data/opal/corelib/time.rb +47 -57
  131. data/opal/corelib/trace_point.rb +2 -2
  132. data/opal/corelib/unsupported.rb +31 -120
  133. data/opal/corelib/variables.rb +3 -3
  134. data/opal/opal/base.rb +9 -8
  135. data/opal/opal/full.rb +8 -8
  136. data/opal/opal/mini.rb +17 -17
  137. data/opal/opal.rb +17 -18
  138. data/opal.gemspec +1 -1
  139. data/spec/filters/bugs/array.rb +4 -24
  140. data/spec/filters/bugs/basicobject.rb +0 -1
  141. data/spec/filters/bugs/bigdecimal.rb +0 -23
  142. data/spec/filters/bugs/binding.rb +0 -1
  143. data/spec/filters/bugs/boolean.rb +3 -0
  144. data/spec/filters/bugs/class.rb +2 -0
  145. data/spec/filters/bugs/date.rb +0 -5
  146. data/spec/filters/bugs/encoding.rb +8 -50
  147. data/spec/filters/bugs/enumerable.rb +4 -1
  148. data/spec/filters/bugs/enumerator.rb +3 -36
  149. data/spec/filters/bugs/exception.rb +0 -2
  150. data/spec/filters/bugs/file.rb +0 -2
  151. data/spec/filters/bugs/float.rb +0 -3
  152. data/spec/filters/bugs/hash.rb +5 -3
  153. data/spec/filters/bugs/integer.rb +2 -3
  154. data/spec/filters/bugs/kernel.rb +2 -31
  155. data/spec/filters/bugs/language.rb +29 -49
  156. data/spec/filters/bugs/main.rb +0 -2
  157. data/spec/filters/bugs/marshal.rb +2 -3
  158. data/spec/filters/bugs/matrix.rb +0 -36
  159. data/spec/filters/bugs/module.rb +7 -61
  160. data/spec/filters/bugs/numeric.rb +0 -7
  161. data/spec/filters/bugs/objectspace.rb +1 -1
  162. data/spec/filters/bugs/pack_unpack.rb +0 -4
  163. data/spec/filters/bugs/proc.rb +0 -9
  164. data/spec/filters/bugs/random.rb +0 -5
  165. data/spec/filters/bugs/range.rb +1 -6
  166. data/spec/filters/bugs/regexp.rb +0 -3
  167. data/spec/filters/bugs/set.rb +8 -1
  168. data/spec/filters/bugs/string.rb +9 -34
  169. data/spec/filters/bugs/stringscanner.rb +8 -7
  170. data/spec/filters/bugs/struct.rb +2 -3
  171. data/spec/filters/bugs/symbol.rb +0 -1
  172. data/spec/filters/bugs/time.rb +0 -8
  173. data/spec/filters/bugs/unboundmethod.rb +0 -8
  174. data/spec/filters/bugs/warnings.rb +1 -7
  175. data/spec/filters/unsupported/freeze.rb +24 -0
  176. data/spec/filters/unsupported/integer.rb +1 -0
  177. data/spec/filters/unsupported/kernel.rb +12 -0
  178. data/spec/filters/unsupported/privacy.rb +3 -0
  179. data/spec/filters/unsupported/string.rb +2 -0
  180. data/spec/lib/builder_spec.rb +2 -2
  181. data/spec/lib/cli_spec.rb +1 -1
  182. data/spec/lib/compiler_spec.rb +37 -37
  183. data/spec/lib/simple_server_spec.rb +2 -2
  184. data/spec/lib/source_map/file_spec.rb +1 -1
  185. data/spec/opal/compiler/irb_spec.rb +2 -2
  186. data/spec/opal/core/io/read_spec.rb +69 -0
  187. data/spec/opal/core/kernel/puts_spec.rb +90 -0
  188. data/spec/opal/core/language/super_spec.rb +21 -0
  189. data/spec/opal/core/language/xstring_spec.rb +13 -0
  190. data/spec/opal/core/language_spec.rb +14 -0
  191. data/spec/opal/core/string/gsub_spec.rb +8 -0
  192. data/spec/ruby_specs +4 -2
  193. data/spec/support/rewriters_helper.rb +1 -1
  194. data/stdlib/bigdecimal.rb +7 -11
  195. data/stdlib/buffer/view.rb +2 -2
  196. data/stdlib/buffer.rb +2 -2
  197. data/stdlib/date.rb +5 -6
  198. data/stdlib/erb.rb +1 -0
  199. data/stdlib/js.rb +2 -1
  200. data/stdlib/native.rb +7 -8
  201. data/stdlib/nodejs/argf.rb +4 -4
  202. data/stdlib/nodejs/base.rb +29 -0
  203. data/stdlib/nodejs/dir.rb +1 -1
  204. data/stdlib/nodejs/env.rb +6 -9
  205. data/stdlib/nodejs/file.rb +23 -17
  206. data/stdlib/nodejs/fileutils.rb +3 -3
  207. data/stdlib/nodejs/io.rb +2 -20
  208. data/stdlib/nodejs/irb.rb +0 -0
  209. data/stdlib/nodejs/kernel.rb +2 -37
  210. data/stdlib/nodejs.rb +1 -3
  211. data/stdlib/opal/miniracer.rb +2 -0
  212. data/stdlib/opal/platform.rb +6 -13
  213. data/stdlib/opal/replutils.rb +16 -5
  214. data/stdlib/opal-parser.rb +2 -2
  215. data/stdlib/optparse/ac.rb +54 -0
  216. data/stdlib/optparse/date.rb +14 -0
  217. data/stdlib/optparse/kwargs.rb +22 -0
  218. data/stdlib/optparse/shellwords.rb +7 -0
  219. data/stdlib/optparse/time.rb +15 -0
  220. data/stdlib/optparse/uri.rb +7 -0
  221. data/stdlib/optparse/version.rb +69 -0
  222. data/stdlib/optparse.rb +2279 -0
  223. data/stdlib/pathname.rb +5 -6
  224. data/stdlib/pp.rb +18 -2
  225. data/stdlib/promise/v2.rb +18 -29
  226. data/stdlib/promise.rb +15 -21
  227. data/stdlib/quickjs/io.rb +0 -2
  228. data/stdlib/quickjs/kernel.rb +0 -2
  229. data/stdlib/quickjs.rb +2 -0
  230. data/stdlib/set.rb +32 -32
  231. data/stdlib/shellwords.rb +240 -0
  232. data/stdlib/stringio.rb +3 -6
  233. data/stdlib/strscan.rb +5 -8
  234. data/stdlib/template.rb +2 -2
  235. data/stdlib/thread.rb +7 -9
  236. data/tasks/linting-parse-eslint-results.js +1 -0
  237. data/tasks/linting.rake +0 -10
  238. data/tasks/performance.rake +5 -2
  239. data/tasks/testing/mspec_special_calls.rb +0 -12
  240. data/tasks/testing.rake +55 -37
  241. data/test/nodejs/test_file.rb +11 -0
  242. metadata +55 -8
  243. data/lib/opal/nodes/case.rb +0 -114
@@ -13,33 +13,40 @@ module Opal
13
13
  children :body
14
14
 
15
15
  def compile
16
+ compiler.top_scope = self
17
+
16
18
  push version_comment
17
19
 
18
20
  in_scope do
19
- line '"use strict";' if compiler.use_strict?
21
+ if body == s(:nil)
22
+ # A shortpath for empty (stub?) modules.
23
+ line 'return Opal.nil;'
24
+ else
25
+ line '"use strict";' if compiler.use_strict?
20
26
 
21
- body_code = stmt(stmts)
22
- body_code = [body_code] unless body_code.is_a?(Array)
27
+ body_code = stmt(stmts)
28
+ body_code = [body_code] unless body_code.is_a?(Array)
23
29
 
24
- if compiler.eval?
25
- add_temp '$nesting = self.$$is_a_module ? [self] : [self.$$class]'
26
- else
27
- add_temp 'self = Opal.top'
28
- add_temp '$nesting = []'
29
- end
30
- add_temp 'nil = Opal.nil'
31
- add_temp '$$$ = Opal.$$$'
32
- add_temp '$$ = Opal.$$'
30
+ if compiler.eval?
31
+ add_temp '$nesting = self.$$is_a_module ? [self] : [self.$$class]' if @define_nesting
32
+ else
33
+ add_temp 'self = Opal.top' if @define_self
34
+ add_temp '$nesting = []' if @define_nesting
35
+ end
36
+ add_temp '$$ = Opal.$r($nesting)' if @define_relative_access
37
+
38
+ add_temp 'nil = Opal.nil'
39
+ add_temp '$$$ = Opal.$$$' if @define_absolute_const
33
40
 
34
- add_used_helpers
35
- add_used_operators
36
- line scope.to_vars
41
+ add_used_helpers
42
+ line scope.to_vars
37
43
 
38
- compile_method_stubs
39
- compile_irb_vars
40
- compile_end_construct
44
+ compile_method_stubs
45
+ compile_irb_vars
46
+ compile_end_construct
41
47
 
42
- line body_code
48
+ line body_code
49
+ end
43
50
  end
44
51
  opening
45
52
  closing
@@ -73,6 +80,12 @@ module Opal
73
80
  compiler.returns(body)
74
81
  end
75
82
 
83
+ # Returns '$$$', but also ensures that the '$$$' variable is set
84
+ def absolute_const
85
+ @define_absolute_const = true
86
+ '$$$'
87
+ end
88
+
76
89
  def compile_irb_vars
77
90
  if compiler.irb?
78
91
  line 'if (!Opal.irb_vars) { Opal.irb_vars = {}; }'
@@ -83,21 +96,11 @@ module Opal
83
96
  compiler.helpers.to_a.each { |h| add_temp "$#{h} = Opal.#{h}" }
84
97
  end
85
98
 
86
- def add_used_operators
87
- operators = compiler.operator_helpers.to_a
88
- operators.each do |op|
89
- name = Nodes::CallNode::OPERATORS[op]
90
- line "function $rb_#{name}(lhs, rhs) {"
91
- line " return (typeof(lhs) === 'number' && typeof(rhs) === 'number') ? lhs #{op} rhs : lhs['$#{op}'](rhs);"
92
- line '}'
93
- end
94
- end
95
-
96
99
  def compile_method_stubs
97
100
  if compiler.method_missing?
98
101
  calls = compiler.method_calls
99
- stubs = calls.to_a.map { |k| "'$#{k}'" }.join(', ')
100
- line "Opal.add_stubs([#{stubs}]);" unless stubs.empty?
102
+ stubs = calls.to_a.map(&:to_s).join(',')
103
+ line "Opal.add_stubs('#{stubs}');" unless stubs.empty?
101
104
  end
102
105
  end
103
106
 
@@ -70,7 +70,7 @@ module Opal
70
70
  def compile
71
71
  name = property(var_name)
72
72
  add_ivar name
73
- push "self#{name}"
73
+ push "#{scope.self}#{name}"
74
74
  end
75
75
  end
76
76
 
@@ -85,7 +85,7 @@ module Opal
85
85
 
86
86
  def compile
87
87
  name = property(var_name)
88
- push "self#{name} = "
88
+ push "#{scope.self}#{name} = "
89
89
  push expr(value)
90
90
 
91
91
  wrap '(', ')' if (recv? || expr?) && value
@@ -8,9 +8,9 @@ module Opal
8
8
  def compile
9
9
  @should_add_semicolon = false
10
10
  unpacked_children = unpack_return(children)
11
- stripped_children = strip_empty_children(unpacked_children)
11
+ stripped_children = XStringNode.strip_empty_children(unpacked_children)
12
12
 
13
- if single_line?(stripped_children)
13
+ if XStringNode.single_line?(stripped_children)
14
14
  # If it's a single line we'll try to:
15
15
  #
16
16
  # - strip empty lines
@@ -30,6 +30,31 @@ module Opal
30
30
  push ';' if @should_add_semicolon
31
31
  end
32
32
 
33
+ # Check if there's only one child or if they're all part of
34
+ # the same line (e.g. because of interpolations)
35
+ def self.single_line?(children)
36
+ (children.size == 1) || children.none? do |c|
37
+ c.type == :str && c.loc.expression.source.end_with?("\n")
38
+ end
39
+ end
40
+
41
+ # Will remove empty :str lines coming from cosmetic newlines in x-strings
42
+ #
43
+ # @example
44
+ # # this will generate two additional empty
45
+ # # children before and after `foo()`
46
+ # %x{
47
+ # foo()
48
+ # }
49
+ def self.strip_empty_children(children)
50
+ children = children.dup
51
+ empty_line = ->(child) { child.nil? || (child.type == :str && child.loc.expression.source.rstrip.empty?) }
52
+
53
+ children.shift while children.any? && empty_line[children.first]
54
+ children.pop while children.any? && empty_line[children.last]
55
+
56
+ children
57
+ end
33
58
 
34
59
  private
35
60
 
@@ -37,6 +62,7 @@ module Opal
37
62
  case child.type
38
63
  when :str
39
64
  value = child.loc.expression.source
65
+ scope.self if value.include? 'self'
40
66
  push Fragment.new(value, scope, child)
41
67
  when :begin, :gvar, :ivar, :nil
42
68
  push expr(child)
@@ -81,6 +107,8 @@ module Opal
81
107
  def extract_last_value(last_child)
82
108
  last_value = last_child.loc.expression.source.rstrip
83
109
 
110
+ scope.self if last_value.include? 'self'
111
+
84
112
  if (@returning || expr?) && last_value.end_with?(';')
85
113
  compiler.warning(
86
114
  'Removed semicolon ending x-string expression, interpreted as unintentional',
@@ -94,14 +122,6 @@ module Opal
94
122
  last_value
95
123
  end
96
124
 
97
- # Check if there's only one child or if they're all part of
98
- # the same line (e.g. because of interpolations)
99
- def single_line?(children)
100
- (children.size == 1) || children.none? do |c|
101
- c.type == :str && c.loc.expression.source.end_with?("\n")
102
- end
103
- end
104
-
105
125
  # A case for manually created :js_return statement in Compiler#returns
106
126
  # Since we need to take original source of :str we have to use raw source
107
127
  # so we need to combine "return" with "raw_source"
@@ -116,24 +136,6 @@ module Opal
116
136
 
117
137
  children
118
138
  end
119
-
120
- # Will remove empty :str lines coming from cosmetic newlines in x-strings
121
- #
122
- # @example
123
- # # this will generate two additional empty
124
- # # children before and after `foo()`
125
- # %x{
126
- # foo()
127
- # }
128
- def strip_empty_children(children)
129
- children = children.dup
130
- empty_line = ->(child) { child.nil? || (child.type == :str && child.loc.expression.source.rstrip.empty?) }
131
-
132
- children.shift while children.any? && empty_line[children.first]
133
- children.pop while children.any? && empty_line[children.last]
134
-
135
- children
136
- end
137
139
  end
138
140
  end
139
141
  end
data/lib/opal/nodes.rb CHANGED
@@ -20,7 +20,6 @@ require 'opal/nodes/logic'
20
20
  require 'opal/nodes/definitions'
21
21
  require 'opal/nodes/yield'
22
22
  require 'opal/nodes/rescue'
23
- require 'opal/nodes/case'
24
23
  require 'opal/nodes/super'
25
24
  require 'opal/nodes/top'
26
25
  require 'opal/nodes/while'
@@ -48,6 +48,81 @@ if RUBY_ENGINE == 'opal'
48
48
  diagnostic :error, :lvar_name, { name: name }, loc
49
49
  end
50
50
  end
51
+
52
+ # Taken From:
53
+ # https://github.com/whitequark/parser/blob/a7c638b7b205db9213a56897b41a8e5620df766e/lib/parser/builders/default.rb#L388
54
+ def dedent_string(node, dedent_level)
55
+ unless dedent_level.nil?
56
+ dedenter = ::Parser::Lexer::Dedenter.new(dedent_level)
57
+
58
+ case node.type
59
+ when :str
60
+ node = node.updated(nil, [dedenter.dedent(node.children.first)])
61
+ when :dstr, :xstr
62
+ children = node.children.map do |str_node|
63
+ if str_node.type == :str
64
+ str_node = str_node.updated(nil, [dedenter.dedent(str_node.children.first)])
65
+ next nil if str_node.children.first.empty?
66
+ else
67
+ dedenter.interrupt
68
+ end
69
+ str_node
70
+ end
71
+
72
+ node = node.updated(nil, children.compact)
73
+ end
74
+ end
75
+
76
+ node
77
+ end
78
+ end
79
+
80
+ class Parser::Lexer::Dedenter
81
+ # Taken From:
82
+ # https://github.com/whitequark/parser/blob/b7a08031523d05b2f76b0bab22fac00b1d3fe653/lib/parser/lexer/dedenter.rb#L36
83
+ def dedent(string)
84
+ original_encoding = string.encoding
85
+ # Prevent the following error when processing binary encoded source.
86
+ # "\xC0".split # => ArgumentError (invalid byte sequence in UTF-8)
87
+ lines = string.force_encoding(Encoding::BINARY).split("\\\n")
88
+ if lines.length == 1
89
+ # If the line continuation sequence was found but there is no second
90
+ # line, it was not really a line continuation and must be ignored.
91
+ lines = [string.force_encoding(original_encoding)]
92
+ else
93
+ lines.map! { |s| s.force_encoding(original_encoding) }
94
+ end
95
+
96
+ lines.each_with_index do |line, index|
97
+ next if (index == 0) && !@at_line_begin
98
+ left_to_remove = @dedent_level
99
+ remove = 0
100
+
101
+ line.each_char do |char|
102
+ break if left_to_remove <= 0
103
+ case char
104
+ when "\s"
105
+ remove += 1
106
+ left_to_remove -= 1
107
+ when "\t"
108
+ break if TAB_WIDTH * (remove / TAB_WIDTH + 1) > @dedent_level
109
+ remove += 1
110
+ left_to_remove -= TAB_WIDTH
111
+ else
112
+ # no more spaces or tabs
113
+ break
114
+ end
115
+ end
116
+
117
+ lines[index] = line[remove..-1]
118
+ end
119
+
120
+ string = lines.join
121
+
122
+ @at_line_begin = string.end_with?("\n")
123
+
124
+ string
125
+ end
51
126
  end
52
127
  end
53
128
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Opal::Parser::WithRubyLexer < Parser::Ruby30
3
+ class Opal::Parser::WithRubyLexer < Parser::Ruby31
4
4
  include Opal::Parser::DefaultConfig
5
5
  Opal::Parser.default_parser_class = self
6
6
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Opal
4
- REGEXP_START = RUBY_ENGINE == 'opal' ? '^' : '\A'
5
- REGEXP_END = RUBY_ENGINE == 'opal' ? '$' : '\z'
4
+ self::REGEXP_START = RUBY_ENGINE == 'opal' ? '^' : '\A'
5
+ self::REGEXP_END = RUBY_ENGINE == 'opal' ? '$' : '\z'
6
6
 
7
7
  # Unicode characters in ranges
8
8
  # \u0001 - \u002F (blank unicode characters + space + !"#$%&'()*+,-./ chars)
@@ -11,7 +11,7 @@ module Opal
11
11
  # \u0060 (` char)
12
12
  # \u007B - \u007F ({|}~ chars})
13
13
  # are not allowed to be used in identifier in the beggining or middle of its name
14
- FORBIDDEN_STARTING_IDENTIFIER_CHARS = '\u0001-\u002F\u003A-\u0040\u005B-\u005E\u0060\u007B-\u007F'
14
+ self::FORBIDDEN_STARTING_IDENTIFIER_CHARS = '\u0001-\u002F\u003A-\u0040\u005B-\u005E\u0060\u007B-\u007F'
15
15
 
16
16
  # Unicode characters in ranges
17
17
  # \u0001 - \u0020 (blank unicode characters + space)
@@ -23,11 +23,11 @@ module Opal
23
23
  # \u007B - \u007F ({|}~ chars})
24
24
  # are not allowed to be used in identifier in the end of its name
25
25
  # In fact, FORBIDDEN_STARTING_IDENTIFIER_CHARS = FORBIDDEN_ENDING_IDENTIFIER_CHARS + \u0021 ('?') + \u003F ('!')
26
- FORBIDDEN_ENDING_IDENTIFIER_CHARS = '\u0001-\u0020\u0022-\u002F\u003A-\u003E\u0040\u005B-\u005E\u0060\u007B-\u007F'
27
- INLINE_IDENTIFIER_REGEXP = Regexp.new("[^#{FORBIDDEN_STARTING_IDENTIFIER_CHARS}]*[^#{FORBIDDEN_ENDING_IDENTIFIER_CHARS}]")
26
+ self::FORBIDDEN_ENDING_IDENTIFIER_CHARS = '\u0001-\u0020\u0022-\u002F\u003A-\u003E\u0040\u005B-\u005E\u0060\u007B-\u007F'
27
+ self::INLINE_IDENTIFIER_REGEXP = Regexp.new("[^#{self::FORBIDDEN_STARTING_IDENTIFIER_CHARS}]*[^#{self::FORBIDDEN_ENDING_IDENTIFIER_CHARS}]")
28
28
 
29
29
  # For constants rules are pretty much the same, but ':' is allowed and '?!' are not.
30
30
  # Plus it may start with a '::' which indicates that the constant comes from toplevel.
31
- FORBIDDEN_CONST_NAME_CHARS = '\u0001-\u0020\u0021-\u002F\u003B-\u003F\u0040\u005B-\u005E\u0060\u007B-\u007F'
32
- CONST_NAME_REGEXP = Regexp.new("#{REGEXP_START}(::)?[A-Z][^#{FORBIDDEN_CONST_NAME_CHARS}]*#{REGEXP_END}")
31
+ self::FORBIDDEN_CONST_NAME_CHARS = '\u0001-\u0020\u0021-\u002F\u003B-\u003F\u0040\u005B-\u005E\u0060\u007B-\u007F'
32
+ self::CONST_NAME_REGEXP = Regexp.new("#{self::REGEXP_START}(::)?[A-Z][^#{self::FORBIDDEN_CONST_NAME_CHARS}]*#{self::REGEXP_END}")
33
33
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opal/config'
4
+ require 'opal/compiler'
5
+ require 'opal/builder'
6
+ require 'opal/builder_processors'
7
+ require 'opal/erb'
8
+ require 'opal/paths'
9
+ require 'opal/version'
10
+ require 'opal/errors'
11
+ require 'opal/source_map'
12
+ require 'opal/deprecations'
13
+
14
+ # Opal is a ruby to javascript compiler, with a runtime for running
15
+ # in any JavaScript environment.
16
+ module Opal
17
+ autoload :Server, 'opal/server' if RUBY_ENGINE != 'opal'
18
+ autoload :SimpleServer, 'opal/simple_server'
19
+ end
@@ -60,7 +60,7 @@ module Opal
60
60
  # raise NoMatchingPatternError, from
61
61
  def raise_no_matching_pattern_error(from)
62
62
  s(:send, nil, :raise,
63
- s(:const, nil, :NoMatchingPatternError),
63
+ s(:const, s(:cbase), :NoMatchingPatternError),
64
64
  s(:lvar, from)
65
65
  )
66
66
  end
@@ -11,22 +11,120 @@ module Opal
11
11
  "$ret_or_#{@counter}"
12
12
  end
13
13
 
14
+ def free_tmp
15
+ @counter -= 1
16
+ end
17
+
14
18
  def reset_tmp_counter!
15
19
  @counter = nil
16
20
  end
17
21
 
22
+ def on_if(node)
23
+ test, = *node.children
24
+ # The if_test metadata signifies that we don't care about the return value except if it's
25
+ # truthy or falsy. And those tests will be carried out by the respective $truthy helper calls.
26
+ test.meta[:if_test] = true if test
27
+ super
28
+ end
29
+
30
+ def on_case(node)
31
+ lhs, *whens, els = *node.children
32
+ els ||= s(:nil)
33
+ lhs_tmp = next_tmp if lhs
34
+
35
+ out = build_if_from_when(node, lhs, lhs_tmp, whens, els)
36
+ free_tmp if lhs
37
+ out
38
+ end
39
+
40
+ # `a || b` / `a or b`
18
41
  def on_or(node)
19
42
  lhs, rhs = *node.children
20
- lhs_tmp = next_tmp
21
43
 
22
- node.updated(:if, [s(:lvasgn, lhs_tmp, process(lhs)), s(:js_tmp, lhs_tmp), process(rhs)])
44
+ if node.meta[:if_test]
45
+ # Let's forward the if_test to the lhs and rhs - since we don't care about the exact return
46
+ # value of our or, we neither do care about a return value of our lhs or rhs.
47
+ lhs.meta[:if_test] = rhs.meta[:if_test] = true
48
+ out = process(node.updated(:if, [lhs, s(:true), rhs]))
49
+ else
50
+ lhs_tmp = next_tmp
51
+ out = process(node.updated(:if, [s(:lvasgn, lhs_tmp, lhs), s(:js_tmp, lhs_tmp), rhs]))
52
+ free_tmp
53
+ end
54
+ out
23
55
  end
24
56
 
57
+ # `a && b` / `a and b`
25
58
  def on_and(node)
26
59
  lhs, rhs = *node.children
27
- lhs_tmp = next_tmp
28
60
 
29
- node.updated(:if, [s(:lvasgn, lhs_tmp, process(lhs)), process(rhs), s(:js_tmp, lhs_tmp)])
61
+ if node.meta[:if_test]
62
+ lhs.meta[:if_test] = rhs.meta[:if_test] = true
63
+ out = process(node.updated(:if, [lhs, rhs, s(:false)]))
64
+ else
65
+ lhs_tmp = next_tmp
66
+ out = process(node.updated(:if, [s(:lvasgn, lhs_tmp, lhs), rhs, s(:js_tmp, lhs_tmp)]))
67
+ free_tmp
68
+ end
69
+ out
70
+ end
71
+
72
+ # Parser sometimes generates parentheses as a begin node. If it's a single node begin value, then
73
+ # let's forward the if_test metadata.
74
+ def on_begin(node)
75
+ if node.meta[:if_test] && node.children.count == 1
76
+ node.children.first.meta[:if_test] = true
77
+ end
78
+ node.meta.delete(:if_test)
79
+ super
80
+ end
81
+
82
+ private
83
+
84
+ def build_if_from_when(node, lhs, lhs_tmp, whens, els)
85
+ first_when, *next_whens = *whens
86
+
87
+ *parts, expr = *first_when.children
88
+
89
+ rule = build_rule_from_parts(node, lhs, lhs_tmp, parts)
90
+
91
+ first_when.updated(:if, [rule, process(expr), next_whens.empty? ? process(els) : build_if_from_when(nil, nil, lhs_tmp, next_whens, els)])
92
+ end
93
+
94
+ def build_rule_from_parts(node, lhs, lhs_tmp, parts)
95
+ lhs = if node && lhs_tmp
96
+ node.updated(:lvasgn, [lhs_tmp, process(lhs)])
97
+ else
98
+ s(:js_tmp, lhs_tmp)
99
+ end
100
+
101
+ first_part, *next_parts = *parts
102
+
103
+ subrule = if first_part.type == :splat
104
+ splat_on = first_part.children.first
105
+ iter_val = next_tmp
106
+ block = s(:send, process(splat_on), :any?,
107
+ s(:iter,
108
+ s(:args, s(:arg, iter_val)),
109
+ build_rule_from_parts(nil, nil, lhs_tmp, [s(:lvar, iter_val)])
110
+ )
111
+ )
112
+ if node && lhs_tmp
113
+ s(:begin, lhs, block)
114
+ else
115
+ block
116
+ end
117
+ elsif lhs_tmp
118
+ s(:send, process(first_part), :===, lhs)
119
+ else
120
+ process(first_part)
121
+ end
122
+
123
+ if next_parts.empty?
124
+ subrule
125
+ else
126
+ s(:if, subrule, s(:true), build_rule_from_parts(nil, nil, lhs_tmp, next_parts))
127
+ end
30
128
  end
31
129
  end
32
130
  end
data/lib/opal/util.rb CHANGED
@@ -14,8 +14,8 @@ module Opal
14
14
  #
15
15
  # @param str [String] string to minify
16
16
  # @return [String]
17
- def uglify(source)
18
- sh 'bin/yarn -s run terser -c', data: source
17
+ def uglify(source, mangle: false)
18
+ sh "bin/yarn -s run terser -c #{'-m' if mangle}", data: source
19
19
  end
20
20
 
21
21
  # Gzip code to check file size.
data/lib/opal/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  module Opal
4
4
  # WHEN RELEASING:
5
5
  # Remember to update RUBY_ENGINE_VERSION in opal/corelib/constants.rb too!
6
- VERSION = '1.3.1'
6
+ VERSION = '1.4.1'
7
7
  end
data/lib/opal.rb CHANGED
@@ -1,19 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'opal/config'
4
- require 'opal/compiler'
5
- require 'opal/builder'
6
- require 'opal/builder_processors'
7
- require 'opal/erb'
8
- require 'opal/paths'
9
- require 'opal/version'
10
- require 'opal/errors'
11
- require 'opal/source_map'
12
- require 'opal/deprecations'
13
-
14
- # Opal is a ruby to javascript compiler, with a runtime for running
15
- # in any JavaScript environment.
16
- module Opal
17
- autoload :Server, 'opal/server'
18
- autoload :SimpleServer, 'opal/simple_server'
19
- end
3
+ require 'opal/requires'
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'corelib/pack_unpack/format_string_parser'
4
4
 
5
- class Array
5
+ class ::Array
6
6
  %x{
7
7
  // Format Parser
8
8
  var eachDirectiveAndCount = Opal.PackUnpack.eachDirectiveAndCount;
@@ -93,7 +93,7 @@ class Array
93
93
  var buffer = callback(data);
94
94
 
95
95
  return buffer.map(function(item) {
96
- return $coerce_to(item, #{Integer}, 'to_int')
96
+ return $coerce_to(item, #{::Integer}, 'to_int')
97
97
  });
98
98
  }
99
99
  }
@@ -103,7 +103,7 @@ class Array
103
103
  var buffer = callback(data);
104
104
 
105
105
  return buffer.map(function(item) {
106
- return $coerce_to(item, #{String}, 'to_str')
106
+ return $coerce_to(item, #{::String}, 'to_str')
107
107
  });
108
108
  }
109
109
  }
@@ -116,7 +116,7 @@ class Array
116
116
  return String.fromCodePoint(item);
117
117
  } catch (error) {
118
118
  if (error instanceof RangeError) {
119
- #{raise RangeError, 'value out of range'};
119
+ #{::Kernel.raise ::RangeError, 'value out of range'};
120
120
  }
121
121
  throw error;
122
122
  }
@@ -199,7 +199,7 @@ class Array
199
199
  }
200
200
  } else {
201
201
  if (buffer.length < count) {
202
- #{raise ArgumentError, 'too few arguments'};
202
+ #{::Kernel.raise ::ArgumentError, 'too few arguments'};
203
203
  }
204
204
  for (var i = 0; i < count; i++) {
205
205
  chunkData = callback(buffer);
@@ -225,9 +225,9 @@ class Array
225
225
  if (source === nil) {
226
226
  source = '';
227
227
  } else if (source === undefined) {
228
- #{raise ArgumentError, 'too few arguments'};
228
+ #{::Kernel.raise ::ArgumentError, 'too few arguments'};
229
229
  } else {
230
- source = $coerce_to(source, #{String}, 'to_str');
230
+ source = $coerce_to(source, #{::String}, 'to_str');
231
231
  }
232
232
 
233
233
  buffer = buffer.slice(1, buffer.length);
@@ -388,7 +388,7 @@ class Array
388
388
  }
389
389
 
390
390
  def pack(format)
391
- format = Opal.coerce_to!(format, String, :to_str).gsub(/\s/, '').delete("\000")
391
+ format = ::Opal.coerce_to!(format, ::String, :to_str).gsub(/\s/, '').delete("\000")
392
392
 
393
393
  %x{
394
394
  var output = '';
@@ -408,7 +408,7 @@ class Array
408
408
  chunkReader = readChunk[directive];
409
409
 
410
410
  if (chunkReader == null) {
411
- #{raise "Unsupported pack directive #{`directive`.inspect} (no chunk reader defined)"}
411
+ #{::Kernel.raise "Unsupported pack directive #{`directive`.inspect} (no chunk reader defined)"}
412
412
  }
413
413
 
414
414
  var chunkData = chunkReader(buffer, count);
@@ -418,7 +418,7 @@ class Array
418
418
  var handler = handlers[directive];
419
419
 
420
420
  if (handler == null) {
421
- #{raise "Unsupported pack directive #{`directive`.inspect} (no handler defined)"}
421
+ #{::Kernel.raise "Unsupported pack directive #{`directive`.inspect} (no handler defined)"}
422
422
  }
423
423
 
424
424
  return handler(chunk);
@@ -431,7 +431,7 @@ class Array
431
431
  var shouldAutocomplete = autocompletion[directive]
432
432
 
433
433
  if (shouldAutocomplete == null) {
434
- #{raise "Unsupported pack directive #{`directive`.inspect} (no autocompletion rule defined)"}
434
+ #{::Kernel.raise "Unsupported pack directive #{`directive`.inspect} (no autocompletion rule defined)"}
435
435
  }
436
436
 
437
437
  if (shouldAutocomplete) {