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
@@ -1,4 +1,5 @@
1
1
  require 'lib/spec_helper'
2
+ require 'support/match_helpers'
2
3
 
3
4
  describe Opal::Compiler do
4
5
  describe 'requiring' do
@@ -20,6 +21,10 @@ describe Opal::Compiler do
20
21
  end
21
22
  end
22
23
 
24
+ it 'raises syntax errors properly' do
25
+ expect(lambda {Opal::Compiler.new('def foo').compile}).to raise_error SyntaxError, /An error occurred while compiling:.*false/m
26
+ end
27
+
23
28
  it "should compile simple ruby values" do
24
29
  expect_compiled("3.142").to include("return 3.142")
25
30
  expect_compiled("123e1").to include("return 1230")
@@ -143,9 +148,10 @@ describe Opal::Compiler do
143
148
 
144
149
  describe '#require_tree' do
145
150
  require 'pathname'
146
- let(:file) { Pathname(__FILE__).join('../fixtures/require_tree_test.rb') }
147
151
 
148
152
  it 'parses and resolve #require argument' do
153
+ file = Pathname(__FILE__).join('../fixtures/require_tree_test.rb')
154
+
149
155
  compiler = compiler_for(file.read)
150
156
  expect(compiler.required_trees).to eq(['../fixtures/required_tree_test'])
151
157
  end
@@ -11,6 +11,12 @@ describe "Kernel#instance_variables" do
11
11
  end
12
12
  end
13
13
 
14
+ context 'for non-empty string' do
15
+ it 'returns blank array' do
16
+ expect('test'.instance_variables).to eq([])
17
+ end
18
+ end
19
+
14
20
  context 'for hash' do
15
21
  it 'returns blank array' do
16
22
  expect({}.instance_variables).to eq([])
@@ -31,6 +37,24 @@ describe "Kernel#instance_variables" do
31
37
  end
32
38
  end
33
39
 
40
+ context 'for non-empty hash' do
41
+ it 'returns blank array' do
42
+ expect({ 1 => 2 }.instance_variables).to eq([])
43
+ end
44
+ end
45
+
46
+ context 'for array' do
47
+ it 'returns blank array' do
48
+ expect([].instance_variables).to eq([])
49
+ end
50
+ end
51
+
52
+ context 'for non-empty array' do
53
+ it 'returns blank array' do
54
+ expect((1..20).to_a.instance_variables).to eq([])
55
+ end
56
+ end
57
+
34
58
  context 'for object' do
35
59
  it 'returns blank array' do
36
60
  expect(Object.new.instance_variables).to eq([])
@@ -67,4 +91,20 @@ describe "Kernel#instance_variables" do
67
91
  end
68
92
  end
69
93
  end
94
+
95
+ context 'for a class' do
96
+ it 'returns blank array' do
97
+ expect(Class.new.instance_variables).to eq([])
98
+ end
99
+ end
100
+
101
+ context 'for a class with nested constant' do
102
+ class ClassWithConstantWithoutIvar
103
+ A = 1
104
+ end
105
+
106
+ it 'returns blank array' do
107
+ expect(ClassWithConstantWithoutIvar.instance_variables).to eq([])
108
+ end
109
+ end
70
110
  end
@@ -11,4 +11,10 @@ describe "Ternary condition operator" do
11
11
  # this could be interpreted as a Ruby 1.9 symbol hash key
12
12
  (true ?'str':'another str').should == 'str'
13
13
  end
14
+
15
+ it "doesn't interpret ?? as an identifier" do
16
+ obj = mock("object with a query method")
17
+ obj.should_receive("m?").and_return(true)
18
+ (obj.m?? 1 : 2).should == 1
19
+ end
14
20
  end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ module MarshalExtension
