opal 1.1.0.rc1 → 1.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +4 -4
  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/CHANGELOG.md +42 -1
  9. data/Gemfile +0 -4
  10. data/HACKING.md +1 -1
  11. data/LICENSE +1 -1
  12. data/README.md +20 -16
  13. data/UNRELEASED.md +32 -95
  14. data/benchmark-ips/bm_array_unshift.rb +7 -0
  15. data/benchmark-ips/bm_js_symbols_vs_strings.rb +7 -2
  16. data/bin/build-browser-source-map-support +2 -3
  17. data/bin/opal-mspec +2 -0
  18. data/docs/compiler.md +1 -1
  19. data/examples/rack/Gemfile +0 -1
  20. data/examples/rack/Gemfile.lock +0 -4
  21. data/lib/opal/cli.rb +1 -0
  22. data/lib/opal/cli_options.rb +4 -0
  23. data/lib/opal/cli_runners/nodejs.rb +5 -1
  24. data/lib/opal/cli_runners/source-map-support-browser.js +8 -2
  25. data/lib/opal/cli_runners/source-map-support-node.js +3706 -0
  26. data/lib/opal/cli_runners/source-map-support.js +3 -1
  27. data/lib/opal/compiler.rb +2 -2
  28. data/lib/opal/nodes/args/arity_check.rb +1 -0
  29. data/lib/opal/nodes/args/parameters.rb +6 -0
  30. data/lib/opal/nodes/class.rb +1 -13
  31. data/lib/opal/nodes/literal.rb +14 -7
  32. data/lib/opal/nodes/module.rb +13 -9
  33. data/lib/opal/nodes/variables.rb +13 -4
  34. data/lib/opal/nodes/while.rb +54 -17
  35. data/lib/opal/parser.rb +1 -5
  36. data/lib/opal/parser/patch.rb +34 -0
  37. data/lib/opal/repl.rb +7 -0
  38. data/lib/opal/rewriter.rb +2 -0
  39. data/lib/opal/rewriters/arguments.rb +4 -1
  40. data/lib/opal/rewriters/forward_args.rb +54 -0
  41. data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
  42. data/lib/opal/rewriters/opal_engine_check.rb +5 -7
  43. data/lib/opal/version.rb +1 -1
  44. data/opal.gemspec +1 -1
  45. data/opal/corelib/array.rb +42 -20
  46. data/opal/corelib/array/pack.rb +6 -1
  47. data/opal/corelib/complex.rb +2 -0
  48. data/opal/corelib/constants.rb +3 -3
  49. data/opal/corelib/hash.rb +36 -38
  50. data/opal/corelib/module.rb +2 -7
  51. data/opal/corelib/number.rb +2 -180
  52. data/opal/corelib/numeric.rb +156 -0
  53. data/opal/corelib/object_space.rb +102 -0
  54. data/opal/corelib/random.rb +31 -66
  55. data/opal/corelib/random/formatter.rb +122 -0
  56. data/opal/corelib/range.rb +50 -19
  57. data/opal/corelib/runtime.js +82 -21
  58. data/opal/corelib/string.rb +86 -52
  59. data/opal/corelib/string/encoding.rb +140 -25
  60. data/opal/corelib/string/unpack.rb +26 -40
  61. data/opal/opal.rb +1 -0
  62. data/opal/opal/full.rb +1 -0
  63. data/package.json +1 -1
  64. data/spec/filters/bugs/array.rb +0 -22
  65. data/spec/filters/bugs/basicobject.rb +3 -0
  66. data/spec/filters/bugs/encoding.rb +0 -2
  67. data/spec/filters/bugs/exception.rb +1 -0
  68. data/spec/filters/bugs/float.rb +0 -2
  69. data/spec/filters/bugs/hash.rb +2 -7
  70. data/spec/filters/bugs/integer.rb +0 -2
  71. data/spec/filters/bugs/kernel.rb +16 -3
  72. data/spec/filters/bugs/language.rb +6 -14
  73. data/spec/filters/bugs/marshal.rb +1 -3
  74. data/spec/filters/bugs/module.rb +16 -1
  75. data/spec/filters/bugs/numeric.rb +4 -12
  76. data/spec/filters/bugs/objectspace.rb +67 -0
  77. data/spec/filters/bugs/pack_unpack.rb +0 -9
  78. data/spec/filters/bugs/pathname.rb +1 -0
  79. data/spec/filters/bugs/proc.rb +8 -0
  80. data/spec/filters/bugs/random.rb +3 -6
  81. data/spec/filters/bugs/range.rb +83 -113
  82. data/spec/filters/bugs/set.rb +2 -0
  83. data/spec/filters/bugs/string.rb +31 -70
  84. data/spec/filters/bugs/struct.rb +2 -0
  85. data/spec/filters/bugs/time.rb +8 -2
  86. data/spec/filters/unsupported/float.rb +3 -0
  87. data/spec/filters/unsupported/freeze.rb +1 -0
  88. data/spec/filters/unsupported/integer.rb +3 -0
  89. data/spec/filters/unsupported/refinements.rb +5 -0
  90. data/spec/filters/unsupported/string.rb +100 -95
  91. data/spec/filters/unsupported/time.rb +4 -0
  92. data/spec/lib/compiler_spec.rb +16 -0
  93. data/spec/lib/rewriters/forward_args_spec.rb +61 -0
  94. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
  95. data/spec/lib/rewriters/numblocks_spec.rb +44 -0
  96. data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
  97. data/spec/opal/core/language/forward_args_spec.rb +53 -0
  98. data/spec/opal/core/language/infinite_range_spec.rb +13 -0
  99. data/spec/opal/core/language/memoization_spec.rb +16 -0
  100. data/spec/opal/core/module_spec.rb +38 -2
  101. data/spec/opal/core/number/to_i_spec.rb +28 -0
  102. data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
  103. data/spec/opal/core/runtime/constants_spec.rb +20 -1
  104. data/spec/opal/core/string/subclassing_spec.rb +16 -0
  105. data/spec/opal/core/string/unpack_spec.rb +22 -0
  106. data/spec/opal/core/string_spec.rb +4 -4
  107. data/spec/ruby_specs +4 -1
  108. data/stdlib/json.rb +3 -1
  109. data/stdlib/securerandom.rb +55 -35
  110. data/tasks/testing.rake +6 -3
  111. data/test/nodejs/test_string.rb +25 -0
  112. data/vendored-minitest/minitest/assertions.rb +2 -0
  113. metadata +35 -12
  114. 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