opal 1.1.1 → 1.2.0.beta1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/build.yml +11 -5
  6. data/.gitignore +1 -0
  7. data/.jshintrc +1 -1
  8. data/Gemfile +0 -4
  9. data/HACKING.md +1 -1
  10. data/README.md +19 -15
  11. data/UNRELEASED.md +41 -0
  12. data/benchmark-ips/bm_array_unshift.rb +7 -0
  13. data/bin/opal-mspec +2 -0
  14. data/docs/compiler.md +1 -1
  15. data/examples/rack/Gemfile +0 -1
  16. data/examples/rack/Gemfile.lock +0 -4
  17. data/lib/opal/cli.rb +1 -0
  18. data/lib/opal/cli_options.rb +4 -0
  19. data/lib/opal/cli_runners/nodejs.rb +4 -0
  20. data/lib/opal/cli_runners/source-map-support-browser.js +3 -1
  21. data/lib/opal/cli_runners/source-map-support-node.js +3 -1
  22. data/lib/opal/cli_runners/source-map-support.js +3 -1
  23. data/lib/opal/compiler.rb +2 -2
  24. data/lib/opal/nodes/args/arity_check.rb +1 -0
  25. data/lib/opal/nodes/args/parameters.rb +6 -0
  26. data/lib/opal/nodes/class.rb +1 -13
  27. data/lib/opal/nodes/literal.rb +14 -7
  28. data/lib/opal/nodes/module.rb +13 -9
  29. data/lib/opal/nodes/variables.rb +13 -4
  30. data/lib/opal/nodes/while.rb +54 -17
  31. data/lib/opal/parser.rb +1 -5
  32. data/lib/opal/parser/patch.rb +34 -0
  33. data/lib/opal/repl.rb +7 -0
  34. data/lib/opal/rewriter.rb +2 -0
  35. data/lib/opal/rewriters/arguments.rb +4 -1
  36. data/lib/opal/rewriters/forward_args.rb +54 -0
  37. data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
  38. data/lib/opal/rewriters/opal_engine_check.rb +5 -7
  39. data/lib/opal/version.rb +1 -1
  40. data/opal/corelib/array.rb +42 -20
  41. data/opal/corelib/array/pack.rb +6 -1
  42. data/opal/corelib/complex.rb +2 -0
  43. data/opal/corelib/constants.rb +3 -3
  44. data/opal/corelib/hash.rb +36 -38
  45. data/opal/corelib/module.rb +2 -7
  46. data/opal/corelib/number.rb +2 -180
  47. data/opal/corelib/numeric.rb +156 -0
  48. data/opal/corelib/object_space.rb +102 -0
  49. data/opal/corelib/random.rb +31 -66
  50. data/opal/corelib/random/formatter.rb +122 -0
  51. data/opal/corelib/range.rb +50 -19
  52. data/opal/corelib/runtime.js +82 -21
  53. data/opal/corelib/string.rb +86 -52
  54. data/opal/corelib/string/encoding.rb +140 -25
  55. data/opal/corelib/string/unpack.rb +26 -40
  56. data/opal/opal.rb +1 -0
  57. data/opal/opal/full.rb +1 -0
  58. data/spec/filters/bugs/array.rb +0 -22
  59. data/spec/filters/bugs/basicobject.rb +3 -0
  60. data/spec/filters/bugs/encoding.rb +0 -2
  61. data/spec/filters/bugs/exception.rb +1 -0
  62. data/spec/filters/bugs/float.rb +0 -2
  63. data/spec/filters/bugs/hash.rb +2 -7
  64. data/spec/filters/bugs/integer.rb +0 -2
  65. data/spec/filters/bugs/kernel.rb +16 -3
  66. data/spec/filters/bugs/language.rb +6 -14
  67. data/spec/filters/bugs/marshal.rb +1 -3
  68. data/spec/filters/bugs/module.rb +16 -1
  69. data/spec/filters/bugs/numeric.rb +4 -12
  70. data/spec/filters/bugs/objectspace.rb +67 -0
  71. data/spec/filters/bugs/pack_unpack.rb +0 -9
  72. data/spec/filters/bugs/pathname.rb +1 -0
  73. data/spec/filters/bugs/proc.rb +8 -0
  74. data/spec/filters/bugs/random.rb +3 -6
  75. data/spec/filters/bugs/range.rb +83 -113
  76. data/spec/filters/bugs/set.rb +2 -0
  77. data/spec/filters/bugs/string.rb +31 -70
  78. data/spec/filters/bugs/struct.rb +2 -0
  79. data/spec/filters/bugs/time.rb +8 -2
  80. data/spec/filters/unsupported/float.rb +3 -0
  81. data/spec/filters/unsupported/freeze.rb +1 -0
  82. data/spec/filters/unsupported/integer.rb +3 -0
  83. data/spec/filters/unsupported/refinements.rb +5 -0
  84. data/spec/filters/unsupported/string.rb +100 -95
  85. data/spec/filters/unsupported/time.rb +4 -0
  86. data/spec/lib/compiler_spec.rb +16 -0
  87. data/spec/lib/rewriters/forward_args_spec.rb +61 -0
  88. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
  89. data/spec/lib/rewriters/numblocks_spec.rb +44 -0
  90. data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
  91. data/spec/opal/core/language/forward_args_spec.rb +53 -0
  92. data/spec/opal/core/language/infinite_range_spec.rb +13 -0
  93. data/spec/opal/core/language/memoization_spec.rb +16 -0
  94. data/spec/opal/core/module_spec.rb +38 -2
  95. data/spec/opal/core/number/to_i_spec.rb +28 -0
  96. data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
  97. data/spec/opal/core/runtime/constants_spec.rb +20 -1
  98. data/spec/opal/core/string/subclassing_spec.rb +16 -0
  99. data/spec/opal/core/string/unpack_spec.rb +22 -0
  100. data/spec/opal/core/string_spec.rb +4 -4
  101. data/spec/ruby_specs +4 -1
  102. data/stdlib/json.rb +3 -1
  103. data/stdlib/securerandom.rb +55 -35
  104. data/tasks/testing.rake +6 -3
  105. data/test/nodejs/test_string.rb +25 -0
  106. data/vendored-minitest/minitest/assertions.rb +2 -0
  107. metadata +31 -10
  108. data/lib/opal/parser/with_c_lexer.rb +0 -15