4
+ end
5
+
6
+ class MarshalUserRegexp < Regexp
7
+ end
8
+
9
+ class UserMarshal
10
+ attr_reader :data
11
+
12
+ def initialize
13
+ @data = 'stuff'
14
+ end
15
+ def marshal_dump() 'data' end
16
+ def marshal_load(data) @data = data end
17
+ def ==(other) self.class === other and @data == other.data end
18
+ end
19
+
20
+ describe 'Marshal.dump' do
21
+ it 'dumps non-empty Array' do
22
+ expect(Marshal.dump(['a', 1, 2])).to eq("\u0004\b[\b\"\u0006ai\u0006i\a")
23
+ end
24
+
25
+ it 'dumps case-sensitive regexp' do
26
+ expect(Marshal.dump(/\w+/)).to eq("\u0004\b/\b\\w+\u0000")
27
+ end
28
+
29
+ it 'dumps case-insensitive regexp' do
30
+ expect(Marshal.dump(/\w+/i)).to eq("\u0004\b/\b\\w+\u0001")
31
+ end
32
+
33
+ it "dumps a Float" do
34
+ Marshal.dump(123.4567).should == "\004\bf\r123.4567"
35
+ Marshal.dump(-0.841).should == "\x04\bf\v-0.841"
36
+ Marshal.dump(9876.345).should == "\004\bf\r9876.345"
37
+ Marshal.dump(Float::INFINITY).should == "\004\bf\binf"
38
+ Marshal.dump(-Float::INFINITY).should == "\004\bf\t-inf"
39
+ Marshal.dump(Float::NAN).should == "\004\bf\bnan"
40
+ end
41
+
42
+ it "dumps a Regexp with flags" do
43
+ Marshal.dump(/\w/im).should == "\x04\b/\a\\w\u0005"
44
+ end
45
+
46
+ it 'dumps an extended Regexp' do
47
+ Marshal.dump(/\w/.extend(MarshalExtension)).should == "\x04\be:\u0015MarshalExtension/\a\\w\u0000"
48
+ end
49
+
50
+ it 'dumps object#marshal_dump when object responds to #marshal_dump' do
51
+ Marshal.dump(UserMarshal.new).should == "\u0004\bU:\u0010UserMarshal\"\tdata"
52
+ end
53
+ end
@@ -0,0 +1,7 @@
1
+ describe "Marshal.load" do
2
+ it 'loads array with instance variable' do
3
+ a = Marshal.load("\x04\bI[\bi\x06i\ai\b\x06:\n@ivari\x01{")
4
+ a.should == [1, 2, 3]
5
+ a.instance_variable_get(:@ivar).should == 123
6
+ end
7
+ end
@@ -4,12 +4,46 @@ require 'opal-source-maps'
4
4
  describe Opal::SourceMap do
5
5
  before do
6
6
  pathname = 'foo.rb'
7
- compiler = Opal::Compiler.new("1 + 1", :file => pathname)
7
+ compiler = Opal::Compiler.new("def foo\n123\nend", :file => pathname)
8
8
  @source = compiler.compile
9
9
  @map = Opal::SourceMap.new(compiler.fragments, pathname)
10
10
  end
11
11
 
12
+ def js_line_for(code)
13
+ index = @source.split("\n").index {|line| line.include?(code)}
14
+ # 1 based line numbers
15
+ index + 1
16
+ end
17
+
18
+ def parsed_map
19
+ SourceMap::Map.from_json(@map.as_json.to_json)
20
+ end
21
+
22
+ def mappings
23
+ mappings = []
24
+ # mappings is not exposed on the ext library
25
+ parsed_map.each do |mapping|
26
+ mappings << mapping
27
+ end
28
+ mappings
29
+ end
30
+
12
31
  it 'does not blow while generating the map' do
13
32
  lambda { @map.as_json }.should_not raise_error
14
33
  end
34
+
35
+ it 'parses the map without error' do
36
+ parsed_map.should_not be_nil
37
+ end
38
+
39
+ it 'identifies line numbers' do
40
+ match = mappings.find {|map| map.original.line == 2 }
41
+ # as of Opal 0.10
42
+ match.generated.line.should == js_line_for('return 123')
43
+ end
44
+
45
+ it 'uses method names' do
46
+ match = mappings.find {|map| map.original.line == 2 }
47
+ match.name.should == 'foo'
48
+ end
15
49
  end
@@ -0,0 +1,16 @@
1
+ module JavaScriptAPIFixtures
2
+ class A
3
+ end
4
+
5
+ class A::B
6
+ end
7
+ end
8
+
9
+ require 'spec_helper'
10
+ require 'native'
11
+
12
+ describe "JavaScript API" do
13
+ it "allows to acces scopes on `Opal` with dots (regression for #1418)" do
14
+ `Opal.JavaScriptAPIFixtures.A.B`.should == JavaScriptAPIFixtures::A::B
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'source_map'
3
+
4
+ describe 'SourceMap::VLQ' do
5
+ it 'encodes properly' do
6
+ SourceMap::VLQ.encode([0]).should == 'A'
7
+ end
8
+ end
@@ -15,12 +15,12 @@ ruby/core/exception
15
15
 
