opal 0.10.0.beta2 → 0.10.0.beta3

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