opal 0.7.2 → 0.8.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -1
  3. data/CHANGELOG.md +29 -0
  4. data/CONTRIBUTING.md +51 -4
  5. data/Gemfile +3 -0
  6. data/README.md +5 -5
  7. data/config.ru +1 -1
  8. data/examples/sinatra/Gemfile +1 -0
  9. data/examples/sinatra/config.ru +13 -3
  10. data/lib/mspec/opal/rake_task.rb +21 -30
  11. data/lib/mspec/opal/runner.rb +37 -0
  12. data/lib/mspec/opal/special_calls.rb +6 -0
  13. data/lib/opal/builder.rb +1 -0
  14. data/lib/opal/builder_processors.rb +5 -2
  15. data/lib/opal/cli_runners/phantom.js +10 -1
  16. data/lib/opal/compiler.rb +6 -3
  17. data/lib/opal/config.rb +48 -0
  18. data/lib/opal/nodes/call.rb +3 -2
  19. data/lib/opal/nodes/literal.rb +19 -2
  20. data/lib/opal/parser/grammar.rb +2224 -2196
  21. data/lib/opal/parser/grammar.y +25 -7
  22. data/lib/opal/parser/lexer.rb +12 -9
  23. data/lib/opal/path_reader.rb +1 -1
  24. data/lib/opal/sprockets/erb.rb +6 -20
  25. data/lib/opal/sprockets/path_reader.rb +4 -2
  26. data/lib/opal/sprockets/processor.rb +135 -80
  27. data/lib/opal/sprockets/server.rb +49 -78
  28. data/lib/opal/sprockets/source_map_header_patch.rb +41 -0
  29. data/lib/opal/sprockets/source_map_server.rb +115 -0
  30. data/lib/opal/version.rb +1 -1
  31. data/lib/tilt/opal.rb +48 -0
  32. data/opal.gemspec +1 -1
  33. data/opal/corelib/array.rb +179 -51
  34. data/opal/corelib/array/inheritance.rb +14 -0
  35. data/opal/corelib/boolean.rb +5 -0
  36. data/opal/corelib/hash.rb +1 -1
  37. data/opal/corelib/kernel.rb +660 -164
  38. data/opal/corelib/match_data.rb +44 -21
  39. data/opal/corelib/module.rb +83 -53
  40. data/opal/corelib/numeric.rb +15 -1
  41. data/opal/corelib/regexp.rb +31 -75
  42. data/opal/corelib/runtime.js +20 -8
  43. data/opal/corelib/string.rb +754 -243
  44. data/opal/corelib/string/inheritance.rb +20 -3
  45. data/opal/corelib/struct.rb +30 -6
  46. data/opal/corelib/variables.rb +2 -2
  47. data/spec/filters/bugs/array.rb +0 -39
  48. data/spec/filters/bugs/kernel.rb +10 -7
  49. data/spec/filters/bugs/module.rb +21 -0
  50. data/spec/filters/bugs/opal.rb +0 -5
  51. data/spec/filters/bugs/singleton.rb +0 -2
  52. data/spec/filters/bugs/string.rb +69 -315
  53. data/spec/filters/bugs/struct.rb +0 -16
  54. data/spec/filters/unsupported/encoding.rb +7 -0
  55. data/spec/filters/unsupported/float.rb +3 -0
  56. data/spec/filters/unsupported/integer_size.rb +52 -0
  57. data/spec/filters/unsupported/marshal.rb +4 -0
  58. data/spec/filters/unsupported/mutable_strings.rb +37 -0
  59. data/spec/filters/unsupported/private_methods.rb +11 -0
  60. data/spec/filters/unsupported/rational_numbers.rb +4 -0
  61. data/spec/filters/unsupported/regular_expressions.rb +47 -0
  62. data/spec/filters/unsupported/symbols.rb +7 -0
  63. data/spec/filters/unsupported/tainted.rb +23 -1
  64. data/spec/filters/unsupported/trusted.rb +5 -0
  65. data/spec/lib/fixtures/complex_sprockets.js.rb.erb +4 -0
  66. data/spec/lib/fixtures/jst_file.js.jst +1 -0
  67. data/spec/lib/parser/call_spec.rb +19 -0
  68. data/spec/lib/parser/def_spec.rb +6 -0
  69. data/spec/lib/sprockets/erb_spec.rb +17 -4
  70. data/spec/lib/sprockets/processor_spec.rb +50 -18
  71. data/spec/lib/sprockets/server_spec.rb +39 -11
  72. data/spec/lib/tilt/opal_spec.rb +19 -0
  73. data/spec/opal/core/kernel/format_spec.rb +10 -10
  74. data/spec/opal/core/language/predefined_spec.rb +10 -0
  75. data/spec/opal/core/object_id_spec.rb +56 -0
  76. data/spec/opal/core/runtime/bridged_classes_spec.rb +4 -4
  77. data/spec/opal/core/runtime_spec.rb +7 -0
  78. data/spec/opal/stdlib/native/native_class_spec.rb +1 -1
  79. data/spec/opal/stdlib/promise/always_spec.rb +30 -0
  80. data/spec/opal/stdlib/promise/then_spec.rb +8 -0
  81. data/spec/opal/stdlib/promise/trace_spec.rb +8 -0
  82. data/spec/rubyspecs +15 -104
  83. data/spec/spec_helper.rb +4 -0
  84. data/stdlib/native.rb +7 -18
  85. data/stdlib/nodejs/file.rb +1 -1
  86. data/stdlib/opal-parser.rb +1 -0
  87. data/stdlib/pp.rb +7 -5
  88. data/stdlib/promise.rb +53 -41
  89. data/tasks/testing.rake +8 -6
  90. metadata +28 -14
  91. data/spec/filters/bugs/match_data.rb +0 -13
  92. data/spec/filters/bugs/numeric.rb +0 -22
  93. data/spec/filters/bugs/regexp.rb +0 -9
  94. data/spec/filters/bugs/unknown.rb +0 -11