16
16
  ruby/core/false
17
17
  ruby/core/fixnum
18
+ ruby/core/file/basename_spec
19
+ ruby/core/file/dirname_spec
18
20
  ruby/core/file/extname_spec
19
21
  ruby/core/float
20
22
  ruby/core/hash
21
-
22
23
  ruby/core/integer
23
- !ruby/core/integer/times_spec
24
24
 
25
25
  ruby/core/kernel
26
26
  !ruby/core/kernel/abort_spec
@@ -69,6 +69,11 @@ ruby/core/method
69
69
 
70
70
  ruby/core/module
71
71
 
72
+ ruby/core/marshal/load_spec
73
+ ruby/core/marshal/dump_spec
74
+ ruby/core/marshal/major_version_spec
75
+ ruby/core/marshal/minor_version_spec
76
+
72
77
  ruby/core/nil
73
78
  ruby/core/numeric
74
79
 
@@ -95,10 +100,8 @@ ruby/core/unboundmethod
95
100
  ruby/language
96
101
  !ruby/language/constants_spec
97
102
  !ruby/language/execution_spec
98
- !ruby/language/hash_spec
99
103
  !ruby/language/next_spec
100
104
  !ruby/language/return_spec
101
- !ruby/language/send_spec
102
105
  !ruby/language/yield_spec
103
106
 
104
107
  ruby/library/base64
@@ -0,0 +1,57 @@
1
+ module MatchHelpers
2
+ class MatchMatcher
3
+ def initialize(expected)
4
+ fail "Expected #{expected} to be a Regexp!" unless expected.is_a?(Regexp)
5
+ @expected = expected
6
+ end
7
+
8
+ def matches?(actual)
9
+ @actual = actual
10
+ @expected.match(@actual)
11
+ end
12
+
13
+ def failure_message
14
+ ["Expected #{@actual.inspect} (#{@actual.class})",
15
+ "to match #{@expected}"]
16
+ end
17
+
18
+ def negative_failure_message
19
+ ["Expected #{@actual.inspect} (#{@actual.class})",
20
+ "not to match #{@expected}"]
21
+ end
22
+ end
23
+
24
+ class EndWithHelper
25
+ def initialize(expected)
26
+ @expected = expected
27
+ end
28
+
29
+ def matches?(actual)
30
+ @actual = actual
31
+ @actual.end_with?(@expected)
32
+ end
33
+
34
+ def failure_message
35
+ ["Expected #{@actual.inspect} (#{@actual.class})",
36
+ "to end with #{@expected}"]
37
+ end
38
+
39
+ def negative_failure_message
40
+ ["Expected #{@actual.inspect} (#{@actual.class})",
41
+ "not to end with #{@expected}"]
42
+ end
43
+ end
44
+ end
45
+
46
+ if !defined? RSpec
47
+ class Object
48
+ def match(expected)
49
+ MatchHelpers::MatchMatcher.new(expected)
50
+ end
51
+
52
+ def end_with(expected)
53
+ MatchHelpers::EndWithHelper.new(expected)
54
+ end
55
+ end
56
+ end
57
+
@@ -23,7 +23,7 @@ module MSpecRSpecAdapter
23
23
  end
24
24
  end
25
25
 
26
- def not_to
26
+ def not_to(expectation)
27
27
  apply_expectation(:should_not, expectation)
28
28
  end
29
29
  alias to_not not_to
@@ -5,7 +5,9 @@ require 'opal/version'
5
5
  module Kernel
6
6
  def eval(str)
7
7
  str = Opal.coerce_to!(str, String, :to_str)
