opal 1.8.0.alpha1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.eslintrc.js +1 -0
- data/CHANGELOG.md +119 -2
- data/README.md +7 -7
- data/UNRELEASED.md +1 -78
- data/docs/releasing.md +8 -16
- data/lib/opal/cli_runners/chrome.rb +5 -1
- data/lib/opal/nodes/closure.rb +15 -7
- data/lib/opal/nodes/defined.rb +1 -1
- data/lib/opal/nodes/literal.rb +10 -6
- data/lib/opal/nodes/rescue.rb +1 -1
- data/lib/opal/simple_server.rb +2 -2
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +39 -21
- data/opal/corelib/class.rb +26 -8
- data/opal/corelib/complex.rb +1 -1
- data/opal/corelib/constants.rb +2 -2
- data/opal/corelib/enumerator/arithmetic_sequence.rb +1 -1
- data/opal/corelib/error.rb +10 -0
- data/opal/corelib/hash.rb +36 -20
- data/opal/corelib/kernel.rb +0 -1
- data/opal/corelib/method.rb +1 -0
- data/opal/corelib/module.rb +82 -22
- data/opal/corelib/number.rb +28 -1
- data/opal/corelib/range.rb +55 -11
- data/opal/corelib/rational.rb +1 -1
- data/opal/corelib/runtime.js +132 -50
- data/opal/corelib/string.rb +36 -8
- data/opal/corelib/struct.rb +1 -1
- data/opal/corelib/time.rb +1 -1
- data/spec/filters/bugs/array.rb +58 -0
- data/spec/filters/bugs/basicobject.rb +7 -0
- data/spec/filters/bugs/bigdecimal.rb +1 -2
- data/spec/filters/bugs/binding.rb +1 -0
- data/spec/filters/bugs/cgi.rb +40 -0
- data/spec/filters/bugs/class.rb +2 -3
- data/spec/filters/bugs/complex.rb +3 -0
- data/spec/filters/bugs/date.rb +5 -2
- data/spec/filters/bugs/datetime.rb +1 -0
- data/spec/filters/bugs/delegate.rb +1 -2
- data/spec/filters/bugs/encoding.rb +1 -1
- data/spec/filters/bugs/enumerable.rb +11 -0
- data/spec/filters/bugs/enumerator.rb +15 -2
- data/spec/filters/bugs/exception.rb +9 -4
- data/spec/filters/bugs/file.rb +2 -0
- data/spec/filters/bugs/float.rb +1 -0
- data/spec/filters/bugs/freeze.rb +5 -49
- data/spec/filters/bugs/hash.rb +1 -0
- data/spec/filters/bugs/integer.rb +5 -6
- data/spec/filters/bugs/kernel.rb +12 -6
- data/spec/filters/bugs/language.rb +33 -15
- data/spec/filters/bugs/marshal.rb +63 -4
- data/spec/filters/bugs/method.rb +2 -13
- data/spec/filters/bugs/module.rb +19 -17
- data/spec/filters/bugs/objectspace.rb +2 -0
- data/spec/filters/bugs/pathname.rb +1 -0
- data/spec/filters/bugs/proc.rb +4 -2
- data/spec/filters/bugs/random.rb +0 -3
- data/spec/filters/bugs/range.rb +3 -17
- data/spec/filters/bugs/rational.rb +2 -0
- data/spec/filters/bugs/refinement.rb +19 -0
- data/spec/filters/bugs/regexp.rb +27 -5
- data/spec/filters/bugs/ruby-32.rb +0 -4
- data/spec/filters/bugs/set.rb +10 -2
- data/spec/filters/bugs/singleton.rb +0 -2
- data/spec/filters/bugs/string.rb +140 -1
- data/spec/filters/bugs/struct.rb +15 -5
- data/spec/filters/bugs/time.rb +56 -2
- data/spec/filters/bugs/trace_point.rb +1 -0
- data/spec/filters/bugs/unboundmethod.rb +4 -9
- data/spec/filters/bugs/warnings.rb +0 -1
- data/spec/filters/platform/firefox/exception.rb +3 -3
- data/spec/filters/platform/firefox/kernel.rb +1 -0
- data/spec/filters/platform/safari/exception.rb +2 -2
- data/spec/filters/platform/safari/float.rb +1 -0
- data/spec/filters/platform/safari/kernel.rb +1 -0
- data/spec/filters/platform/safari/literal_regexp.rb +2 -2
- data/spec/filters/unsupported/hash.rb +1 -1
- data/spec/lib/compiler_spec.rb +4 -0
- data/spec/opal/core/class/clone_spec.rb +36 -0
- data/spec/opal/core/module/define_method_spec.rb +29 -0
- data/spec/opal/core/object_id_spec.rb +0 -6
- data/spec/opal/language/predefined_spec.rb +20 -0
- data/spec/opal/language/yield_spec.rb +43 -0
- data/spec/ruby_specs +1 -2
- data/stdlib/bigdecimal.rb +2 -0
- data/stdlib/cgi/util.rb +89 -0
- data/stdlib/cgi.rb +1 -14
- data/stdlib/delegate.rb +3 -4
- data/stdlib/pathname.rb +1 -1
- data/stdlib/promise/v2.rb +22 -7
- data/stdlib/stringio.rb +2 -0
- data/tasks/testing.rake +15 -11
- data/test/opal/promisev2/test_always.rb +14 -0
- data/test/opal/unsupported_and_bugs.rb +0 -8
- metadata +16 -8
data/opal/corelib/constants.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
::RUBY_PLATFORM = 'opal'
|
2
2
|
::RUBY_ENGINE = 'opal'
|
3
3
|
::RUBY_VERSION = '3.2.0'
|
4
|
-
::RUBY_ENGINE_VERSION = '1.8.0
|
5
|
-
::RUBY_RELEASE_DATE = '2023-
|
4
|
+
::RUBY_ENGINE_VERSION = '1.8.0'
|
5
|
+
::RUBY_RELEASE_DATE = '2023-10-26'
|
6
6
|
::RUBY_PATCHLEVEL = 0
|
7
7
|
::RUBY_REVISION = '0'
|
8
8
|
::RUBY_COPYRIGHT = 'opal - Copyright (C) 2011-2023 Adam Beynon and the Opal contributors'
|
data/opal/corelib/error.rb
CHANGED
@@ -316,6 +316,16 @@ class ::KeyError
|
|
316
316
|
end
|
317
317
|
end
|
318
318
|
|
319
|
+
class ::LocalJumpError
|
320
|
+
attr_reader :exit_value, :reason
|
321
|
+
|
322
|
+
def initialize(message, exit_value = nil, reason = :noreason)
|
323
|
+
super message
|
324
|
+
@exit_value = exit_value
|
325
|
+
@reason = reason
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
319
329
|
module ::JS
|
320
330
|
class Error
|
321
331
|
end
|
data/opal/corelib/hash.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# helpers: yield1, hash_clone, hash_delete, hash_each, hash_get, hash_put, deny_frozen_access, freeze
|
1
|
+
# helpers: yield1, hash_clone, hash_delete, hash_each, hash_get, hash_put, deny_frozen_access, freeze, opal32_init, opal32_add
|
2
2
|
# backtick_javascript: true
|
3
3
|
|
4
4
|
require 'corelib/enumerable'
|
@@ -210,7 +210,8 @@ class ::Hash < `Map`
|
|
210
210
|
def clone
|
211
211
|
%x{
|
212
212
|
var hash = self.$class().$new();
|
213
|
-
|
213
|
+
$hash_clone(self, hash);
|
214
|
+
return self["$frozen?"]() ? hash.$freeze() : hash;
|
214
215
|
}
|
215
216
|
end
|
216
217
|
|
@@ -365,6 +366,10 @@ class ::Hash < `Map`
|
|
365
366
|
item.dig(*keys)
|
366
367
|
end
|
367
368
|
|
369
|
+
def dup
|
370
|
+
`$hash_clone(self, self.$class().$new())`
|
371
|
+
end
|
372
|
+
|
368
373
|
def each(&block)
|
369
374
|
return enum_for(:each) { size } unless block
|
370
375
|
|
@@ -481,41 +486,53 @@ class ::Hash < `Map`
|
|
481
486
|
}
|
482
487
|
end
|
483
488
|
|
489
|
+
`var $hash_ids`
|
490
|
+
|
484
491
|
def hash
|
485
492
|
%x{
|
486
|
-
var top = (
|
493
|
+
var top = ($hash_ids === undefined),
|
487
494
|
hash_id = self.$object_id(),
|
488
|
-
result =
|
489
|
-
key, item
|
495
|
+
result = $opal32_init(),
|
496
|
+
key, item, i,
|
497
|
+
size = self.size, ary = new Int32Array(size);
|
490
498
|
|
491
|
-
|
492
|
-
|
493
|
-
Opal.hash_ids = Object.create(null);
|
494
|
-
}
|
499
|
+
result = $opal32_add(result, 0x4);
|
500
|
+
result = $opal32_add(result, size);
|
495
501
|
|
496
|
-
|
497
|
-
|
498
|
-
|
502
|
+
if (top) {
|
503
|
+
$hash_ids = Object.create(null);
|
504
|
+
}
|
505
|
+
else if ($hash_ids[hash_id]) {
|
506
|
+
return $opal32_add(result, 0x01010101);
|
507
|
+
}
|
499
508
|
|
500
|
-
|
501
|
-
|
509
|
+
try {
|
510
|
+
for (key in $hash_ids) {
|
511
|
+
item = $hash_ids[key];
|
502
512
|
if (#{eql?(`item`)}) {
|
503
|
-
return
|
513
|
+
return $opal32_add(result, 0x01010101);
|
504
514
|
}
|
505
515
|
}
|
506
516
|
|
507
|
-
|
517
|
+
$hash_ids[hash_id] = self;
|
518
|
+
i = 0
|
508
519
|
|
509
520
|
$hash_each(self, false, function(key, value) {
|
510
|
-
|
521
|
+
ary[i] = [0x70414952, key, value].$hash();
|
522
|
+
i++;
|
511
523
|
return [false, false];
|
512
524
|
});
|
513
525
|
|
514
|
-
|
526
|
+
ary = ary.sort();
|
527
|
+
|
528
|
+
for (i = 0; i < ary.length; i++) {
|
529
|
+
result = $opal32_add(result, ary[i]);
|
530
|
+
}
|
515
531
|
|
532
|
+
return result;
|
516
533
|
} finally {
|
517
534
|
if (top) {
|
518
|
-
|
535
|
+
$hash_ids = undefined;
|
519
536
|
}
|
520
537
|
}
|
521
538
|
}
|
@@ -920,7 +937,6 @@ class ::Hash < `Map`
|
|
920
937
|
`Array.from(self.values())`
|
921
938
|
end
|
922
939
|
|
923
|
-
alias dup clone
|
924
940
|
alias each_pair each
|
925
941
|
alias eql? ==
|
926
942
|
alias filter select
|
data/opal/corelib/kernel.rb
CHANGED
data/opal/corelib/method.rb
CHANGED
data/opal/corelib/module.rb
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
# backtick_javascript: true
|
3
3
|
|
4
4
|
class ::Module
|
5
|
+
%x{
|
6
|
+
function ensure_symbol_or_string(name) {
|
7
|
+
if (name.$$is_string) {
|
8
|
+
return name;
|
9
|
+
};
|
10
|
+
var converted_name = #{::Opal.try_convert(`name`, ::String, :to_str)};
|
11
|
+
if (converted_name.$$is_string) {
|
12
|
+
return converted_name;
|
13
|
+
} else if (converted_name === nil) {
|
14
|
+
#{::Kernel.raise ::TypeError, "#{`name`} is not a symbol nor a string"}
|
15
|
+
} else {
|
16
|
+
#{::Kernel.raise ::TypeError, "can't convert #{`name`.class} to String (#{`name`.class}#to_str gives #{`converted_name`.class}"}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
5
21
|
def self.allocate
|
6
22
|
%x{
|
7
23
|
var module = Opal.allocate_module(nil, function(){});
|
@@ -388,9 +404,12 @@ class ::Module
|
|
388
404
|
|
389
405
|
if (method === undefined && block === nil)
|
390
406
|
#{::Kernel.raise ::ArgumentError, 'tried to create a Proc object without a block'}
|
407
|
+
|
408
|
+
name = ensure_symbol_or_string(name);
|
391
409
|
}
|
392
410
|
|
393
|
-
|
411
|
+
if `method !== undefined`
|
412
|
+
block = case method
|
394
413
|
when ::Proc
|
395
414
|
method
|
396
415
|
|
@@ -398,15 +417,21 @@ class ::Module
|
|
398
417
|
`#{method.to_proc}.$$unbound`
|
399
418
|
|
400
419
|
when ::UnboundMethod
|
401
|
-
|
402
|
-
bound = method.bind(self)
|
403
|
-
bound.call(*args)
|
404
|
-
}
|
420
|
+
`Opal.wrap_method_body(method.$$method)`
|
405
421
|
|
406
422
|
else
|
407
|
-
::Kernel.raise ::TypeError, "wrong argument type #{
|
423
|
+
::Kernel.raise ::TypeError, "wrong argument type #{method.class} (expected Proc/Method/UnboundMethod)"
|
408
424
|
end
|
409
425
|
|
426
|
+
if `!method.$$is_proc`
|
427
|
+
owner = method.owner
|
428
|
+
if `owner.$$is_class` && !(self <= owner) # rubocop:disable Style/InverseMethods
|
429
|
+
message = `owner.$$is_singleton` ? "can't bind singleton method to a different class" : "bind argument must be a subclass of #{owner}"
|
430
|
+
::Kernel.raise ::TypeError, message
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
410
435
|
%x{
|
411
436
|
if (typeof(Proxy) !== 'undefined') {
|
412
437
|
var meta = Object.create(null)
|
@@ -414,15 +439,17 @@ class ::Module
|
|
414
439
|
block.$$proxy_target = block
|
415
440
|
block = new Proxy(block, {
|
416
441
|
apply: function(target, self, args) {
|
417
|
-
var old_name = target.$$jsid
|
442
|
+
var old_name = target.$$jsid, old_lambda = target.$$is_lambda;
|
418
443
|
target.$$jsid = name;
|
444
|
+
target.$$is_lambda = true;
|
419
445
|
try {
|
420
446
|
return target.apply(self, args);
|
421
447
|
} catch(e) {
|
422
448
|
if (e === target.$$brk || e === target.$$ret) return e.$v;
|
423
449
|
throw e;
|
424
450
|
} finally {
|
425
|
-
target.$$jsid = old_name
|
451
|
+
target.$$jsid = old_name;
|
452
|
+
target.$$is_lambda = old_lambda;
|
426
453
|
}
|
427
454
|
}
|
428
455
|
})
|
@@ -453,10 +480,7 @@ class ::Module
|
|
453
480
|
def remove_method(*names)
|
454
481
|
%x{
|
455
482
|
for (var i = 0; i < names.length; i++) {
|
456
|
-
var name = names[i];
|
457
|
-
if (!(typeof name === "string" || name.$$is_string)) {
|
458
|
-
#{raise ::TypeError, "#{name} is not a symbol nor a string"}
|
459
|
-
}
|
483
|
+
var name = ensure_symbol_or_string(names[i]);
|
460
484
|
$deny_frozen_access(self);
|
461
485
|
|
462
486
|
Opal.rdef(self, "$" + name);
|
@@ -705,13 +729,10 @@ class ::Module
|
|
705
729
|
def undef_method(*names)
|
706
730
|
%x{
|
707
731
|
for (var i = 0; i < names.length; i++) {
|
708
|
-
var name = names[i];
|
709
|
-
if (!(typeof name === "string" || name.$$is_string)) {
|
710
|
-
#{raise ::TypeError, "#{name} is not a symbol nor a string"}
|
711
|
-
}
|
732
|
+
var name = ensure_symbol_or_string(names[i]);
|
712
733
|
$deny_frozen_access(self);
|
713
734
|
|
714
|
-
Opal.udef(self, "$" +
|
735
|
+
Opal.udef(self, "$" + name);
|
715
736
|
}
|
716
737
|
}
|
717
738
|
|
@@ -733,11 +754,50 @@ class ::Module
|
|
733
754
|
}
|
734
755
|
end
|
735
756
|
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
757
|
+
%x{
|
758
|
+
function copyInstanceMethods(from, to) {
|
759
|
+
var i, method_names = Opal.own_instance_methods(from);
|
760
|
+
for (i = 0; i < method_names.length; i++) {
|
761
|
+
var name = method_names[i],
|
762
|
+
jsid = $jsid(name),
|
763
|
+
body = from.$$prototype[jsid],
|
764
|
+
wrapped = Opal.wrap_method_body(body);
|
765
|
+
|
766
|
+
wrapped.$$jsid = name;
|
767
|
+
Opal.defn(to, jsid, wrapped);
|
768
|
+
}
|
769
|
+
}
|
770
|
+
|
771
|
+
function copyIncludedModules(from, to) {
|
772
|
+
var modules = from.$$own_included_modules;
|
773
|
+
for (var i = modules.length - 1; i >= 0; i--) {
|
774
|
+
Opal.append_features(modules[i], to);
|
775
|
+
}
|
776
|
+
}
|
777
|
+
|
778
|
+
function copyPrependedModules(from, to) {
|
779
|
+
var modules = from.$$own_prepended_modules;
|
780
|
+
for (var i = modules.length - 1; i >= 0; i--) {
|
781
|
+
Opal.prepend_features(modules[i], to);
|
782
|
+
}
|
783
|
+
}
|
784
|
+
}
|
785
|
+
|
786
|
+
def initialize_copy(other)
|
787
|
+
%x{
|
788
|
+
copyInstanceMethods(other, self);
|
789
|
+
copyIncludedModules(other, self);
|
790
|
+
copyPrependedModules(other, self);
|
791
|
+
self.$$cloned_from = other.$$cloned_from.concat(other);
|
792
|
+
}
|
793
|
+
copy_class_variables(other)
|
794
|
+
copy_constants(other)
|
795
|
+
end
|
796
|
+
|
797
|
+
def initialize_dup(other)
|
798
|
+
super
|
799
|
+
# Unlike other classes, Module's singleton methods are copied on Object#dup.
|
800
|
+
copy_singleton_methods(other)
|
741
801
|
end
|
742
802
|
|
743
803
|
def copy_class_variables(other)
|
data/opal/corelib/number.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# backtick_javascript: true
|
2
|
+
# use_strict: true
|
2
3
|
|
3
4
|
require 'corelib/numeric'
|
4
5
|
|
@@ -6,6 +7,7 @@ class ::Number < ::Numeric
|
|
6
7
|
::Opal.bridge(`Number`, self)
|
7
8
|
`Opal.prop(self.$$prototype, '$$is_number', true)`
|
8
9
|
`self.$$is_number_class = true`
|
10
|
+
`var number_id_map = new Map()`
|
9
11
|
|
10
12
|
class << self
|
11
13
|
def allocate
|
@@ -36,7 +38,32 @@ class ::Number < ::Numeric
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def __id__
|
39
|
-
|
41
|
+
%x{
|
42
|
+
// Binary-safe integers
|
43
|
+
if (self|0 === self) {
|
44
|
+
return (self * 2) + 1;
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
if (number_id_map.has(self)) {
|
48
|
+
return number_id_map.get(self);
|
49
|
+
}
|
50
|
+
var id = Opal.uid();
|
51
|
+
number_id_map.set(self, id);
|
52
|
+
return id;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def hash
|
58
|
+
%x{
|
59
|
+
// Binary-safe integers
|
60
|
+
if (self|0 === self) {
|
61
|
+
return #{__id__}
|
62
|
+
}
|
63
|
+
else {
|
64
|
+
return self.toString().$hash();
|
65
|
+
}
|
66
|
+
}
|
40
67
|
end
|
41
68
|
|
42
69
|
def +(other)
|
data/opal/corelib/range.rb
CHANGED
@@ -19,7 +19,8 @@ class ::Range
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def ===(value)
|
22
|
-
|
22
|
+
return false if `value.$$is_range`
|
23
|
+
cover? value
|
23
24
|
end
|
24
25
|
|
25
26
|
%x{
|
@@ -44,13 +45,34 @@ class ::Range
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def cover?(value)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
compare = ->(a, b) {
|
49
|
+
a <=> b || 1
|
50
|
+
}
|
51
|
+
|
52
|
+
if `value.$$is_range`
|
53
|
+
val_begin = value.begin
|
54
|
+
val_end = value.end
|
55
|
+
val_excl = value.exclude_end?
|
56
|
+
if (@begin && val_begin.nil?) ||
|
57
|
+
(@end && val_end.nil?) ||
|
58
|
+
(val_begin && val_end && compare.call(val_begin, val_end).then { |c| val_excl ? c >= 0 : c > 0 }) ||
|
59
|
+
(val_begin && !cover?(val_begin))
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
|
63
|
+
cmp = compare.call(@end, val_end)
|
64
|
+
return cmp >= 0 if @excl == val_excl
|
65
|
+
return cmp > 0 if @excl
|
66
|
+
return true if cmp >= 0
|
67
|
+
|
68
|
+
val_max = value.max
|
69
|
+
return !val_max.nil? && compare.call(val_max, @end) <= 0
|
70
|
+
end
|
71
|
+
|
72
|
+
return false if @begin && compare.call(@begin, value) > 0
|
73
|
+
return true if @end.nil?
|
74
|
+
end_cmp = compare.call(value, @end)
|
75
|
+
@excl ? end_cmp < 0 : end_cmp <= 0
|
54
76
|
end
|
55
77
|
|
56
78
|
def each(&block)
|
@@ -113,6 +135,29 @@ class ::Range
|
|
113
135
|
super
|
114
136
|
end
|
115
137
|
|
138
|
+
def include?(val)
|
139
|
+
if `self.begin.$$is_number || self.end.$$is_number` ||
|
140
|
+
@begin.is_a?(::Time) || @end.is_a?(::Time) ||
|
141
|
+
::Integer.try_convert(@begin) || ::Integer.try_convert(@end)
|
142
|
+
return cover?(val)
|
143
|
+
end
|
144
|
+
|
145
|
+
if `self.begin.$$is_string || self.end.$$is_string`
|
146
|
+
if `self.begin.$$is_string && self.end.$$is_string`
|
147
|
+
return @begin.upto(@end, @excl).any? { |s| s == val }
|
148
|
+
elsif @begin.nil?
|
149
|
+
cmp = val <=> @end
|
150
|
+
return !cmp.nil? && (@excl ? cmp < 0 : cmp <= 0)
|
151
|
+
elsif @end.nil?
|
152
|
+
cmp = @begin <=> val
|
153
|
+
return !cmp.nil? && cmp <= 0
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# invoke Enumerable#include?
|
158
|
+
super
|
159
|
+
end
|
160
|
+
|
116
161
|
def last(n = undefined)
|
117
162
|
::Kernel.raise ::RangeError, 'cannot get the maximum of endless range' if @end.nil?
|
118
163
|
return @end if `n == null`
|
@@ -315,10 +360,9 @@ class ::Range
|
|
315
360
|
end
|
316
361
|
|
317
362
|
def hash
|
318
|
-
[@begin, @end, @excl].hash
|
363
|
+
[::Range, @begin, @end, @excl].hash
|
319
364
|
end
|
320
365
|
|
321
366
|
alias == eql?
|
322
|
-
alias
|
323
|
-
alias member? cover?
|
367
|
+
alias member? include?
|
324
368
|
end
|