opal 0.7.0.beta3 → 0.7.0.rc1

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/.gitmodules +4 -0
  3. data/.travis.yml +7 -3
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile +1 -1
  7. data/README.md +3 -12
  8. data/Rakefile +4 -150
  9. data/bin/opal-mspec +1 -1
  10. data/docs/compiler_directives.md +127 -0
  11. data/examples/rack/.gitignore +1 -0
  12. data/examples/rack/app/user.rb +1 -0
  13. data/lib/mspec/opal/special_calls.rb +15 -2
  14. data/lib/opal/builder.rb +15 -8
  15. data/lib/opal/compiler.rb +75 -4
  16. data/lib/opal/erb.rb +22 -2
  17. data/lib/opal/fragment.rb +17 -5
  18. data/lib/opal/nodes/def.rb +174 -53
  19. data/lib/opal/nodes/if.rb +14 -0
  20. data/lib/opal/nodes/module.rb +0 -1
  21. data/lib/opal/nodes/rescue.rb +10 -2
  22. data/lib/opal/nodes/scope.rb +0 -17
  23. data/lib/opal/parser.rb +83 -19
  24. data/lib/opal/parser/grammar.rb +2511 -2414
  25. data/lib/opal/parser/grammar.y +71 -9
  26. data/lib/opal/parser/lexer.rb +44 -12
  27. data/lib/opal/parser/parser_scope.rb +3 -0
  28. data/lib/opal/parser/sexp.rb +7 -1
  29. data/lib/opal/paths.rb +5 -5
  30. data/lib/opal/sprockets/environment.rb +2 -10
  31. data/lib/opal/sprockets/path_reader.rb +1 -1
  32. data/lib/opal/sprockets/processor.rb +1 -0
  33. data/lib/opal/sprockets/server.rb +2 -0
  34. data/lib/opal/util.rb +7 -2
  35. data/lib/opal/version.rb +1 -1
  36. data/opal.gemspec +1 -0
  37. data/opal/README.md +1 -1
  38. data/opal/corelib/dir.rb +1 -1
  39. data/opal/corelib/enumerable.rb +3 -1
  40. data/opal/corelib/error.rb +3 -0
  41. data/opal/corelib/file.rb +2 -0
  42. data/opal/corelib/hash.rb +3 -0
  43. data/opal/corelib/io.rb +15 -1
  44. data/opal/corelib/kernel.rb +8 -0
  45. data/opal/corelib/module.rb +42 -17
  46. data/opal/corelib/runtime.js +223 -49
  47. data/opal/corelib/string.rb +1 -1
  48. data/opal/corelib/struct.rb +1 -7
  49. data/spec/README.md +8 -0
  50. data/spec/filters/bugs/language.rb +1 -0
  51. data/spec/filters/bugs/module.rb +4 -0
  52. data/spec/filters/unsupported/frozen.rb +2 -0
  53. data/spec/lib/compiler/pre_processed_conditionals_spec.rb +87 -0
  54. data/spec/lib/compiler_spec.rb +1 -67
  55. data/spec/lib/fixtures/file_with_directives.js +2 -0
  56. data/spec/lib/fixtures/required_file.js +1 -0
  57. data/spec/lib/parser/def_spec.rb +29 -16
  58. data/spec/lib/parser/variables_spec.rb +5 -5
  59. data/spec/lib/sprockets/path_reader_spec.rb +24 -8
  60. data/spec/lib/sprockets/server_spec.rb +10 -3
  61. data/spec/opal/core/date_spec.rb +14 -0
  62. data/spec/opal/core/language/versions/def_2_0_spec.rb +62 -0
  63. data/spec/opal/core/language_spec.rb +23 -0
  64. data/spec/opal/core/runtime/donate_spec.rb +53 -0
  65. data/spec/opal/stdlib/native/native_alias_spec.rb +19 -0
  66. data/spec/opal/stdlib/native/native_class_spec.rb +18 -0
  67. data/spec/opal/stdlib/native/native_module_spec.rb +13 -0
  68. data/spec/rubyspecs +2 -0
  69. data/stdlib/buffer.rb +1 -0
  70. data/stdlib/date.rb +18 -0
  71. data/stdlib/encoding.rb +3 -2
  72. data/stdlib/minitest.rb +780 -0
  73. data/stdlib/minitest/assertions.rb +662 -0
  74. data/stdlib/minitest/autorun.rb +12 -0
  75. data/stdlib/minitest/benchmark.rb +426 -0
  76. data/stdlib/minitest/expectations.rb +281 -0
  77. data/stdlib/minitest/hell.rb +11 -0
  78. data/stdlib/minitest/mock.rb +220 -0
  79. data/stdlib/minitest/parallel.rb +65 -0
  80. data/stdlib/minitest/pride.rb +4 -0
  81. data/stdlib/minitest/pride_plugin.rb +142 -0
  82. data/stdlib/minitest/spec.rb +310 -0
  83. data/stdlib/minitest/test.rb +293 -0
  84. data/stdlib/minitest/unit.rb +45 -0
  85. data/stdlib/native.rb +12 -3
  86. data/stdlib/nodejs/process.rb +16 -2
  87. data/stdlib/promise.rb +99 -0
  88. data/stdlib/test/unit.rb +10 -0
  89. data/stdlib/thread.rb +4 -0
  90. data/tasks/building.rake +58 -0
  91. data/tasks/documentation.rake +38 -0
  92. data/tasks/documenting.rake +37 -0
  93. data/tasks/testing.rake +102 -0
  94. metadata +57 -2
