opal 1.1.1 → 1.2.0.beta1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/build.yml +11 -5
  6. data/.gitignore +1 -0
  7. data/.jshintrc +1 -1
  8. data/Gemfile +0 -4
  9. data/HACKING.md +1 -1
  10. data/README.md +19 -15
  11. data/UNRELEASED.md +41 -0
  12. data/benchmark-ips/bm_array_unshift.rb +7 -0
  13. data/bin/opal-mspec +2 -0
  14. data/docs/compiler.md +1 -1
  15. data/examples/rack/Gemfile +0 -1
  16. data/examples/rack/Gemfile.lock +0 -4
  17. data/lib/opal/cli.rb +1 -0
  18. data/lib/opal/cli_options.rb +4 -0
  19. data/lib/opal/cli_runners/nodejs.rb +4 -0
  20. data/lib/opal/cli_runners/source-map-support-browser.js +3 -1
  21. data/lib/opal/cli_runners/source-map-support-node.js +3 -1
  22. data/lib/opal/cli_runners/source-map-support.js +3 -1
  23. data/lib/opal/compiler.rb +2 -2
  24. data/lib/opal/nodes/args/arity_check.rb +1 -0
  25. data/lib/opal/nodes/args/parameters.rb +6 -0
  26. data/lib/opal/nodes/class.rb +1 -13
  27. data/lib/opal/nodes/literal.rb +14 -7
  28. data/lib/opal/nodes/module.rb +13 -9
  29. data/lib/opal/nodes/variables.rb +13 -4
  30. data/lib/opal/nodes/while.rb +54 -17
  31. data/lib/opal/parser.rb +1 -5
  32. data/lib/opal/parser/patch.rb +34 -0
  33. data/lib/opal/repl.rb +7 -0
  34. data/lib/opal/rewriter.rb +2 -0
  35. data/lib/opal/rewriters/arguments.rb +4 -1
  36. data/lib/opal/rewriters/forward_args.rb +54 -0
  37. data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
  38. data/lib/opal/rewriters/opal_engine_check.rb +5 -7
  39. data/lib/opal/version.rb +1 -1
  40. data/opal/corelib/array.rb +42 -20
  41. data/opal/corelib/array/pack.rb +6 -1
  42. data/opal/corelib/complex.rb +2 -0
  43. data/opal/corelib/constants.rb +3 -3
  44. data/opal/corelib/hash.rb +36 -38
  45. data/opal/corelib/module.rb +2 -7
  46. data/opal/corelib/number.rb +2 -180
  47. data/opal/corelib/numeric.rb +156 -0
  48. data/opal/corelib/object_space.rb +102 -0
  49. data/opal/corelib/random.rb +31 -66
  50. data/opal/corelib/random/formatter.rb +122 -0
  51. data/opal/corelib/range.rb +50 -19
  52. data/opal/corelib/runtime.js +82 -21
  53. data/opal/corelib/string.rb +86 -52
  54. data/opal/corelib/string/encoding.rb +140 -25
  55. data/opal/corelib/string/unpack.rb +26 -40
  56. data/opal/opal.rb +1 -0
  57. data/opal/opal/full.rb +1 -0
  58. data/spec/filters/bugs/array.rb +0 -22
  59. data/spec/filters/bugs/basicobject.rb +3 -0
  60. data/spec/filters/bugs/encoding.rb +0 -2
  61. data/spec/filters/bugs/exception.rb +1 -0
  62. data/spec/filters/bugs/float.rb +0 -2
  63. data/spec/filters/bugs/hash.rb +2 -7
  64. data/spec/filters/bugs/integer.rb +0 -2
  65. data/spec/filters/bugs/kernel.rb +16 -3
  66. data/spec/filters/bugs/language.rb +6 -14
  67. data/spec/filters/bugs/marshal.rb +1 -3
  68. data/spec/filters/bugs/module.rb +16 -1
  69. data/spec/filters/bugs/numeric.rb +4 -12
  70. data/spec/filters/bugs/objectspace.rb +67 -0
  71. data/spec/filters/bugs/pack_unpack.rb +0 -9
  72. data/spec/filters/bugs/pathname.rb +1 -0
  73. data/spec/filters/bugs/proc.rb +8 -0
  74. data/spec/filters/bugs/random.rb +3 -6
  75. data/spec/filters/bugs/range.rb +83 -113
  76. data/spec/filters/bugs/set.rb +2 -0
  77. data/spec/filters/bugs/string.rb +31 -70
  78. data/spec/filters/bugs/struct.rb +2 -0
  79. data/spec/filters/bugs/time.rb +8 -2
  80. data/spec/filters/unsupported/float.rb +3 -0
  81. data/spec/filters/unsupported/freeze.rb +1 -0
  82. data/spec/filters/unsupported/integer.rb +3 -0
  83. data/spec/filters/unsupported/refinements.rb +5 -0
  84. data/spec/filters/unsupported/string.rb +100 -95
  85. data/spec/filters/unsupported/time.rb +4 -0
  86. data/spec/lib/compiler_spec.rb +16 -0
  87. data/spec/lib/rewriters/forward_args_spec.rb +61 -0
  88. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
  89. data/spec/lib/rewriters/numblocks_spec.rb +44 -0
  90. data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
  91. data/spec/opal/core/language/forward_args_spec.rb +53 -0
  92. data/spec/opal/core/language/infinite_range_spec.rb +13 -0
  93. data/spec/opal/core/language/memoization_spec.rb +16 -0
  94. data/spec/opal/core/module_spec.rb +38 -2
  95. data/spec/opal/core/number/to_i_spec.rb +28 -0
  96. data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
  97. data/spec/opal/core/runtime/constants_spec.rb +20 -1
  98. data/spec/opal/core/string/subclassing_spec.rb +16 -0
  99. data/spec/opal/core/string/unpack_spec.rb +22 -0
  100. data/spec/opal/core/string_spec.rb +4 -4
  101. data/spec/ruby_specs +4 -1
  102. data/stdlib/json.rb +3 -1
  103. data/stdlib/securerandom.rb +55 -35
  104. data/tasks/testing.rake +6 -3
  105. data/test/nodejs/test_string.rb +25 -0
  106. data/vendored-minitest/minitest/assertions.rb +2 -0
  107. metadata +31 -10
  108. data/lib/opal/parser/with_c_lexer.rb +0 -15