@@ -6,8 +6,8 @@ class MatchData
6
6
  @regexp = regexp
7
7
  @begin = `match_groups.index`
8
8
  @string = `match_groups.input`
9
- @pre_match = `#@string.substr(0, regexp.lastIndex - match_groups[0].length)`
10
- @post_match = `#@string.substr(regexp.lastIndex)`
9
+ @pre_match = `match_groups.input.slice(0, match_groups.index)`
10
+ @post_match = `match_groups.input.slice(match_groups.index + match_groups[0].length)`
11
11
  @matches = []
12
12
 
13
13
  %x{
@@ -28,22 +28,43 @@ class MatchData
28
28
  @matches[*args]
29
29
  end
30
30
 
31
+ def offset(n)
32
+ %x{
33
+ if (n !== 0) {
34
+ #{raise ArgumentError, 'MatchData#offset only supports 0th element'}
35
+ }
36
+ return [self.begin, self.begin + self.matches[n].length];
37
+ }
38
+ end
39
+
31
40
  def ==(other)
32
41
  return false unless MatchData === other
33
42
 
34
43
  `self.string == other.string` &&
35
- `self.regexp == other.regexp` &&
44
+ `self.regexp.toString() == other.regexp.toString()` &&
36
45
  `self.pre_match == other.pre_match` &&
37
46
  `self.post_match == other.post_match` &&
38
47
  `self.begin == other.begin`
39
48
  end
40
49
 
41
- def begin(pos)
42
- if pos != 0 && pos != 1
43
- raise ArgumentError, 'MatchData#begin only supports 0th element'
44
- end
50
+ alias eql? ==
45
51
 
46
- @begin
52
+ def begin(n)
53
+ %x{
54
+ if (n !== 0) {
55
+ #{raise ArgumentError, 'MatchData#begin only supports 0th element'}
56
+ }
57
+ return self.begin;
58
+ }
59
+ end
60
+
61
+ def end(n)
62
+ %x{
63
+ if (n !== 0) {
64
+ #{raise ArgumentError, 'MatchData#end only supports 0th element'}
65
+ }
66
+ return self.begin + self.matches[n].length;
67
+ }
47
68
  end
48
69
 
49
70
  def captures
@@ -76,27 +97,29 @@ class MatchData
76
97
  `#@matches[0]`
77
98
  end
78
99
 
79
- def values_at(*indexes)
100
+ def values_at(*args)
80
101
  %x{
81
- var values = [],
82
- match_length = #@matches.length;
102
+ var i, a, index, values = [];
83
103
 
84
- for (var i = 0, length = indexes.length; i < length; i++) {
85
- var pos = indexes[i];
104
+ for (i = 0; i < args.length; i++) {
86
105
 
87
- if (pos >= 0) {
88
- values.push(#@matches[pos]);
106
+ if (args[i].$$is_range) {
107
+ a = #{`args[i]`.to_a};
108
+ a.unshift(i, 1);
109
+ Array.prototype.splice.apply(args, a);
89
110
  }
90
- else {
91
- pos += match_length;
92
111
 
93
- if (pos > 0) {
94
- values.push(#@matches[pos]);
95
- }
96
- else {
112
+ index = #{Opal.coerce_to!(`args[i]`, Integer, :to_int)};
113
+
114
+ if (index < 0) {
115
+ index += #@matches.length;
116
+ if (index < 0) {
97
117
  values.push(nil);
118
+ continue;
98
119
  }
99
120
  }
121
+
122
+ values.push(#@matches[index]);
100
123
  }
101
124
 
102
125
  return values;
@@ -92,20 +92,33 @@ class Module
92
92
  attr_writer(*names)
93
93
  end
94
94
 
95
+ alias attr attr_accessor
96
+
95
97
  def attr_reader(*names)
96
98
  %x{
97
- for (var i = 0, length = names.length; i < length; i++) {
98
- (function(name) {
99
- self.$$proto[name] = nil;
100
- var func = function() { return this[name] };
99
+ var proto = self.$$proto;
101
100
 
102
- if (self.$$is_singleton) {
103
- self.$$proto.constructor.prototype['$' + name] = func;
104
- }
105
- else {
106
- Opal.defn(self, '$' + name, func);
107
- }
108
- })(names[i]);
101
+ for (var i = names.length - 1; i >= 0; i--) {
102
+ var name = names[i],
103
+ id = '$' + name;
104
+
105
+ // the closure here is needed because name will change at the next
106
+ // cycle, I wish we could use let.
107
+ var body = (function(name) {
108
+ return function() {
109
+ return this[name];
110
+ };
111
+ })(name);
112
+
113
+ // initialize the instance variable as nil
114
+ proto[name] = nil;
115
+
116
+ if (self.$$is_singleton) {
117
+ proto.constructor.prototype[id] = body;
118
+ }
119
+ else {
120
+ Opal.defn(self, id, body);
121
+ }
109
122
  }
110
123
  }
111
124
 
@@ -114,25 +127,35 @@ class Module
114
127
 
115
128
  def attr_writer(*names)
116
129
  %x{
117
- for (var i = 0, length = names.length; i < length; i++) {
118
- (function(name) {
119
- self.$$proto[name] = nil;
120
- var func = function(value) { return this[name] = value; };
130
+ var proto = self.$$proto;
121
131
 
122
- if (self.$$is_singleton) {
123
- self.$$proto.constructor.prototype['$' + name + '='] = func;
124
- }
125
- else {
126
- Opal.defn(self, '$' + name + '=', func);
132
+ for (var i = names.length - 1; i >= 0; i--) {
133
+ var name = names[i],
134
+ id = '$' + name + '=';
135
+
136
+ // the closure here is needed because name will change at the next
137
+ // cycle, I wish we could use let.
138
+ var body = (function(name){
139
+ return function(value) {
140
+ return this[name] = value;
127
141
  }
128
- })(names[i]);
142
+ })(name);
143
+
144
+ // initialize the instance variable as nil
145
+ proto[name] = nil;
146
+
147
+ if (self.$$is_singleton) {
148
+ proto.constructor.prototype[id] = body;
149
+ }
150
+ else {
151
+ Opal.defn(self, id, body);
152
+ }
129
153
  }
130
154
  }
155
+
131
156
  nil
132
157
  end
133
158
 
134
- alias attr attr_accessor
135
-
136
159
  def autoload(const, path)
137
160
  %x{
138
161
  var autoloaders;
@@ -176,15 +199,18 @@ class Module
176
199
 
177
200
  %x{
178
201
  scopes = [self.$$scope];
202
+
179
203
  if (inherit || self === Opal.Object) {
180
204
  var parent = self.$$super;
205
+
181
206
  while (parent !== Opal.BasicObject) {
182
207
  scopes.push(parent.$$scope);
208
+
183
209
  parent = parent.$$super;
184
210
  }
185
211
  }
186
212
 
187
- for (var i = 0, len = scopes.length; i < len; i++) {
213
+ for (var i = 0, length = scopes.length; i < length; i++) {
188
214
  if (scopes[i].hasOwnProperty(name)) {
189
215
  return true;
190
216
  }
@@ -195,19 +221,24 @@ class Module
195
221
  end
196
222
 
197
223
  def const_get(name, inherit = true)
224
+ if name['::'] && name != '::'
225
+ return name.split('::').inject(self){|o, c| o.const_get(c)}
226
+ end
198
227
  raise NameError, "wrong constant name #{name}" unless name =~ /^[A-Z]\w*$/
199
-
200
228
  %x{
201
229
  var scopes = [self.$$scope];
230
+
202
231
  if (inherit || self == Opal.Object) {
203
232
  var parent = self.$$super;
233
+
204
234
  while (parent !== Opal.BasicObject) {
205
235
  scopes.push(parent.$$scope);
236
+
206
237
  parent = parent.$$super;
207
238
  }
208
239
  }
209
240
 
210
- for (var i = 0, len = scopes.length; i < len; i++) {
241
+ for (var i = 0, length = scopes.length; i < length; i++) {
211
242
  if (scopes[i].hasOwnProperty(name)) {
212
243
  return scopes[i][name];
213
244
  }
@@ -217,20 +248,20 @@ class Module
217
248
  }
218
249
  end
219
250
 
220
- def const_missing(const)
251
+ def const_missing(name)
221
252
  %x{
222
253
  if (self.$$autoload) {
223
- var file = self.$$autoload[#{const}];
254
+ var file = self.$$autoload[name];
224
255
 
225
256
  if (file) {
226
257
  self.$require(file);
227
258
 
228
- return #{const_get const};
259
+ return #{const_get name};
229
260
  }
230
261
  }
231
262
  }
232
263
 
233
- raise NameError, "uninitialized constant #{self}::#{const}"
264
+ raise NameError, "uninitialized constant #{self}::#{name}"
234
265
  end
235
266
 
236
267
  def const_set(name, value)
@@ -247,26 +278,31 @@ class Module
247
278
  value
248
279
  end
249
280
 
250
- def define_method(name, method = undefined, &block)
251
- %x{
252
- if (method) {
253
- block = #{method.to_proc};
254
- }
281
+ def define_method(name, method = nil, &block)
282
+ unless method || block
283
+ raise ArgumentError, 'tried to create Proc object without a block'
284
+ end
255
285
 
256
- if (block === nil) {
257
- throw new Error("no block given");
258
- }
286
+ if method
287
+ if Proc === method
288
+ block = method
289
+ else
290
+ raise TypeError, "wrong argument type #{method.class} (expected Proc/Method)"
291
+ end
292
+ end
293
+
294
+ %x{
295
+ var id = '$' + name;
259
296
 
260
- var jsid = '$' + name;
261
297
  block.$$jsid = name;
262
298
  block.$$s = null;
263
299
  block.$$def = block;
264
300
 
265
301
  if (self.$$is_singleton) {
266
- self.$$proto[jsid] = block;
302
+ self.$$proto[id] = block;
267
303
  }
268
304
  else {
269
- Opal.defn(self, jsid, block);
305
+ Opal.defn(self, id, block);
270
306
  }
271
307
 
272
308
  return name;
@@ -274,15 +310,9 @@ class Module
274
310
  end
275
311
 
276
312
  def remove_method(name)
277
- %x{
278
- var jsid = '$' + name;
279
- var current = self.$$proto[jsid];
280
- delete self.$$proto[jsid];
313
+ `Opal.undef(self, '$' + name)`
281
314
 
282
- // Check if we need to reverse Opal.donate
283
- // Opal.retire(self, [jsid]);
284
- return self;
285
- }
315
+ self
286
316
  end
287
317
 
288
318
  def include(*mods)
@@ -304,7 +334,7 @@ class Module
304
334
 
305
335
  def include?(mod)
306
336
  %x{
307
- for (var cls = self; cls; cls = cls.parent) {
337
+ for (var cls = self; cls; cls = cls.$$super) {
308
338
  for (var i = 0; i != cls.$$inc.length; i++) {
309
339
  var mod2 = cls.$$inc[i];
310
340
  if (mod === mod2) {
@@ -328,17 +358,17 @@ class Module
328
358
  }
329
359
  end
330
360
 
331
- def instance_methods(include_super = false)
361
+ def instance_methods(include_super = true)
332
362
  %x{
333
363
  var methods = [],
334
364
  proto = self.$$proto;
335
365
 
336
366
  for (var prop in proto) {
337
- if (!prop.charAt(0) === '$') {
367
+ if (!(prop.charAt(0) === '$')) {
338
368
  continue;
339
369
  }
340
370
 
341
- if (typeof(proto[prop]) !== "function") {
371
+ if (!(typeof(proto[prop]) === "function")) {
342
372
  continue;
343
373
  }
344
374
 
@@ -5,6 +5,11 @@ class Numeric
5
5
 
6
6
  `def.$$is_number = true`
7
7
 
8
+ def __id__
9
+ `(self * 2) + 1`
10
+ end
11
+ alias object_id __id__
12
+
8
13
  def coerce(other, type = :operation)
9
14
  %x{
10
15
  if (other.$$is_number) {
@@ -266,6 +271,9 @@ class Numeric
266
271
  return enum_for :downto, finish unless block
267
272
 
268
273
  %x{
274
+ if (!finish.$$is_number) {
275
+ #{raise ArgumentError, "comparison of #{self.class} with #{finish.class} failed"}
276
+ }
269
277
  for (var i = self; i >= finish; i--) {
270
278
  if (block(i) === $breaker) {
271
279
  return $breaker.$v;
@@ -277,7 +285,10 @@ class Numeric
277
285
  end
278
286
 
279
287
  alias eql? ==
280
- alias equal? ==
288
+
289
+ def equal?(other)
290
+ self == other || `isNaN(self) && isNaN(other)`
291
+ end
281
292
 
282
293
  def even?
283
294
  `self % 2 === 0`
@@ -455,6 +466,9 @@ class Numeric
455
466
  return enum_for :upto, finish unless block
456
467
 
457
468
  %x{
469
+ if (!finish.$$is_number) {
470
+ #{raise ArgumentError, "comparison of #{self.class} with #{finish.class} failed"}
471
+ }
458
472
  for (var i = self; i <= finish; i++) {
459
473
  if (block(i) === $breaker) {
460
474
  return $breaker.$v;
@@ -35,53 +35,12 @@ class Regexp
35
35
  `other.constructor == RegExp && self.toString() === other.toString()`
36
36
  end
37
37
 
38
- def ===(str)
39
- %x{
40
- if (!str.$$is_string && #{str.respond_to?(:to_str)}) {
41
- #{str = str.to_str};
42
- }
43
-
44
- if (!str.$$is_string) {
45
- return false;
46
- }
47
-
48
- return self.test(str);
49
- }
38
+ def ===(string)
39
+ `#{match(string)} !== nil`
50
40
  end
51
41
 
52
42
  def =~(string)
53
- if `string === nil`
54
- $~ = nil
55
-
56
- return
57
- end
58
-
59
- string = Opal.coerce_to(string, String, :to_str).to_s
60
-
61
- %x{
62
- var re = self;
63
-
64
- if (re.global) {
65
- // should we clear it afterwards too?
66
- re.lastIndex = 0;
67
- }
68
- else {
69
- // rewrite regular expression to add the global flag to capture pre/post match
70
- re = new RegExp(re.source, 'g' + (re.multiline ? 'm' : '') + (re.ignoreCase ? 'i' : ''));
71
- }
72
-
73
- var result = re.exec(string);
74
-
75
- if (result) {
76
- #{$~ = MatchData.new(`re`, `result`)};
77
-
78
- return result.index;
79
- }
80
- else {
81
- #{$~ = nil};
82
- return nil;
83
- }
84
- }
43
+ match(string) && $~.begin(0)
85
44
  end
86
45
 
87
46
  alias eql? ==
@@ -91,49 +50,46 @@ class Regexp
91
50
  end
92
51
 
93
52
  def match(string, pos = undefined, &block)
94
- if `string === nil`
95
- $~ = nil
96
-
97
- return
98
- end
99
-
100
- if `string.$$is_string == null`
101
- unless string.respond_to? :to_str
102
- raise TypeError, "no implicit conversion of #{string.class} into String"
103
- end
104
-
105
- string = string.to_str
106
- end
107
-
108
53
  %x{
109
- var re = self;
110
-
111
- if (re.global) {
112
- // should we clear it afterwards too?
113
- re.lastIndex = 0;
54
+ if (pos === undefined) {
55
+ pos = 0;
56
+ } else {
57
+ pos = #{Opal.coerce_to(pos, Integer, :to_int)};
114
58
  }
115
- else {
116
- re = new RegExp(re.source, 'g' + (re.multiline ? 'm' : '') + (re.ignoreCase ? 'i' : ''));
59
+
60
+ if (string === nil) {
61
+ return #{$~ = nil};
117
62
  }
118
63
 
119
- var result = re.exec(string);
64
+ string = #{Opal.coerce_to(string, String, :to_str)};
65
+
66
+ if (pos < 0) {
67
+ pos += string.length;
68
+ if (pos < 0) {
69
+ return #{$~ = nil};
70
+ }
71
+ }
120
72
 
121
- if (result) {
122
- result = #{$~ = MatchData.new(`re`, `result`)};
73
+ var md, re = new RegExp(self.source, 'gm' + (self.ignoreCase ? 'i' : ''));
123
74
 
124
- if (block === nil) {
125
- return result;
75
+ while (true) {
76
+ md = re.exec(string);
77
+ if (md === null) {
78
+ return #{$~ = nil};
126
79
  }
127
- else {
128
- return #{block.call(`result`)};
80
+ if (md.index >= pos) {
81
+ #{$~ = MatchData.new(`re`, `md`)}
82
+ return block === nil ? #{$~} : #{block.call($~)};
129
83
  }
130
- }
131
- else {
132
- return #{$~ = nil};
84
+ re.lastIndex = md.index + 1;
133
85
  }
134
86
  }
135
87
  end
136
88
 
89
+ def ~
90
+ self =~ $_
91
+ end
92
+
137
93
  def source
138
94
  `self.source`
139
95
  end