opal 0.10.0.beta2 → 0.10.0.beta3

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/lib/opal/compiler.rb +15 -9
  4. data/lib/opal/fragment.rb +8 -1
  5. data/lib/opal/nodes/args/restarg.rb +6 -1
  6. data/lib/opal/nodes/base.rb +1 -1
  7. data/lib/opal/nodes/call.rb +4 -0
  8. data/lib/opal/nodes/def.rb +20 -25
  9. data/lib/opal/nodes/hash.rb +89 -17
  10. data/lib/opal/nodes/iter.rb +30 -2
  11. data/lib/opal/nodes/logic.rb +54 -4
  12. data/lib/opal/nodes/node_with_args.rb +72 -0
  13. data/lib/opal/parser.rb +16 -0
  14. data/lib/opal/parser/grammar.rb +2555 -2562
  15. data/lib/opal/parser/grammar.y +28 -20
  16. data/lib/opal/parser/lexer.rb +4 -4
  17. data/lib/opal/regexp_anchors.rb +13 -5
  18. data/lib/opal/source_map.rb +2 -1
  19. data/lib/opal/version.rb +1 -1
  20. data/opal/corelib/array.rb +4 -0
  21. data/opal/corelib/basic_object.rb +3 -1
  22. data/opal/corelib/constants.rb +1 -1
  23. data/opal/corelib/file.rb +196 -4
  24. data/opal/corelib/hash.rb +7 -7
  25. data/opal/corelib/kernel.rb +7 -4
  26. data/opal/corelib/marshal.rb +31 -0
  27. data/opal/corelib/marshal/read_buffer.rb +427 -0
  28. data/opal/corelib/marshal/write_buffer.rb +383 -0
  29. data/opal/corelib/method.rb +8 -0
  30. data/opal/corelib/module.rb +21 -0
  31. data/opal/corelib/number.rb +19 -5
  32. data/opal/corelib/proc.rb +33 -6
  33. data/opal/corelib/range.rb +6 -0
  34. data/opal/corelib/regexp.rb +5 -1
  35. data/opal/corelib/runtime.js +69 -17
  36. data/opal/corelib/string.rb +8 -0
  37. data/opal/corelib/string/inheritance.rb +4 -0
  38. data/opal/corelib/struct.rb +5 -0
  39. data/opal/corelib/unsupported.rb +0 -18
  40. data/opal/opal/full.rb +1 -0
  41. data/spec/filters/bugs/basicobject.rb +0 -2
  42. data/spec/filters/bugs/compiler_opal.rb +5 -0
  43. data/spec/filters/bugs/enumerable.rb +1 -0
  44. data/spec/filters/bugs/enumerator.rb +0 -2
  45. data/spec/filters/bugs/exception.rb +0 -1
  46. data/spec/filters/bugs/kernel.rb +0 -5
  47. data/spec/filters/bugs/language.rb +7 -27
  48. data/spec/filters/bugs/marshal.rb +43 -0
  49. data/spec/filters/bugs/method.rb +0 -56
  50. data/spec/filters/bugs/module.rb +0 -1
  51. data/spec/filters/bugs/proc.rb +0 -46
  52. data/spec/filters/bugs/regexp.rb +1 -0
  53. data/spec/filters/bugs/unboundmethod.rb +0 -13
  54. data/spec/filters/unsupported/bignum.rb +5 -0
  55. data/spec/filters/unsupported/freeze.rb +2 -0
  56. data/spec/filters/unsupported/marshal.rb +46 -0
  57. data/spec/filters/unsupported/symbol.rb +5 -0
  58. data/spec/lib/compiler/call_spec.rb +29 -29
  59. data/spec/lib/compiler_spec.rb +7 -1
  60. data/spec/opal/core/kernel/instance_variables_spec.rb +40 -0
  61. data/spec/opal/core/language/ternary_operator_spec.rb +6 -0
  62. data/spec/opal/core/marshal/dump_spec.rb +53 -0
  63. data/spec/opal/core/marshal/load_spec.rb +7 -0
  64. data/spec/opal/core/source_map_spec.rb +35 -1
  65. data/spec/opal/javascript_api_spec.rb +16 -0
  66. data/spec/opal/stdlib/source_map_spec.rb +8 -0
  67. data/spec/ruby_specs +7 -4
  68. data/spec/support/match_helpers.rb +57 -0
  69. data/spec/support/mspec_rspec_adapter.rb +1 -1
  70. data/stdlib/opal-parser.rb +3 -1
  71. data/stdlib/pathname.rb +105 -7
  72. data/stdlib/racc/parser.rb +551 -138
  73. data/stdlib/source_map/vlq.rb +3 -2
  74. data/tasks/testing.rake +4 -2
  75. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0d30f54134e49ebcd91c2ece0dd0cf54a6d72ae
