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
@@ -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