@@ -9,7 +9,7 @@ class Range
9
9
 
10
10
  def initialize(first, last, exclude = false)
11
11
  raise NameError, "'initialize' called twice" if @begin
12
- raise ArgumentError, 'bad value for range' unless first <=> last
12
+ raise ArgumentError, 'bad value for range' unless first <=> last || first.nil? || last.nil?
13
13
 
14
14
  @begin = first
15
15
  @end = last
@@ -20,15 +20,35 @@ class Range
20
20
  include? value
21
21
  end
22
22
 
23
+ %x{
24
+ function is_infinite(self) {
25
+ if (self.begin === nil || self.end === nil ||
26
+ self.begin === -Infinity || self.end === Infinity ||
27
+ self.begin === Infinity || self.end === -Infinity) return true;
28
+ return false;
29
+ }
30
+ }
31
+
32
+ def count(&block)
33
+ if !block_given? && `is_infinite(self)`
34
+ return Float::INFINITY
35
+ end
36
+ super
37
+ end
38
+
39
+ def to_a
40
+ raise TypeError, 'cannot convert endless range to an array' if `is_infinite(self)`
41
+ super
42
+ end
43
+
23
44
  def cover?(value)
24
- beg_cmp = (@begin <=> value)
25
- return false unless beg_cmp && beg_cmp <= 0
26
- end_cmp = (value <=> @end)
45
+ beg_cmp = (@begin.nil? && -1) || (@begin <=> value) || false
46
+ end_cmp = (@end.nil? && -1) || (value <=> @end) || false
27
47
  if @excl
28
48
  end_cmp && end_cmp < 0
29
49
  else
30
50
  end_cmp && end_cmp <= 0
31
- end
51
+ end && beg_cmp && beg_cmp <= 0
32
52
  end
33
53
 