data/lib/opal/compiler.rb CHANGED
@@ -151,7 +151,7 @@ module Opal
151
151
 
152
152
  # @!method use_strict?
153
153
  #
154
- # Adds source_location for every method definition
154
+ # Enables JavaScript's strict mode (i.e., adds 'use strict'; statement)
155
155
  compiler_option :use_strict, default: false, as: :use_strict?, magic_comment: true
156
156
 
157
157
  # @!method parse_comments?
@@ -358,7 +358,7 @@ module Opal
358
358
  def in_while
359
359
  return unless block_given?
360
360
  @while_loop = @scope.push_while
361
- result = indent { yield }
361
+ result = yield
362
362
  @scope.pop_while
363
363
  result
364
364
  end
@@ -21,6 +21,7 @@ module Opal
21
21
  @kwargs = arguments.kwargs
22
22
  @kwoptargs = arguments.kwoptargs
23
23
  @kwrestarg = arguments.kwrestarg
24
+ @kwnilarg = arguments.kwnilarg
24
25
  end
25
26
 
26
27
  def compile
@@ -30,6 +30,7 @@ module Opal
30
30
 
31
31
  def on_restarg(arg_name = nil)
32
32
  if arg_name
33
+ arg_name = :* if arg_name == :fwd_rest_arg
33
34
  %{['rest', '#{arg_name}']}
34
35
  else
35
36
  %{['rest']}
@@ -53,9 +54,14 @@ module Opal
53
54
  end
54
55
 
55
56
  def on_blockarg(arg_name)
57
+ arg_name = :& if arg_name == :fwd_block_arg
56
58
  %{['block', '#{arg_name}']}
57
59
  end
58
60
 
61
+ def on_kwnilarg
62
+ %{['nokey']}
63
+ end
64
+
59
65
  def on_shadowarg(_arg_name); end
60
66
  end
61
67
  end
@@ -15,28 +15,16 @@ module Opal
15
15
 
16
16
  push '(function($base, $super, $parent_nesting) {'
17
17
  line " var self = $klass($base, $super, '#{name}');"
18
-
19
18
  in_scope do
20
19
  scope.name = name
21
- add_temp '$nesting = [self].concat($parent_nesting)'
22
-
23
- body_code = self.body_code
24
- empty_line
25
-
26
- line scope.to_vars
27
- line body_code
20
+ compile_body
28
21
  end
29
-
30
22
  line '})(', base, ', ', super_code, ', $nesting)'
31
23
  end
32
24
 
33
25
  def super_code
34
26
  sup ? expr(sup) : 'null'
35
27
  end
36
-
37
- def body_code
38
- stmt(compiler.returns(body || s(:nil)))
39
- end
40
28
  end
41
29
  end
42
30
  end
