opal 1.1.1 → 1.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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