@@ -47,11 +47,17 @@ class Module
47
47
 
48
48
  def alias_method(newname, oldname)
49
49
  %x{
50
- self.$$proto['$' + newname] = self.$$proto['$' + oldname];
50
+ var newjsid = '$' + newname,
51
+ body = self.$$proto['$' + oldname];
51
52
 
52
- if (self.$$methods) {
53
- Opal.donate(self, ['$' + newname ])
53
+ if (self.$$is_singleton) {
54
+ self.$$proto[newjsid] = body;
54
55
  }
56
+ else {
57
+ Opal.defn(self, newjsid, body);
58
+ }
59
+
60
+ return self;
55
61
  }
56
62
  self
57
63
  end
@@ -88,18 +94,16 @@ class Module
88
94
 
89
95
  def attr_reader(*names)
90
96
  %x{
91
- var proto = self.$$proto, cls = self;
92
97
  for (var i = 0, length = names.length; i < length; i++) {
93
98
  (function(name) {
94
- proto[name] = nil;
99
+ self.$$proto[name] = nil;
95
100
  var func = function() { return this[name] };
96
101
 
97
- if (cls.$$is_singleton) {
98
- proto.constructor.prototype['$' + name] = func;
102
+ if (self.$$is_singleton) {
103
+ self.$$proto.constructor.prototype['$' + name] = func;
99
104
  }
100
105
  else {
101
- proto['$' + name] = func;
102
- Opal.donate(self, ['$' + name ]);
106
+ Opal.defn(self, '$' + name, func);
103
107
  }
104
108
  })(names[i]);
105
109
  }
@@ -110,18 +114,16 @@ class Module
110
114
 
111
115
  def attr_writer(*names)
112
116
  %x{
113
- var proto = self.$$proto, cls = self;
114
117
  for (var i = 0, length = names.length; i < length; i++) {
115
118
  (function(name) {
116
- proto[name] = nil;
119
+ self.$$proto[name] = nil;
117
120
  var func = function(value) { return this[name] = value; };
118
121
 
119
- if (cls.$$is_singleton) {
120
- proto.constructor.prototype['$' + name + '='] = func;
122
+ if (self.$$is_singleton) {
123
+ self.$$proto.constructor.prototype['$' + name + '='] = func;
121
124
  }
122
125
  else {
123
- proto['$' + name + '='] = func;
124
- Opal.donate(self, ['$' + name + '=']);
126
+ Opal.defn(self, '$' + name + '=', func);
125
127
  }
126
128
  })(names[i]);
127
129
  }
@@ -144,6 +146,25 @@ class Module
144
146
  }
145
147
  end
146
148
 
149
+ def class_variable_get(name)
150
+ name = Opal.coerce_to!(name, String, :to_str)
151
+ raise NameError, 'class vars should start with @@' if `name.length < 3 || name.slice(0,2) !== '@@'`
152
+ %x{
153
+ var value = Opal.cvars[name.slice(2)];
154
+ #{raise NameError, 'uninitialized class variable @@a in' if `value == null`}
155
+ return value;
156
+ }
157
+ end
158
+
159
+ def class_variable_set(name, value)
160
+ name = Opal.coerce_to!(name, String, :to_str)
161
+ raise NameError if `name.length < 3 || name.slice(0,2) !== '@@'`
162
+ %x{
163
+ Opal.cvars[name.slice(2)] = value;
164
+ return value;
165
+ }
166
+ end
167
+
147
168
  def constants
148
169
  `self.$$scope.constants`
149
170
  end
@@ -241,8 +262,12 @@ class Module
241
262
  block.$$s = null;
242
263
  block.$$def = block;
243
264
 
244
- self.$$proto[jsid] = block;
245
- Opal.donate(self, [jsid]);
265
+ if (self.$$is_singleton) {
266
+ self.$$proto[jsid] = block;
267
+ }
268
+ else {
269
+ Opal.defn(self, jsid, block);
270
+ }
246
271
 
247
272
  return name;
248
273
  }
@@ -43,7 +43,22 @@
43
43
  // Globals table
44
44
  Opal.gvars = {};
45
45
 
46
- // Get constants
46
+ /**
47
+ Get a constant on the given scope. Every class and module in Opal has a
48
+ scope used to store, and inherit, constants. For example, the top level
49
+ `Object` in ruby has a scope accessible as `Opal.Object.$$scope`.
50
+
51
+ To get the `Array` class using this scope, you could use:
52
+
53
+ Opal.Object.$$scope.get("Array")
54
+
55
+ If a constant with the given name cannot be found, then a dispatch to the
56
+ class/module's `#const_method` is called, which by default will raise an
57
+ error.
58
+
59
+ @param [String] name the name of the constant to lookup
60
+ @returns [RubyObject]
61
+ */
47
62
  Opal.get = function(name) {
48
63
  var constant = this[name];
49
64
 
@@ -148,7 +163,7 @@
148
163
 
149
164
  // Copy all parent constants to child, unless parent is Object
150
165
  if (superklass !== ObjectClass && superklass !== BasicObjectClass) {
151
- Opal.donate_constants(superklass, klass);
166
+ donate_constants(superklass, klass);
152
167
  }
153
168
 
154
169
  // call .inherited() hook with new class on the superclass
@@ -253,7 +268,26 @@
253
268
  module.$$inc = [];
254
269
  }
255
270
 
256
- // Define new module (or return existing module)
271
+ /**
272
+ Define new module (or return existing module). The given `base` is basically
273
+ the current `self` value the `module` statement was defined in. If this is
274
+ a ruby module or class, then it is used, otherwise if the base is a ruby
275
+ object then that objects real ruby class is used (e.g. if the base is the
276
+ main object, then the top level `Object` class is used as the base).
277
+
278
+ If a module of the given name is already defined in the base, then that
279
+ instance is just returned.
280
+
281
+ If there is a class of the given name in the base, then an error is
282
+ generated instead (cannot have a class and module of same name in same base).
283
+
284
+ Otherwise, a new module is created in the base with the given name, and that
285
+ new instance is returned back (to be referenced at runtime).
286
+
287
+ @param [RubyModule or Class] base class or module this definition is inside
288
+ @param [String] id the name of the new (or existing) module
289
+ @returns [RubyModule]
290
+ */
257
291
  Opal.module = function(base, id) {
258
292
  var module;
259
293
 
@@ -303,11 +337,19 @@
303
337
  return module;
304
338
  }
305
339
 
306
- /*
307
- * Get (or prepare) the singleton class for the passed object.
308
- *
309
- * @param object [Ruby Object]
310
- */
340
+ /**
341
+ Return the singleton class for the passed object.
342
+
343
+ If the given object alredy has a singleton class, then it will be stored on
344
+ the object as the `$$meta` property. If this exists, then it is simply
345
+ returned back.
346
+
347
+ Otherwise, a new singleton object for the class or object is created, set on
348
+ the object at `$$meta` for future use, and then returned.
349
+
350
+ @param [RubyObject] object the ruby object
351
+ @returns [RubyClass] the singleton class for object
352
+ */
311
353
  Opal.get_singleton_class = function(object) {
312
354
  if (object.$$meta) {
313
355
  return object.$$meta;
@@ -320,11 +362,14 @@
320
362
  return build_object_singleton_class(object);
321
363
  };
322
364
 
323
- /*
324
- * Build the singleton class for an existing class.
325
- *
326
- * NOTE: Actually in MRI a class' singleton class inherits from its
327
- * superclass' singleton class which in turn inherits from Class;
365
+ /**
366
+ Build the singleton class for an existing class.
367
+
368
+ NOTE: Actually in MRI a class' singleton class inherits from its
369
+ superclass' singleton class which in turn inherits from Class.
370
+
371
+ @param [RubyClass] klass
372
+ @returns [RubyClass]
328
373
  */
329
374
  function build_class_singleton_class(klass) {
330
375
  var meta = new Opal.Class.$$alloc;
@@ -340,8 +385,11 @@
340
385
  return klass.$$meta = meta;
341
386
  }
342
387
 
343
- /*
344
- * Build the singleton class for a Ruby (non class) Object.
388
+ /**
389
+ Build the singleton class for a Ruby (non class) Object.
390
+
391
+ @param [RubyObject] object
392
+ @returns [RubyClass]
345
393
  */
346
394
  function build_object_singleton_class(object) {
347
395
  var orig_class = object.$$class,
@@ -358,9 +406,26 @@
358
406
  return object.$$meta = meta;
359
407
  }
360
408
 
361
- /*
362
- * The actual inclusion of a module into a class.
363
- */
409
+ /**
410
+ The actual inclusion of a module into a class.
411
+
412
+ ## Class `$$parent` and `iclass`
413
+
414
+ To handle `super` calls, every class has a `$$parent`. This parent is
415
+ used to resolve the next class for a super call. A normal class would
416
+ have this point to its superclass. However, if a class includes a module
417
+ then this would need to take into account the module. The module would
418
+ also have to then point its `$$parent` to the actual superclass. We
419
+ cannot modify modules like this, because it might be included in more
420
+ then one class. To fix this, we actually insert an `iclass` as the class'
421
+ `$$parent` which can then point to the superclass. The `iclass` acts as
422
+ a proxy to the actual module, so the `super` chain can then search it for
423
+ the required method.
424
+
425
+ @param [RubyModule] module the module to include
426
+ @param [RubyClass] klass the target class to include module into
427
+ @returns [null]
428
+ */
364
429
  Opal.append_features = function(module, klass) {
365
430
  var included = klass.$$inc;
366
431
 
@@ -406,10 +471,10 @@
406
471
  }
407
472
 
408
473
  if (klass.$$dep) {
409
- Opal.donate(klass, methods.slice(), true);
474
+ donate_methods(klass, methods.slice(), true);
410
475
  }
411
476
 
412
- Opal.donate_constants(module, klass);
477
+ donate_constants(module, klass);
413
478
  };
414
479
 
415
480
  // Boot a base class (makes instances).
@@ -534,30 +599,11 @@
534
599
  return base_scope[name] = value;
535
600
  };
536
601
 
537
- /*
538
- * constant get
539
- */
540
- Opal.cget = function(base_scope, path) {
541
- if (path == null) {
542
- path = base_scope;
543
- base_scope = Opal.Object;
544
- }
545
-
546
- var result = base_scope;
547
-
548
- path = path.split('::');
549
- while (path.length !== 0) {
550
- result = result.$const_get(path.shift());
551
- }
552
-
553
- return result;
554
- };
555
-
556
602
  /*
557
603
  * When a source module is included into the target module, we must also copy
558
604
  * its constants to the target.
559
605
  */
560
- Opal.donate_constants = function(source_mod, target_mod) {
606
+ function donate_constants(source_mod, target_mod) {
561
607
  var source_constants = source_mod.$$scope.constants,
562
608
  target_scope = target_mod.$$scope,
563
609
  target_constants = target_scope.constants;
@@ -844,6 +890,34 @@
844
890
  return [value];
845
891
  };
846
892
 
893
+ /**
894
+ Used to get a list of rest keyword arguments. Method takes the given
895
+ keyword args, i.e. the hash literal passed to the method containing all
896
+ keyword arguemnts passed to method, as well as the used args which are
897
+ the names of required and optional arguments defined. This method then
898
+ just returns all key/value pairs which have not been used, in a new
899
+ hash literal.
900
+
901
+ @param given_args [Hash] all kwargs given to method
902
+ @param used_args [Object<String: true>] all keys used as named kwargs
903
+ @return [Hash]
904
+ */
905
+ Opal.kwrestargs = function(given_args, used_args) {
906
+ var keys = [],
907
+ map = {},
908
+ key = null,
909
+ given_map = given_args.smap;
910
+
911
+ for (key in given_map) {
912
+ if (!used_args[key]) {
913
+ keys.push(key);
914
+ map[key] = given_map[key];
915
+ }
916
+ }
917
+
918
+ return Opal.hash2(keys, map);
919
+ };
920
+
847
921
  /*
848
922
  * Call a ruby method on a ruby object with some arguments:
849
923
  *
@@ -885,7 +959,7 @@
885
959
  /*
886
960
  * Donate methods for a class/module
887
961
  */
888
- Opal.donate = function(klass, defined, indirect) {
962
+ function donate_methods(klass, defined, indirect) {
889
963
  var methods = klass.$$methods, included_in = klass.$$dep;
890
964
 
891
965
  // if (!indirect) {
@@ -905,21 +979,121 @@
905
979
  }
906
980
 
907
981
  if (includee.$$dep) {
908
- Opal.donate(includee, defined, true);
982
+ donate_methods(includee, defined, true);
909
983
  }
910
984
  }
911
985
  }
912
986
  };
913
987
 
914
- Opal.defn = function(obj, jsid, body) {
915
- if (obj.$$is_mod) {
916
- obj.$$proto[jsid] = body;
917
- Opal.donate(obj, [jsid]);
988
+ /**
989
+ Define the given method on the module.
990
+
991
+ This also handles donating methods to all classes that include this
992
+ module. Method conflicts are also handled here, where a class might already
993
+ have defined a method of the same name, or another included module defined
994
+ the same method.
995
+
996
+ @param [RubyModule] module the module method defined on
997
+ @param [String] jsid javascript friendly method name (e.g. "$foo")
998
+ @param [Function] body method body of actual function
999
+ */
1000
+ function define_module_method(module, jsid, body) {
1001
+ module.$$proto[jsid] = body;
1002
+ body.$$owner = module;
1003
+
1004
+ module.$$methods.push(jsid);
1005
+
1006
+ if (module.$$module_function) {
1007
+ module[jsid] = body;
1008
+ }
1009
+
1010
+ var included_in = module.$$dep;
1011
+
1012
+ if (included_in) {
1013
+ for (var i = 0, length = included_in.length; i < length; i++) {
1014
+ var includee = included_in[i];
1015
+ var dest = includee.$$proto;
1016
+ var current = dest[jsid];
1017
+
1018
+
1019
+ if (dest.hasOwnProperty(jsid) && !current.$$donated && !current.$$stub) {
1020
+ // target class has already defined the same method name - do nothing
1021
+ }
1022
+ else if (dest.hasOwnProperty(jsid) && !current.$$stub) {
1023
+ // target class includes another module that has defined this method
1024
+ var klass_includees = includee.$$inc;
1025
+
1026
+ for (var j = 0, jj = klass_includees.length; j < jj; j++) {
1027
+ if (klass_includees[j] === current.$$owner) {
1028
+ var current_owner_index = j;
1029
+ }
1030
+ if (klass_includees[j] === module) {
1031
+ var module_index = j;
1032
+ }
1033
+ }
1034
+
1035
+ // only redefine method on class if the module was included AFTER
1036
+ // the module which defined the current method body. Also make sure
1037
+ // a module can overwrite a method it defined before
1038
+ if (current_owner_index <= module_index) {
1039
+ dest[jsid] = body;
1040
+ dest[jsid].$$donated = true;
1041
+ }
1042
+ }
1043
+ else {
1044
+ // neither a class, or module included by class, has defined method
1045
+ dest[jsid] = body;
1046
+ dest[jsid].$$donated = true;
1047
+ }
918
1048
 
919
- if (obj.$$module_function) {
920
- obj[jsid] = body;
1049
+ if (includee.$$dep) {
1050
+ donate_methods(includee, [jsid], true);
1051
+ }
921
1052
  }
922
1053
  }
1054
+ }
1055
+
1056
+ /**
1057
+ Used to define methods on an object. This is a helper method, used by the
1058
+ compiled source to define methods on special case objects when the compiler
1059
+ can not determine the destination object, or the object is a Module
1060
+ instance. This can get called by `Module#define_method` as well.
1061
+
1062
+ ## Modules
1063
+
1064
+ Any method defined on a module will come through this runtime helper.
1065
+ The method is added to the module body, and the owner of the method is
1066
+ set to be the module itself. This is used later when choosing which
1067
+ method should show on a class if more than 1 included modules define
1068
+ the same method. Finally, if the module is in `module_function` mode,
1069
+ then the method is also defined onto the module itself.
1070
+
1071
+ ## Classes
1072
+
1073
+ This helper will only be called for classes when a method is being
1074
+ defined indirectly; either through `Module#define_method`, or by a
1075
+ literal `def` method inside an `instance_eval` or `class_eval` body. In
1076
+ either case, the method is simply added to the class' prototype. A special
1077
+ exception exists for `BasicObject` and `Object`. These two classes are
1078
+ special because they are used in toll-free bridged classes. In each of
1079
+ these two cases, extra work is required to define the methods on toll-free
1080
+ bridged class' prototypes as well.
1081
+
1082
+ ## Objects
1083
+
1084
+ If a simple ruby object is the object, then the method is simply just
1085
+ defined on the object as a singleton method. This would be the case when
1086
+ a method is defined inside an `instance_eval` block.
1087
+
1088
+ @param [RubyObject or Class] obj the actual obj to define method for
1089
+ @param [String] jsid the javascript friendly method name (e.g. '$foo')
1090
+ @param [Function] body the literal javascript function used as method
1091
+ @returns [null]
1092
+ */
1093
+ Opal.defn = function(obj, jsid, body) {
1094
+ if (obj.$$is_mod) {
1095
+ define_module_method(obj, jsid, body);
1096
+ }
923
1097
  else if (obj.$$is_class) {
924
1098
  obj.$$proto[jsid] = body;
925
1099
 
@@ -927,7 +1101,7 @@
927
1101
  define_basic_object_method(jsid, body);
928
1102
  }
929
1103
  else if (obj === ObjectClass) {
930
- Opal.donate(obj, [jsid]);
1104
+ donate_methods(obj, [jsid]);
931
1105
  }
932
1106
  }
933
1107
  else {