@@ -70,6 +70,11 @@ module Opal
70
70
  wrap "$enc(", ", \"#{encoding.name}\")"
71
71
  end
72
72
  end
73
+
74
+ unless value.valid_encoding?
75
+ helper :binary
76
+ wrap "$binary(", ")"
77
+ end
73
78
  end
74
79
 
75
80
  # http://www.2ality.com/2013/09/javascript-unicode.html
@@ -253,9 +258,11 @@ module Opal
253
258
  end
254
259
 
255
260
  def compile_inline?
256
- start.type == finish.type &&
257
- SIMPLE_CHILDREN_TYPES.include?(start.type) &&
258
- SIMPLE_CHILDREN_TYPES.include?(finish.type)
261
+ (
262
+ !start || (start.type && SIMPLE_CHILDREN_TYPES.include?(start.type))
263
+ ) && (
264
+ !finish || (finish.type && SIMPLE_CHILDREN_TYPES.include?(finish.type))
265
+ )
259
266
  end
260
267
 
261
268
  def compile_inline
@@ -271,11 +278,11 @@ module Opal
271
278
  handle :irange
272
279
 
273
280
  def compile_inline
274
- push '$range(', expr(start), ', ', expr(finish), ', false)'
281
+ push '$range(', expr_or_nil(start), ', ', expr_or_nil(finish), ', false)'
275
282
  end
276
283
 
277
284
  def compile_range_initialize
278
- push 'Opal.Range.$new(', expr(start), ', ', expr(finish), ', false)'
285
+ push 'Opal.Range.$new(', expr_or_nil(start), ', ', expr_or_nil(finish), ', false)'
279
286
  end
280
287
  end
281
288
 
@@ -283,11 +290,11 @@ module Opal
283
290
  handle :erange
284
291
 
285
292
  def compile_inline
286
- push '$range(', expr(start), ', ', expr(finish), ', true)'
293
+ push '$range(', expr_or_nil(start), ', ', expr_or_nil(finish), ', true)'
287
294
  end
288
295
 
289
296
  def compile_range_initialize
290
- push 'Opal.Range.$new(', expr(start), ',', expr(finish), ', true)'
297
+ push 'Opal.Range.$new(', expr_or_nil(start), ',', expr_or_nil(finish), ', true)'
291
298
  end
292
299
  end
293
300
 
@@ -15,21 +15,15 @@ module Opal
15
15
 
16
16
  push '(function($base, $parent_nesting) {'
17
17
  line " var self = $module($base, '#{name}');"
18
-
19
18
  in_scope do
20
19
  scope.name = name
21
- add_temp '$nesting = [self].concat($parent_nesting)'
22
-
23
- body_code = stmt(body || s(:nil))
24
- empty_line
25
-
26
- line scope.to_vars
27
- line body_code
20
+ compile_body
28
21
  end
29
-
30
22
  line '})(', base, ', $nesting)'
31
23
  end
32
24
 
25
+ private
26
+
33
27
  # cid is always s(:const, scope_sexp_or_nil, :ConstName)
34
28
  def name_and_base
35
29
  base, name = cid.children
@@ -40,6 +34,16 @@ module Opal
40
34
  [name, expr(base)]
41
35
  end
42
36
  end
37
+
38
+ def compile_body
39
+ add_temp '$nesting = [self].concat($parent_nesting)'
40
+
41
+ body_code = stmt(compiler.returns(body || s(:nil)))
42
+ empty_line
43
+
44
+ line scope.to_vars
45
+ line body_code
46
+ end
43
47
  end
44
48
  end
45
49
  end
@@ -194,9 +194,16 @@ module Opal
194
194
  children :name
195
195
 
196
196
  def compile
197
- with_temp do |tmp|
198
- push "((#{tmp} = #{class_variable_owner}.$$cvars['#{name}']) == null ? nil : #{tmp})"
199
- end
197
+ helper :class_variable_get
198
+
199
+ tolerant = false
200
+ # We should be tolerant of expressions like: def x; @@notexist; 0; end
201
+ # (NB: Shouldn't we actually optimize them out?)
202
+ tolerant = true if stmt?
203
+ # We should be tolerant of expressions like: @@notexist ||= 6
204
+ # (those are handled with logical_operator_assignment)
205
+
206
+ push "$class_variable_get(#{class_variable_owner}, '#{name}', #{tolerant.inspect})"
200
207
  end
201
208
  end
202
209
 
@@ -206,7 +213,9 @@ module Opal
206
213
  children :name, :value
207
214
 
208
215
  def compile