8
- code = Opal.compile str, file: '(eval)', eval: true
8
+ default_eval_options = { file: '(eval)', eval: true }
9
+ compiling_options = __OPAL_COMPILER_CONFIG__.merge(default_eval_options)
10
+ code = Opal.compile str, compiling_options
9
11
  %x{
10
12
  return (function(self) {
11
13
  return eval(#{code});
@@ -1,7 +1,23 @@
1
+ require 'corelib/comparable'
2
+
3
+ # Portions from Author:: Tanaka Akira <akr@m17n.org>
1
4
  class Pathname
5
+ include Comparable
6
+ SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
7
+
2
8
  def initialize(path)
3
- raise ArgumentError if path == "\0"
4
- @path = path
9
+ if Pathname === path
10
+ @path = path.path.to_s
11
+ elsif path.respond_to?(:to_path)
12
+ @path = path.to_path
13
+ elsif path.is_a?(String)
14
+ @path = path
15
+ elsif path.nil?
16
+ raise TypeError, 'no implicit conversion of nil into String'
17
+ else
18
+ raise TypeError, "no implicit conversion of #{path.class} into String"
19
+ end
20
+ raise ArgumentError if @path == "\0"
5
21
  end
6
22
 
7
23
  attr_reader :path
@@ -11,11 +27,25 @@ class Pathname
11
27
  end
12
28
 
13
29
  def absolute?
14
- @path.start_with? '/'
30
+ !relative?
15
31
  end
16
32
 
17
33
  def relative?
18
- !absolute?
34
+ path = @path
35
+ while r = chop_basename(path)
36
+ path, = r
37
+ end
38
+ path == ''
39
+ end
40
+
41
+ def chop_basename(path) # :nodoc:
42
+ base = File.basename(path)
43
+ # ruby uses /^#{SEPARATOR_PAT}?$/o but having issues with interpolation
44
+ if Regexp.new("^#{Pathname::SEPARATOR_PAT.source}?$") =~ base
45
+ return nil
46
+ else
47
+ return path[0, path.rindex(base)], base
48
+ end
19
49
  end
20
50
 
21
51
  def root?
@@ -50,11 +80,52 @@ class Pathname
50
80
 
51
81
  def +(other)
52
82
  other = Pathname.new(other) unless Pathname === other
53
- Pathname.new(File.join(@path, other.to_path))
83
+ Pathname.new(plus(@path, other.to_s))
84
+ end
85
+
86
+ def plus(path1, path2) # -> path # :nodoc:
87
+ prefix2 = path2
88
+ index_list2 = []
89
+ basename_list2 = []
90
+ while r2 = chop_basename(prefix2)
91
+ prefix2, basename2 = r2
92
+ index_list2.unshift prefix2.length
93
+ basename_list2.unshift basename2
94
+ end
95
+ return path2 if prefix2 != ''
96
+ prefix1 = path1
97
+ while true
98
+ while !basename_list2.empty? && basename_list2.first == '.'
99
+ index_list2.shift
100
+ basename_list2.shift
101
+ end
102
+ break unless r1 = chop_basename(prefix1)
103
+ prefix1, basename1 = r1
104
+ next if basename1 == '.'
105
+ if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
106
+ prefix1 = prefix1 + basename1
107
+ break
108
+ end
109
+ index_list2.shift
110
+ basename_list2.shift
111
+ end
112
+ r1 = chop_basename(prefix1)
113
+ if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
114
+ while !basename_list2.empty? && basename_list2.first == '..'
115
+ index_list2.shift
116
+ basename_list2.shift
117
+ end
118
+ end
119
+ if !basename_list2.empty?
120
+ suffix2 = path2[index_list2.first..-1]
121
+ r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
122
+ else
123
+ r1 ? prefix1 : File.dirname(prefix1)
124
+ end
54
125
  end
55
126
 
56
127
  def join(*args)
57
- args.unshift self
128
+ return self if args.empty?
58
129
  result = args.pop
59
130
  result = Pathname.new(result) unless Pathname === result
60
131
  return result if result.absolute?
@@ -63,9 +134,36 @@ class Pathname
63
134
  result = arg + result
64
135
  return result if result.absolute?
65
136
  }
66
- result
137
+ self + result
138
+ end
139
+
140
+ def split
141
+ [ dirname, basename ]
142
+ end
143
+
144
+ def dirname
145
+ Pathname.new(File.dirname(@path))
67
146
  end
68
147
 
148
+ def basename
149
+ Pathname.new(File.basename(@path))
150
+ end
151
+
152
+ def directory?
153
+ File.directory?(@path)
154
+ end
155
+
156
+ def extname
157
+ File.extname(@path)
158
+ end
159
+
160
+ def <=>(other)
161
+ self.path <=> other.path
162
+ end
163
+
164
+ alias eql? ==
165
+ alias === ==
166
+
69
167
  alias :to_str :to_path
70
168
  alias :to_s :to_path
71
169
  end