34
54
  def each(&block)
@@ -62,7 +82,7 @@ class Range
62
82
  raise TypeError, "can't iterate from #{current.class}"
63
83
  end
64
84
 
65
- while (current <=> last) < 0
85
+ while @end.nil? || (current <=> last) < 0
66
86
  yield current
67
87
 
68
88
  current = current.succ
@@ -88,6 +108,7 @@ class Range
88
108
  end
89
109
 
90
110
  def first(n = undefined)
111
+ raise RangeError, 'cannot get the minimum of beginless range' if @begin.nil?
91
112
  return @begin if `n == null`
92
113
  super
93
114
  end
@@ -95,17 +116,19 @@ class Range
95
116
  alias include? cover?
96
117
 
97
118
  def last(n = undefined)
119
+ raise RangeError, 'cannot get the maximum of endless range' if @end.nil?
98
120
  return @end if `n == null`
99
121
  to_a.last(n)
100
122
  end
101
123
 
102
124
  # FIXME: currently hardcoded to assume range holds numerics
103
125
  def max
104
- if block_given?
126
+ if @end.nil?
127
+ raise RangeError, 'cannot get the maximum of endless range'
128
+ elsif block_given?
105
129
  super
106
- elsif @begin > @end
107
- nil
108
- elsif @excl && @begin == @end
130
+ elsif !@begin.nil? && (@begin > @end ||
131
+ @excl && @begin == @end)
109
132
  nil
110
133
  else
111
134
  `#{@excl} ? #{@end} - 1 : #{@end}`
@@ -115,11 +138,12 @@ class Range
115
138
  alias member? cover?
116
139
 
117
140
  def min
118
- if block_given?
141
+ if @begin.nil?
142
+ raise RangeError, 'cannot get the minimum of beginless range'
143
+ elsif block_given?
119
144
  super
120
- elsif @begin > @end
121
- nil
122
- elsif @excl && @begin == @end
145
+ elsif !@end.nil? && (@begin > @end ||
146
+ @excl && @begin == @end)
123
147
  nil
124
148
  else
125
149
  @begin
@@ -127,14 +151,17 @@ class Range
127
151
  end
128
152
 
129
153
  def size
154
+ infinity = Float::INFINITY
155
+
156
+ return 0 if (@begin == infinity && !@end.nil?) || (@end == -infinity && !@begin.nil?)
157
+ return infinity if `is_infinite(self)`
158
+ return nil unless Numeric === @begin && Numeric === @end
159
+
130
160
  range_begin = @begin
131
161
  range_end = @end
132
162
  range_end -= 1 if @excl
133
163
 
134
- return nil unless Numeric === range_begin && Numeric === range_end
135
164
  return 0 if range_end < range_begin
136
- infinity = Float::INFINITY
137
- return infinity if [range_begin.abs, range_end.abs].include?(infinity)
138
165
 
139
166
  `Math.abs(range_end - range_begin) + 1`.to_i
140
167
  end
@@ -228,6 +255,10 @@ class Range
228
255
  def bsearch(&block)
229
256
  return enum_for(:bsearch) unless block_given?
230
257
 
258
+ if `is_infinite(self) && (self.begin.$$is_number || self.end.$$is_number)`
259
+ raise NotImplementedError, "Can't #bsearch an infinite range"
260
+ end
261
+
231
262
  unless `self.begin.$$is_number && self.end.$$is_number`
232
263
  raise TypeError, "can't do binary search for #{@begin.class}"
233
264
  end
@@ -236,11 +267,11 @@ class Range
236
267
  end
237
268
 
238
269
  def to_s
239
- "#{@begin}#{@excl ? '...' : '..'}#{@end}"
270
+ "#{@begin || ''}#{@excl ? '...' : '..'}#{@end || ''}"
240
271
  end
241
272
 
242
273
  def inspect
243
- "#{@begin.inspect}#{@excl ? '...' : '..'}#{@end.inspect}"
274
+ "#{@begin && @begin.inspect}#{@excl ? '...' : '..'}#{@end && @end.inspect}"
244
275
  end
245
276
 