209
- push "(Opal.class_variable_set(#{class_variable_owner}, '#{name}', ", expr(value), '))'
216
+ helper :class_variable_set
217
+
218
+ push "$class_variable_set(#{class_variable_owner}, '#{name}', ", expr(value), ')'
210
219
  end
211
220
  end
212
221
  end
@@ -10,32 +10,45 @@ module Opal
10
10
  children :test, :body
11
11
 
12
12
  def compile
13
- with_temp do |redo_var|
14
- test_code = js_truthy(test)
13
+ test_code = js_truthy(test)
15
14
 
15
+ with_temp do |redo_var|
16
16
  compiler.in_while do
17
17
  while_loop[:closure] = true if wrap_in_closure?
18
18
  while_loop[:redo_var] = redo_var
19
19
 
20
- body_code = stmt(body)
21
-
20
+ body_code = indent { stmt(body) }
22
21
  if uses_redo?
23
- push "#{redo_var} = false; #{while_open}#{redo_var} || "
24
- push test_code
25
- push while_close
22
+ compile_with_redo(test_code, body_code, redo_var)
26
23
  else
27
- push while_open, test_code, while_close
24
+ compile_without_redo(test_code, body_code)
28
25
  end
29
-
30
- push "#{redo_var} = false;" if uses_redo?
31
- line body_code
32
26
  end
33
- line '}'
34
27
  end
35
28
 
36
29
  wrap '(function() {', '; return nil; })()' if wrap_in_closure?
37
30
  end
38
31
 
32
+ private
33
+
34
+ def compile_with_redo(test_code, body_code, redo_var)
35
+ push "#{redo_var} = false; "
36
+ compile_while(
37
+ [redo_var, " || ", test_code],
38
+ ["#{redo_var} = false;", body_code]
39
+ )
40
+ end
41
+
42
+ def compile_without_redo(test_code, body_code)
43
+ compile_while([test_code], [body_code])
44
+ end
45
+
46
+ def compile_while(test_code, body_code)
47
+ push while_open, *test_code, while_close
48
+ indent { line(*body_code) }
49
+ line '}'
50
+ end
51
+
39
52
  def while_open
40
53
  'while ('
41
54
  end
@@ -53,13 +66,11 @@ module Opal
53
66
  end
54
67
  end
55
68
 
56
- class WhilePostNode < WhileNode
57
- handle :while_post
58
- end
59
-
60
69
  class UntilNode < WhileNode
61
70
  handle :until
62
71
 
72
+ private
73
+
63
74
  def while_open
64
75
  'while (!('
65
76
  end
@@ -69,8 +80,34 @@ module Opal
69
80
  end
70
81
  end
71
82
 
72
- class UntilPostNode < UntilNode
83
+ class WhilePostNode < WhileNode
84
+ handle :while_post
85
+
86
+ private
87
+
88
+ def compile_while(test_code, body_code)
89
+ push "do {"
90
+ indent { line(*body_code) }
91
+ line "} ", while_open, *test_code, while_close
92
+ end
93
+
94
+ def while_close
95
+ ');'
96
+ end
97
+ end
98
+
99
+ class UntilPostNode < WhilePostNode
73
100
  handle :until_post
101
+
102
+ private
103
+
104
+ def while_open
105
+ 'while(!('
106
+ end
107
+
108
+ def while_close
109
+ '));'
110
+ end
74
111
  end
75
112
  end
76
113
  end
data/lib/opal/parser.rb CHANGED
@@ -6,8 +6,4 @@ require 'opal/parser/source_buffer'
6
6
  require 'opal/parser/default_config'
7
7
  require 'opal/parser/with_ruby_lexer'
8
8
 
9
- if RUBY_ENGINE == 'opal'
10
- require 'opal/parser/patch'
11
- else
12
- require 'opal/parser/with_c_lexer'
13
- end
9
+ require 'opal/parser/patch'
@@ -39,3 +39,37 @@ if RUBY_ENGINE == 'opal'
39
39
  end
40
40
  end
41
41
  end