4
- data.tar.gz: 57d3c8e202237d2e452523b1d772d8ddfe3baa24
3
+ metadata.gz: 6e7bea1b676089d0921e603acafc794fe81fb671
4
+ data.tar.gz: 798d0eef513a0f7a17eb283d810342ffddb64b03
5
5
  SHA512:
6
- metadata.gz: 0417410d72d8b82f366042085243ccd06d6d4394e46dbe507a3834e9df68d2ec5c76585b660466888ea6d3e8618e96f551e1e3482c3e66b0f270f5a9c4051482
7
- data.tar.gz: da1d154ccf0f0ae319bed44b962e2ef6a588af39d22ede501cd7af7c7d8f095c9223bee1ecf0bafdf6b8500f605589d7c2b814daaf841b9caf5707ea6d0602a4
6
+ metadata.gz: c349f1c93f09a28b2e0c9285008901d985c4196548171318fadcd416e370e9dc2bcbc4d8b1e184b5e4646206e8c64efee28c58d8a4e11a0939eceb21f929912c
7
+ data.tar.gz: 5af01ed324f7c9f0eec062d465d34e051d47bca3e24e33c72e17e6634a7b87cab060f303b3408a5644763e838c3051d29964f8e6d461408f8a18cd2726ceacfa
@@ -26,6 +26,7 @@ Whitespace conventions:
26
26
 
27
27
  ### Added
28
28
 
29
+ - Source maps now include method names
29
30
  - `Module#included_modules` works