246
277
  def marshal_load(args)
@@ -533,7 +533,7 @@
533
533
  Opal.klass = function(scope, superclass, name) {
534
534
  var bridged;
535
535
 
536
- if (scope == null) {
536
+ if (scope == null || scope == '::') {
537
537
  // Global scope
538
538
  scope = _Object;
539
539
  } else if (!scope.$$is_class && !scope.$$is_module) {
@@ -542,9 +542,20 @@
542
542
  }
543
543
 
544
544
  // If the superclass is not an Opal-generated class then we're bridging a native JS class
545
- if (superclass != null && !superclass.hasOwnProperty('$$is_class')) {
546
- bridged = superclass;
547
- superclass = _Object;
545
+ if (
546
+ superclass != null && (!superclass.hasOwnProperty || (
547
+ superclass.hasOwnProperty && !superclass.hasOwnProperty('$$is_class')
548
+ ))
549
+ ) {
550
+ if (superclass.constructor && superclass.constructor.name == "Function") {
551
+ bridged = superclass;
552
+ superclass = _Object;
553
+ } else {
554
+ throw Opal.TypeError.$new("superclass must be a Class (" + (
555
+ (superclass.constructor && (superclass.constructor.name || superclass.constructor.$$name)) ||
556
+ typeof(superclass)
557
+ ) + " given)");
558
+ }
548
559
  }
549
560
 
550
561
  var klass = find_existing_class(scope, name);
@@ -643,7 +654,7 @@
643
654
  Opal.module = function(scope, name) {
644
655
  var module;
645
656
 
646
- if (scope == null) {
657
+ if (scope == null || scope == '::') {
647
658
  // Global scope
648
659
  scope = _Object;
649
660
  } else if (!scope.$$is_class && !scope.$$is_module) {
@@ -884,6 +895,31 @@
884
895
  return value;
885
896
  };
886
897
 
898
+ // Gets class variable with specified +name+ from provided +module+
899
+ //
900
+ // @param module [Module]
901
+ // @param name [String]
902
+ Opal.class_variable_get = function(module, name, tolerant) {
903
+ if ($has_own.call(module.$$cvars, name))
904
+ return module.$$cvars[name];
905
+
906
+ var ancestors = Opal.ancestors(module),
907
+ i, length = ancestors.length;
908
+
909
+ for (i = 0; i < length; i++) {
910
+ var ancestor = ancestors[i];
911
+
912
+ if ($has_own.call(ancestor.$$cvars, name)) {
913
+ return ancestor.$$cvars[name];
914
+ }
915
+ }
916
+
917
+ if (!tolerant)
918
+ throw Opal.NameError.$new('uninitialized class variable '+name+' in '+module.$name());
919
+
920
+ return nil;
921
+ }
922
+
887
923
  function isRoot(proto) {
888
924
  return proto.hasOwnProperty('$$iclass') && proto.hasOwnProperty('$$root');
889
925
  }
@@ -997,10 +1033,10 @@
997
1033
  // because there are no intermediate classes between `parent` and `next ancestor`.
998
1034
  // It doesn't break any prototypes of other objects as we don't change class references.
999
1035
 
1000
- var proto = includer.$$prototype, parent = proto, module_iclass = Object.getPrototypeOf(parent);
1036
+ var parent = includer.$$prototype, module_iclass = Object.getPrototypeOf(parent);
1001
1037
 
1002
1038
  while (module_iclass != null) {
1003
- if (isRoot(module_iclass) && module_iclass.$$module === module) {
1039
+ if (module_iclass.$$module === module && isRoot(module_iclass)) {
1004
1040
  break;
1005
1041
  }
1006
1042
 
@@ -1008,15 +1044,23 @@
1008
1044
  module_iclass = Object.getPrototypeOf(module_iclass);
1009
1045
  }
1010
1046
 
1011
- var next_ancestor = Object.getPrototypeOf(module_iclass);
1047
+ if (module_iclass) {
1048
+ // module has been directly included
1049
+ var next_ancestor = Object.getPrototypeOf(module_iclass);
1012
1050
 
1013
- // skip non-root iclasses (that were recursively included)
1014
- while (next_ancestor.hasOwnProperty('$$iclass') && !isRoot(next_ancestor)) {
1015
- next_ancestor = Object.getPrototypeOf(next_ancestor);
1016
- }
1051
+ // skip non-root iclasses (that were recursively included)
1052
+ while (next_ancestor.hasOwnProperty('$$iclass') && !isRoot(next_ancestor)) {
1053
+ next_ancestor = Object.getPrototypeOf(next_ancestor);
1054
+ }
1017
1055
 
1018
- start_chain_after = parent;
1019
- end_chain_on = next_ancestor;
1056
+ start_chain_after = parent;
1057
+ end_chain_on = next_ancestor;
1058
+ } else {
1059
+ // module has not been directly included but was in ancestor chain because it was included by another module
1060
+ // include it directly
1061
+ start_chain_after = includer.$$prototype;
1062
+ end_chain_on = Object.getPrototypeOf(includer.$$prototype);
1063
+ }
1020
1064
  }
1021
1065
 
1022
1066
  $set_proto(start_chain_after, chain.first);
@@ -2439,29 +2483,46 @@
2439
2483
 
2440
2484
  // Sets the encoding on a string, will treat string literals as frozen strings
2441
2485
  // raising a FrozenError.
2442
- // @param str [String] the string on which the encoding should be set.
2486
+ //
2487
+ // @param str [String] the string on which the encoding should be set
2443
2488
  // @param name [String] the canonical name of the encoding
2444
- Opal.set_encoding = function(str, name) {
2445
- if (typeof str === 'string')
2489
+ // @param type [String] possible values are either `"encoding"`, `"internal_encoding"`, or `undefined
2490
+ Opal.set_encoding = function(str, name, type) {
2491
+ if (typeof type === "undefined") type = "encoding";
2492
+ if (typeof str === 'string' || str.$$frozen === true)
2446
2493
  throw Opal.FrozenError.$new("can't modify frozen String");
2447
2494
 
2448
- var encoding = Opal.encodings[name];
2495
+ var encoding = Opal.find_encoding(name);
2449
2496
 
2450
- if (encoding === str.encoding) { return str; }
2497
+ if (encoding === str[type]) { return str; }
2451
2498
 
2452
- str.encoding = encoding;
2499
+ str[type] = encoding;
2453
2500
 
2454
2501
  return str;
2455
2502
  };
2456
2503
 
2504
+ // Fetches the encoding for the given name or raises ArgumentError.
2505
+ Opal.find_encoding = function(name) {
2506
+ var register = Opal.encodings;
2507
+ var encoding = register[name] || register[name.toUpperCase()];
2508
+ if (!encoding) throw Opal.ArgumentError.$new("unknown encoding name - " + name);
2509
+ return encoding;
2510
+ }
2511
+
2457
2512
  // @returns a String object with the encoding set from a string literal
2458
2513
  Opal.enc = function(str, name) {
2459
2514
  var dup = new String(str);
2460
- Opal.set_encoding(dup, name);
2515
+ dup = Opal.set_encoding(dup, name);
2461
2516
  dup.internal_encoding = dup.encoding;
2462
2517
  return dup
2463
2518
  }
2464
2519
 
2520
+ // @returns a String object with the internal encoding set to Binary
2521
+ Opal.binary = function(str) {
2522
+ var dup = new String(str);
2523
+ return Opal.set_encoding(dup, "binary", "internal_encoding");
2524
+ }
2525
+
2465
2526
 
2466
2527
  // Initialization
2467
2528
  // --------------
@@ -29,18 +29,26 @@ class String < `String`
29
29
  Opal.coerce_to?(what, String, :to_str)
30
30
  end
31
31
 
32
- def self.new(str = '')
33
- str = `$coerce_to(str, #{String}, 'to_str')`
34
- `new self.$$constructor(str)`
35
- end
36
-
37
- def initialize(str = undefined)
32
+ def self.new(*args)
38
33
  %x{
39
- if (str === undefined) {
40
- return self;
41
- }
34
+ var str = args[0] || "";
35
+ var opts = args[args.length-1];
36
+ str = $coerce_to(str, #{String}, 'to_str');
37
+ if (opts && opts.$$is_hash) {
38
+ if (opts.$$smap.encoding) str = str.$force_encoding(opts.$$smap.encoding);
39
+ }
40
+ str = new self.$$constructor(str);
41
+ if (!str.$initialize.$$pristine) #{`str`.initialize(*args)};
42
+ return str;
42
43
  }
43
- raise NotImplementedError, 'Mutable strings are not supported in Opal.'
44
+ end
45
+
46
+ # Our initialize method does nothing, the string value setup is being
47
+ # done by String.new. Therefore not all kinds of subclassing will work.
48
+ # As a rule of thumb, when subclassing String, either make sure to override
49
+ # .new or make sure that the first argument given to a constructor is
50
+ # a string we want our subclass-string to hold.
51
+ def initialize(str = undefined, encoding: nil, capacity: nil)
44
52
  end
45
53
 
46
54
  def %(data)
@@ -92,7 +100,14 @@ class String < `String`
92
100
  def +(other)
93
101
  other = `$coerce_to(#{other}, #{String}, 'to_str')`
94
102
 
95
- `self + #{other.to_s}`
103
+ %x{
104
+ if (other == "" && self.$$class === Opal.String) return #{self};
105
+ if (self == "" && other.$$class === Opal.String) return #{other};
106
+ var out = self + other;
107
+ if (self.encoding === out.encoding && other.encoding === out.encoding) return out;
108
+ if (self.encoding.name === "UTF-8" || other.encoding.name === "UTF-8") return out;
109
+ return Opal.enc(out, self.encoding);
110
+ }
96
111
  end
97
112
 
98
113
  def <=>(other)
@@ -240,7 +255,7 @@ class String < `String`
240
255
  alias byteslice []
241
256
 
242
257
  def b
243
- force_encoding('binary')
258
+ `new String(#{self})`.force_encoding('binary')
244
259
  end
245
260
 
246
261
  def capitalize
@@ -289,12 +304,6 @@ class String < `String`
289
304
  }
290
305
  end
291
306
 
292
- def chars(&block)
293
- return each_char.to_a unless block
294
-
295
- each_char(&block)
296
- end
297
-
298
307
  def chomp(separator = $/)
299
308
  return self if `separator === nil || self.length === 0`
300
309
 
@@ -416,18 +425,6 @@ class String < `String`
416
425
  `self.$$cast(self.toLowerCase())`
417
426
  end
418
427
 
419
- def each_char(&block)
420
- return enum_for(:each_char) { size } unless block_given?
421
-
422
- %x{
423
- for (var i = 0, length = self.length; i < length; i++) {
424
- Opal.yield1(block, self.charAt(i));
425
- }
426
- }
427
-
428
- self
429
- end
430
-
431
428
  def each_line(separator = $/, &block)
432
429
  return enum_for :each_line, separator unless block_given?
433
430
 
@@ -645,7 +642,13 @@ class String < `String`
645
642
  '\\': '\\\\'
646
643
  },
647
644
  escaped = self.replace(escapable, function (chr) {
648
- return meta[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16).toUpperCase()).slice(-4);
645
+ if (meta[chr]) return meta[chr];
646
+ chr = chr.charCodeAt(0);
647
+ if (chr <= 0xff && (self.encoding["$binary?"]() || self.internal_encoding["$binary?"]())) {
648
+ return '\\x' + ('00' + chr.toString(16).toUpperCase()).slice(-2);
649
+ } else {
650
+ return '\\u' + ('0000' + chr.toString(16).toUpperCase()).slice(-4);
651
+ }
649
652
  });
650
653
  return '"' + escaped.replace(/\#[\$\@\{]/g, '\\$&') + '"';
651
654
  }
@@ -660,10 +663,6 @@ class String < `String`
660
663
  block ? self : e.to_a
661
664
  end
662
665
 
663
- def length
664
- `self.length`
665
- end
666
-
667
666
  def ljust(width, padstr = ' ')
668
667
  width = `$coerce_to(#{width}, #{Integer}, 'to_int')`
669
668
  padstr = `$coerce_to(#{padstr}, #{String}, 'to_str')`.to_s
@@ -694,17 +693,10 @@ class String < `String`
694
693
 
695
694
  def ascii_only?
696
695
  # non-ASCII-compatible encoding must return false
697
- # NOTE: Encoding::UTF_16LE is also non-ASCII-compatible encoding,
698
- # but since the default encoding in JavaScript is UTF_16LE,
699
- # we cannot return false otherwise the following will (incorrectly) return false: "hello".ascii_only?
700
- # In other words, we cannot tell the difference between:
701
- # - "hello".force_encoding("UTF-16LE")
702
- # - "hello"
703
- # The problem is that "ascii_only" should return false in the first case and true in the second case.
704
- if encoding == Encoding::UTF_16BE
705
- return false
706
- end
707
- `/^[\x00-\x7F]*$/.test(self)`
696
+ %x{
697
+ if (!self.encoding.ascii) return false;
698
+ return /^[\x00-\x7F]*$/.test(self);
699
+ }
708
700
  end
709
701
 
710
702
  def match(pattern, pos = undefined, &block)
@@ -845,7 +837,14 @@ class String < `String`
845
837
  end
846
838
 
847
839
  def ord
848
- `self.charCodeAt(0)`
840
+ %x{
841
+ if (typeof self.codePointAt === "function") {
842
+ return self.codePointAt(0);
843
+ }
844
+ else {
845
+ return self.charCodeAt(0);
846
+ }
847
+ }
849
848
  end
850
849
 
851
850
  def partition(sep)
@@ -1022,8 +1021,6 @@ class String < `String`
1022
1021
  }
1023
1022
  end
1024
1023
 
1025
- alias size length
1026
-
1027
1024
  alias slice []
1028
1025
 
1029
1026
  def split(pattern = undefined, limit = undefined)
@@ -1141,10 +1138,22 @@ class String < `String`
1141
1138
  def start_with?(*prefixes)
1142
1139
  %x{
1143
1140
  for (var i = 0, length = prefixes.length; i < length; i++) {
1144
- var prefix = $coerce_to(prefixes[i], #{String}, 'to_str').$to_s();
1141
+ if (prefixes[i].$$is_regexp) {
1142
+ var regexp = prefixes[i];
1143
+ var match = regexp.exec(self);
1145
1144
 
1146
- if (self.indexOf(prefix) === 0) {
1147
- return true;
1145
+ if (match != null && match.index === 0) {
1146
+ #{$~ = MatchData.new(`regexp`, `match`)};
1147
+ return true;
1148
+ } else {
1149
+ #{$~ = nil}
1150
+ }
1151
+ } else {
1152
+ var prefix = $coerce_to(prefixes[i], #{String}, 'to_str').$to_s();
1153
+
1154
+ if (self.indexOf(prefix) === 0) {
1155
+ return true;
1156
+ }
1148
1157
  }
1149
1158
  }
1150
1159
 
@@ -1841,6 +1850,31 @@ class String < `String`
1841
1850
  def unpack1(format)
1842
1851
  raise "To use String#unpack1, you must first require 'corelib/string/unpack'."
1843
1852
  end
1853
+
1854
+ def freeze
1855
+ %x{
1856
+ if (typeof self === 'string') return self;
1857
+ self.$$frozen = true;
1858
+ return self;
1859
+ }
1860
+ end
1861
+
1862
+ alias +@ dup
1863
+
1864
+ def -@
1865
+ %x{
1866
+ if (typeof self === 'string') return self;
1867
+ if (self.$$frozen === true) return self;
1868
+ if (self.encoding.name == 'UTF-8' && self.internal_encoding.name == 'UTF-8') return self.toString();
1869
+ return self.$dup().$freeze();
1870
+ }
1871
+ end
1872
+
1873
+ def frozen?
1874
+ `typeof self === 'string' || self.$$frozen === true`
1875
+ end
1876
+
1877
+ Opal.pristine self, :initialize
1844
1878
  end
1845
1879
 
1846
1880
  Symbol = String