42
+
43
+ module AST::Processor::Mixin
44
+ undef process
45
+ # This patch to #process removes a bit of dynamic abilities (removed
46
+ # call to node.to_ast) and it tries to optimize away the string
47
+ # operations and method existence check by caching them inside a
48
+ # processor.
49
+ #
50
+ # This is the second most inefficient call in the compilation phase
51
+ # so an optimization may be warranted.
52
+ def process(node)
53
+ return if node.nil?
54
+
55
+ @_on_handler_cache ||= {}
56
+ type = node.type
57
+
58
+ on_handler = @_on_handler_cache[type] ||= begin
59
+ handler = :"on_#{type}"
60
+ handler = :handler_missing unless respond_to?(handler)
61
+ handler
62
+ end
63
+
64
+ send(on_handler, node) || node
65
+ end
66
+ end
67
+
68
+ class Parser::Builders::Default
69
+ # string_value raises on invalid UTF-8 strings, like "\x80",
70
+ # otherwise it's the same as value.
71
+ undef string_value
72
+ def string_value(token)
73
+ value(token)
74
+ end
75
+ end
data/lib/opal/repl.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'opal'
4
+ require 'securerandom'
4
5
 
5
6
  module Opal
6
7
  class REPL
@@ -38,9 +39,15 @@ module Opal
38
39
  eval_ruby File.read(filename)
39
40
  end
40
41
 
42
+ # A polyfill so that SecureRandom works in repl correctly.
43
+ def random_bytes(bytes)
44
+ ::SecureRandom.bytes(bytes).split('').map(&:ord)
45
+ end
46
+
41
47
  def load_opal
42
48
  v8.attach('console.log', method(:puts).to_proc)
43
49
  v8.attach('console.warn', method(:warn).to_proc)
50
+ v8.attach('crypto.randomBytes', method(:random_bytes).to_proc)
44
51
  v8.eval Opal::Builder.new.build('opal').to_s
45
52
  v8.attach('Opal.exit', method(:exit).to_proc)
46
53
  end
data/lib/opal/rewriter.rb CHANGED
@@ -14,6 +14,7 @@ require 'opal/rewriters/mlhs_args'
14
14
  require 'opal/rewriters/inline_args'
15
15
  require 'opal/rewriters/numblocks'
16
16
  require 'opal/rewriters/returnable_logic'
17
+ require 'opal/rewriters/forward_args'
17
18
 
18
19
  module Opal
19
20
  class Rewriter
@@ -45,6 +46,7 @@ module Opal
45
46
  use Rewriters::OpalEngineCheck
46
47
  use Rewriters::ForRewriter
47
48
  use Rewriters::Numblocks
49
+ use Rewriters::ForwardArgs
48
50
  use Rewriters::BlockToIter
49
51
  use Rewriters::DotJsSyntax
50
52
  use Rewriters::JsReservedWords
@@ -4,7 +4,7 @@ module Opal
4
4
  module Rewriters
5
5
  class Arguments
6
6
  attr_reader :args, :optargs, :restarg, :postargs,
7
- :kwargs, :kwoptargs, :kwrestarg,
7
+ :kwargs, :kwoptargs, :kwrestarg, :kwnilarg,
8
8
  :shadowargs, :blockarg
9
9
 
10
10
  def initialize(args)
@@ -15,6 +15,7 @@ module Opal
15
15
  @kwargs = []
16
16
  @kwoptargs = []
17
17
  @kwrestarg = nil
18
+ @kwnilarg = false
18
19
  @shadowargs = []
19
20
  @blockarg = nil
20
21
 
@@ -30,6 +31,8 @@ module Opal
30
31
  @kwargs << arg
31
32
  when :kwoptarg
32
33
  @kwoptargs << arg
34
+ when :kwnilarg
35
+ @kwnilarg = true
33
36
  when :kwrestarg
34
37
  @kwrestarg = arg
35
38
  when :shadowarg
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opal/rewriters/base'
4
+
5
+ module Opal
6
+ module Rewriters
7
+ class ForwardArgs < Base
8
+ def on_forward_args(_node)
9
+ process(
10
+ s(:args, s(:forward_arg, :"$"))
11
+ )
12
+ end
13
+
14
+ def on_args(node)
15
+ if node.children.last && node.children.last.type == :forward_arg
16
+ prev_children = node.children[0..-2]
17
+
18
+ node.updated(nil,
19
+ [
20
+ *prev_children,
21
+ s(:restarg, '$fwd_rest'),
22
+ s(:blockarg, '$fwd_block')
23
+ ]
24
+ )
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def on_send(node)
31
+ if node.children.last &&
32
+ node.children.last.class != Symbol &&
33
+ node.children.last.type == :forwarded_args
34
+
35
+ prev_children = node.children[0..-2]
36
+
37
+ node.updated(nil,
38
+ [
39
+ *prev_children,
40
+ s(:splat,
41
+ s(:lvar, '$fwd_rest')
42
+ ),
43
+ s(:block_pass,
44
+ s(:lvar, '$fwd_block')
45
+ )
46
+ ]
47
+ )
48
+ else
49
+ super
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end