30
31
  - Internal runtime cleanup (#1241)
31
32
  - Make it easier to add custom runners for the CLI (#1261)
@@ -50,6 +51,7 @@ Whitespace conventions:
50
51
  - Super works with define_method blocks
51
52
  - Added support for kwsplats.
52
53
  - Added support for squiggly heredoc.
54
+ - Implement `Method#parameters` and `Proc#parameters`.
53
55
 
54
56
 
55
57
  ### Changed
@@ -57,8 +59,9 @@ Whitespace conventions:
57
59
  - Remove deprecation of `Opal::Environment` after popular request
58
60
  - Setting `Opal::Config.dynamic_require_severity` will no longer affect `Opal.dynamic_require_severity` which now needs to be explicitly set if it differs from the default value of `"warning"` (See also the `Opal.dynamic_require_severity` rename below).
59
61
  - The new default for `Opal::Config.dynamic_require_severity` is now `:warning`
60
- - `Opal.dynamic_require_severity` and `OPAL_CONFIG` are now merged into `Opal.config.missing_require_severity` (defaults to `error`, the expected ruby behavior) and `Opal.config.unsupported_features_severity` (defaults to `warning`, e.g. a one-time heads up that freezing isn't supported)
62
+ - `Opal.dynamic_require_severity` and `OPAL_CONFIG` are now merged into `Opal.config.missing_require_severity` (defaults to `error`, the expected ruby behavior) and `Opal.config.unsupported_features_severity` (defaults to `warning`, e.g. a one-time heads up that freezing isn't supported). Added variable `__OPAL_COMPILER_CONFIG__` that contains compiler options that may be used in runtime.
61
63
  - `Hash` instances should now list the string map (`$$smap`) as the first key, making debugging easier (most hashes will just have keys there).
64
+ - Handle Pathname object in Pathname constructor
62
65
 
63
66
 
64
67
  ### Deprecated
@@ -75,6 +78,10 @@ Whitespace conventions:
75
78
 
76
79
  ### Fixed
77
80
 
81
+ - `Pathname#absolute?` and `Pathname#relative?` now work properly
82
+ - `File::dirname` and `File::basename` are now Rubyspec compliant
83
+ - `SourceMap::VLQ` patch (#1075)
84
+ - `Regexp::new` no longer throws error when the expression ends in \\\\
78
85
  - `super` works properly with overwritten alias methods (#1384)
79
86
  - `NoMethodError` does not need a name to be instantiated
80
87
  - `method_added` fix for singleton class cases
@@ -106,6 +113,7 @@ Whitespace conventions:
106
113
  - Fixed an issue with `"-"` inside the second arg of `String#tr`
107
114
  - Fixed Base64 and enabled specs
108
115
  - Fixed method definition in method body.
116
+ - Partially implemented `Marshal.load`/`Marshal.dump`. In order to use it require `opal/full`.
109
117
 
110
118
 
111
119
  ### Removed
@@ -106,7 +106,7 @@ module Opal
106
106
  # @!method dynamic_require_severity
107
107
  #
108
108
  # how to handle dynamic requires (:error, :warning, :ignore)
109
- compiler_option :dynamic_require_severity, :error, :valid_values => [:error, :warning, :ignore]
109
+ compiler_option :dynamic_require_severity, :warning, :valid_values => [:error, :warning, :ignore]
110
110
 
111
111
  # @!method requirable?
112
112
  #
@@ -148,13 +148,19 @@ module Opal
148
148
  def compile
149
149
  @parser = Parser.new
150
150
 
151
- @sexp = s(:top, @parser.parse(@source, self.file) || s(:nil))
151
+ parsed = begin
152
+ @parser.parse(@source, self.file)
153
+ rescue => error
154
+ raise SyntaxError, error.message, error.backtrace
155
+ end
156
+
157
+ @sexp = s(:top, parsed || s(:nil))
152
158
  @eof_content = @parser.lexer.eof_content
153
159
 
154
160
  @fragments = process(@sexp).flatten
155
161
 
156
162
  @result = @fragments.map(&:code).join('')
157
- rescue => error
163
+ rescue Exception => error
158
164
  message = "An error occurred while compiling: #{self.file}\n#{error.message}"
159
165
  raise error.class, message, error.backtrace
160
166
  end
@@ -215,8 +221,8 @@ module Opal
215
221
  Sexp.new(parts)
216
222
  end
217
223
 
218
- def fragment(str, sexp = nil)
219
- Fragment.new(str, sexp)
224
+ def fragment(str, scope, sexp = nil)
225
+ Fragment.new(str, scope, sexp)
220
226
  end
221
227
 
222
228
  # Used to generate a unique id name per file. These are used
@@ -320,7 +326,7 @@ module Opal
320
326
  # Process the given sexp by creating a node instance, based on its type,
321
327
  # and compiling it to fragments.
322
328
  def process(sexp, level = :expr)
323
- return fragment('') if sexp == nil
329
+ return fragment('', scope) if sexp == nil
324
330
 
325
331
  if handler = handlers[sexp.type]
326
332
  return handler.new(sexp, level, self).compile_to_fragments
@@ -428,11 +434,11 @@ module Opal
428
434
  def handle_block_given_call(sexp)
429
435
  @scope.uses_block!
430
436
  if @scope.block_name
431
- fragment("(#{@scope.block_name} !== nil)", sexp)
437
+ fragment("(#{@scope.block_name} !== nil)", scope, sexp)
432
438
  elsif scope = @scope.find_parent_def and scope.block_name
433
- fragment("(#{scope.block_name} !== nil)", sexp)
439
+ fragment("(#{scope.block_name} !== nil)", scope, sexp)
434
440
  else
435
- fragment("false", sexp)
441
+ fragment("false", scope, sexp)
436
442
  end
437
443
  end
438
444
  end
@@ -15,9 +15,10 @@ module Opal
15
15
  #
16
16
  # @param code [String] javascript code
17
17
  # @param sexp [Opal::Sexp] sexp used for creating fragment
18
- def initialize(code, sexp = nil)
18
+ def initialize(code, scope, sexp = nil)
19
19
  @code = code.to_s
20
20
  @sexp = sexp
21
+ @scope = scope
21
22
  end
22
23
 
23
24
  # Inspect the contents of this fragment, f("fooo")
@@ -25,6 +26,12 @@ module Opal
25
26
  "f(#{@code.inspect})"
26
27
  end
27
28
 
29
+ def source_map_name
30
+ return nil unless @scope
31
+ def_node = @scope.def? ? @scope : @scope.find_parent_def
32
+ def_node && def_node.mid
33
+ end
34
+
28
35
  # Original line this fragment was created from
29
36
  # @return [Integer, nil]
30
37
  def line
@@ -25,7 +25,12 @@ module Opal
25
25
  # inline restarg case
26
26
  offset = @sexp.meta[:offset]
27
27
  # restarg value should be taken directly from parameters
28
- line "#{var_name} = $slice.call(arguments, #{offset}, arguments.length);"
28
+ line "var $args_len = arguments.length, $rest_len = $args_len - #{offset};"
29
+ line "if ($rest_len < 0) { $rest_len = 0; }"
30
+ line "#{var_name} = new Array($rest_len);"
31
+ line "for (var $arg_idx = #{offset}; $arg_idx < $args_len; $arg_idx++) {"
32
+ line " #{var_name}[$arg_idx - #{offset}] = arguments[$arg_idx];"
33
+ line "}"
29
34
  end
30
35
  end
31
36
  end
@@ -72,7 +72,7 @@ module Opal
72
72
  end
73
73
 
74
74
  def fragment(str)
75
- Opal::Fragment.new str, @sexp
75
+ Opal::Fragment.new str, scope, @sexp
76
76
  end
77
77
 
78
78
  def error(msg)
@@ -291,6 +291,10 @@ module Opal
291
291
  push fragment 'debugger'
292
292
  end
293
293
 
294
+ add_special :__OPAL_COMPILER_CONFIG__ do
295
+ push fragment "Opal.hash({ arity_check: #{compiler.arity_check?} })"
296
+ end
297
+
294
298
  class DependencyResolver
295
299
  def initialize(compiler, sexp)
296
300
  @compiler = compiler
@@ -12,21 +12,10 @@ module Opal
12
12
 
13
13
  def extract_block_arg
14
14
  if args.last.is_a?(Sexp) && args.last.type == :blockarg
15
- @block_arg = args.pop
15
+ @block_arg = args.pop[1]
16
16
  end
17
17
  end
18
18
 
19
- def argc
20
- return @argc if @argc
21
-
22
- @argc = args.length - 1
23
- @argc -= 1 if block_arg
24
- @argc -= 1 if rest_arg
25
- @argc -= keyword_args.size
26
-
27
- @argc
28
- end
29
-
30
19
  def compile
31
20
  extract_block_arg
32
21
  split_args
@@ -36,11 +25,7 @@ module Opal
36
25
 
37
26
  # block name (&block)
38
27
  if block_arg
39
- block_name = variable(block_arg[1]).to_sym
40
- end
41
-
42
- if compiler.arity_check?
43
- arity_code = arity_check(args, opt_args, rest_arg, keyword_args, block_name, mid)
28
+ block_name = variable(block_arg).to_sym
44
29
  end
45
30
 
46
31
  in_scope do
@@ -62,6 +47,12 @@ module Opal
62
47
  compile_inline_args
63
48
  compile_post_args
64
49
 
50
+ scope.identify!
51
+
52
+ if compiler.arity_check?
53
+ arity_code = arity_check(mid)
54
+ end
55
+
65
56
  scope_name = scope.identity
66
57
 
67
58
  compile_block_arg
@@ -100,6 +91,12 @@ module Opal
100
91
  unshift "#{scope_name} = " if scope_name
101
92
  line "}"
102
93
 
94
+ push ", #{scope_name}.$$arity = #{arity}"
95
+
96
+ if compiler.arity_check?
97
+ push ", #{scope_name}.$$parameters = #{parameters_code}"
98
+ end
99
+
103
100
  if recvr
104
101
  unshift 'Opal.defs(', recv(recvr), ", '$#{mid}', "
105
102
  push ')'
@@ -130,18 +127,17 @@ module Opal
130
127
  end
131
128
 
132
129
  # Returns code used in debug mode to check arity of method call
133
- def arity_check(args, opt, splat, kwargs, block_name, mid)
130
+ def arity_check(mid)
134
131
  meth = mid.to_s.inspect
135
132
 
136
133
  arity = args.size - 1
137
- arity -= (opt.size)
134
+ arity -= (opt_args.size)
138
135
 
139
- arity -= 1 if splat
136
+ arity -= 1 if rest_arg
140
137
 
141
- arity -= (kwargs.size)
138
+ arity -= (keyword_args.size)
142
139
 
143
- # arity -= 1 if block_name
144
- arity = -arity - 1 if !opt.empty? or !kwargs.empty? or splat
140
+ arity = -arity - 1 if !opt_args.empty? or !keyword_args.empty? or rest_arg
145
141
 
146
142
  # $arity will point to our received arguments count
147
143
  aritycode = "var $arity = arguments.length;"
@@ -149,10 +145,9 @@ module Opal
149
145
  if arity < 0 # splat or opt args
150
146
  min_arity = -(arity + 1)
151
147
  max_arity = args.size - 1
152
- # max_arity -= 1 if block_name
153
148
  checks = []
154
149
  checks << "$arity < #{min_arity}" if min_arity > 0
155
- checks << "$arity > #{max_arity}" if max_arity and not(splat)
150
+ checks << "$arity > #{max_arity}" if max_arity and not(rest_arg)
156
151
  aritycode + "if (#{checks.join(' || ')}) { Opal.ac($arity, #{arity}, this, #{meth}); }" if checks.size > 0
157
152
  else
158
153
  aritycode + "if ($arity !== #{arity}) { Opal.ac($arity, #{arity}, this, #{meth}); }"
@@ -5,40 +5,101 @@ module Opal
5
5
  class HashNode < Base
6
6
  handle :hash
7
7
 
8
- def extract_keys_and_values(pairs)
9
- keys, values = [], []
8
+ attr_accessor :has_kwsplat, :keys, :values
10
9
 
11
- pairs.each_with_index do |obj, idx|
10
+ def initialize(*)
11
+ super
12
+ @has_kwsplat = false
13
+ @keys = []
14
+ @values = []
15
+ end
16
+
17
+ # Splits keys/values/kwsplats
18
+ #
19
+ # hash like { **{ nested: 1 }, d: 2 }
20
+ # is represetned by sexp:
21
+ # (:hash,
22
+ # (:kwsplat,
23
+ # (:hash,
24
+ # (:sym, :nested),
25
+ # (:int, 1)
26
+ # )
27
+ # ),
28
+ # (:sym, :d),
29
+ # (:int, 2),
30
+ # )
31
+ # So k/v pairs and kwsplats can be mixed in any order.
32
+ def extract_kv_pairs_and_kwsplats
33
+ found_key = false
34
+
35
+ children.each do |obj|
12
36
  if obj.type == :kwsplat
13
- # obj is (:kwsplat, (:hash, (:key, value), ...))
14
- kwsplat_pairs = obj[1].children
15
- kwsplat_keys, kwsplat_values = extract_keys_and_values(kwsplat_pairs)
16
- keys.concat(kwsplat_keys)
17
- values.concat(kwsplat_values)
18
- elsif idx.even?
19
- keys << obj
20
- else
37
+ self.has_kwsplat = true
38
+ elsif found_key
21
39
  values << obj
40
+ found_key = false
41
+ else
42
+ keys << obj
43
+ found_key = true
22
44
  end
23
45
  end
24
46
 
25
47
  [keys, values]
26
48
  end
27
49
 
28
- def simple_keys?(keys)
29
- keys.all? { |key| [:sym, :str].include? key.type }
50
+ def simple_keys?
51
+ keys.all? { |key| [:sym, :str].include?(key.type) }
30
52
  end
31
53
 
32
54
  def compile
33
- keys, values = extract_keys_and_values(children)
55
+ extract_kv_pairs_and_kwsplats
34
56
 
35
- if simple_keys? keys
36
- compile_hash2 keys, values
57
+ if has_kwsplat
58
+ compile_merge
59
+ elsif simple_keys?
60
+ compile_hash2
37
61
  else
38
62
  compile_hash
39
63
  end
40
64
  end
41
65
 
66
+ # Compiles hashes containing kwsplats inside.
67
+ # hash like { **{ nested: 1 }, a: 1, **{ nested: 2} }
68
+ # should be compiled to
69
+ # { nested: 1}.merge(a: 1).merge(nested: 2)
70
+ # Each kwsplat overrides previosly defined keys
71
+ # Hash k/v pairs override previously defined kwsplat values
72
+ def compile_merge
73
+ helper :hash
74
+
75
+ result, seq = [], []
76
+
77
+ children.each do |child|
78
+ if child.type == :kwsplat
79
+ unless seq.empty?
80
+ result << expr(s(:hash, *seq))
81
+ end
82
+ result << expr(child)
83
+ seq = []
84
+ else
85
+ seq << child
86
+ end
87
+ end
88
+ unless seq.empty?
89
+ result << expr(s(:hash, *seq))
90
+ end
91
+
92
+ result.each_with_index do |fragment, idx|
93
+ if idx == 0
94
+ push fragment
95
+ else
96
+ push ".$merge(", fragment, ")"
97
+ end
98
+ end
99
+ end
100
+
101
+ # Compiles a hash without kwsplats
102
+ # with complex keys.
42
103
  def compile_hash
43
104
  helper :hash
44
105
 
@@ -50,7 +111,9 @@ module Opal
50
111
  wrap '$hash(', ')'
51
112
  end
52
113
 
53
- def compile_hash2(keys, values)
114
+ # Compiles a hash without kwsplats
115
+ # and containing **only** string/symbols as keys.
116
+ def compile_hash2
54
117
  hash_obj, hash_keys = {}, []
55
118
  helper :hash2
56
119
 
@@ -69,5 +132,14 @@ module Opal
69
132
  wrap "$hash2([#{hash_keys.join ', '}], {", "})"
70
133
  end
71
134
  end
135
+
136
+ class KwSplatNode < Base
137
+ handle :kwsplat
138
+ children :value
139
+
140
+ def compile
141
+ push "Opal.to_hash(", expr(value), ")"
142
+ end
143
+ end
72
144
  end
73
145
  end
@@ -15,8 +15,6 @@ module Opal
15
15
  extract_shadow_args
16
16
  split_args
17
17
 
18
- # require 'pry'; binding.pry
19
-
20
18
  to_vars = identity = body_code = nil
21
19
 
22
20
  in_scope do
@@ -44,6 +42,28 @@ module Opal
44
42
  unshift "(#{identity} = function(", inline_params, "){"
45
43
  push "}, #{identity}.$$s = self,"
46
44
  push " #{identity}.$$brk = $brk," if compiler.has_break?
45
+ push " #{identity}.$$arity = #{arity},"
46
+
47
+ if compiler.arity_check?
48
+ push " #{identity}.$$parameters = #{parameters_code},"
49
+ end
50
+
51
+ # MRI expands a passed argument if the block:
52
+ # 1. takes a single argument that is an array
53
+ # 2. has more that one argument
54
+ # With a few exceptions:
55
+ # 1. mlhs arg: if a block takes |(a, b)| argument
56
+ # 2. trailing ',' in the arg list (|a, |)
57
+ # This flag on the method indicates that a block has a top level mlhs argument
58
+ # which means that we have to expand passed array explicitly in runtime.
59
+ if has_top_level_mlhs_arg?
60
+ push " #{identity}.$$has_top_level_mlhs_arg = true,"
61
+ end
62
+
63
+ if has_trailing_comma_in_args?
64
+ push " #{identity}.$$has_trailing_comma_in_args = true,"
65
+ end
66
+
47
67
  push " #{identity})"
48
68
  end
49
69
 
@@ -122,6 +142,14 @@ module Opal
122
142
  def mlhs_args
123
143
  scope.mlhs_mapping.keys
124
144
  end
145
+
146
+ def has_top_level_mlhs_arg?
147
+ args.children.any? { |arg| arg.type == :mlhs }
148
+ end
149
+
150
+ def has_trailing_comma_in_args?
151
+ args.meta[:has_trailing_comma]
152
+ end
125
153
  end
126
154
  end
127
155
  end