opal 1.2.0 → 1.3.0.alpha1

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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.await.js +6 -0
  3. data/.eslintrc.js +34 -0
  4. data/.github/workflows/build.yml +8 -0
  5. data/.rubocop.yml +9 -0
  6. data/CHANGELOG.md +4 -0
  7. data/README.md +1 -1
  8. data/Rakefile +1 -0
  9. data/UNRELEASED.md +64 -38
  10. data/docs/async.md +109 -0
  11. data/docs/roda-sprockets.md +0 -2
  12. data/exe/opal +2 -0
  13. data/exe/opal-repl +2 -2
  14. data/lib/opal/builder.rb +5 -1
  15. data/lib/opal/builder_processors.rb +7 -2
  16. data/lib/opal/cache/file_cache.rb +119 -0
  17. data/lib/opal/cache.rb +71 -0
  18. data/lib/opal/cli.rb +35 -1
  19. data/lib/opal/cli_options.rb +21 -0
  20. data/lib/opal/cli_runners/chrome.rb +21 -14
  21. data/lib/opal/cli_runners/chrome_cdp_interface.js +30285 -0
  22. data/lib/opal/cli_runners/{chrome.js → chrome_cdp_interface.rb} +27 -6
  23. data/lib/opal/cli_runners/compiler.rb +2 -1
  24. data/lib/opal/cli_runners/gjs.rb +27 -0
  25. data/lib/opal/cli_runners/mini_racer.rb +36 -0
  26. data/lib/opal/cli_runners/source-map-support-browser.js +276 -91
  27. data/lib/opal/cli_runners/source-map-support-node.js +276 -91
  28. data/lib/opal/cli_runners/source-map-support.js +60 -18
  29. data/lib/opal/cli_runners.rb +2 -0
  30. data/lib/opal/compiler.rb +99 -10
  31. data/lib/opal/fragment.rb +77 -14
  32. data/lib/opal/nodes/args/extract_kwrestarg.rb +6 -4
  33. data/lib/opal/nodes/args/extract_restarg.rb +10 -12
  34. data/lib/opal/nodes/args.rb +28 -0
  35. data/lib/opal/nodes/base.rb +29 -5
  36. data/lib/opal/nodes/call.rb +123 -2
  37. data/lib/opal/nodes/case.rb +7 -1
  38. data/lib/opal/nodes/class.rb +12 -2
  39. data/lib/opal/nodes/def.rb +3 -23
  40. data/lib/opal/nodes/definitions.rb +21 -4
  41. data/lib/opal/nodes/helpers.rb +2 -2
  42. data/lib/opal/nodes/if.rb +39 -9
  43. data/lib/opal/nodes/iter.rb +15 -3
  44. data/lib/opal/nodes/lambda.rb +3 -1
  45. data/lib/opal/nodes/literal.rb +13 -7
  46. data/lib/opal/nodes/logic.rb +2 -2
  47. data/lib/opal/nodes/module.rb +12 -2
  48. data/lib/opal/nodes/rescue.rb +59 -34
  49. data/lib/opal/nodes/scope.rb +88 -6
  50. data/lib/opal/nodes/super.rb +52 -25
  51. data/lib/opal/nodes/top.rb +13 -7
  52. data/lib/opal/nodes/while.rb +7 -1
  53. data/lib/opal/parser/patch.rb +2 -1
  54. data/lib/opal/repl.rb +137 -49
  55. data/lib/opal/rewriters/binary_operator_assignment.rb +10 -10
  56. data/lib/opal/rewriters/block_to_iter.rb +3 -3
  57. data/lib/opal/rewriters/for_rewriter.rb +7 -7
  58. data/lib/opal/rewriters/js_reserved_words.rb +5 -3
  59. data/lib/opal/source_map/file.rb +7 -4
  60. data/lib/opal/source_map/map.rb +17 -3
  61. data/lib/opal/version.rb +1 -1
  62. data/opal/corelib/array.rb +2 -2
  63. data/opal/corelib/binding.rb +46 -0
  64. data/opal/corelib/boolean.rb +54 -4
  65. data/opal/corelib/class.rb +2 -0
  66. data/opal/corelib/constants.rb +2 -2
  67. data/opal/corelib/error.rb +98 -12
  68. data/opal/corelib/io.rb +250 -38
  69. data/opal/corelib/kernel/format.rb +5 -2
  70. data/opal/corelib/kernel.rb +44 -23
  71. data/opal/corelib/main.rb +5 -0
  72. data/opal/corelib/method.rb +1 -0
  73. data/opal/corelib/module.rb +28 -0
  74. data/opal/corelib/number.rb +12 -1
  75. data/opal/corelib/random/seedrandom.js.rb +2 -2
  76. data/opal/corelib/regexp.rb +47 -3
  77. data/opal/corelib/runtime.js +152 -12
  78. data/opal/corelib/string/encoding.rb +17 -17
  79. data/opal/corelib/string.rb +2 -0
  80. data/opal/corelib/struct.rb +10 -3
  81. data/opal/corelib/trace_point.rb +57 -0
  82. data/opal/opal/full.rb +2 -0
  83. data/package.json +3 -2
  84. data/spec/filters/bugs/array.rb +0 -1
  85. data/spec/filters/bugs/basicobject.rb +0 -1
  86. data/spec/filters/bugs/binding.rb +27 -0
  87. data/spec/filters/bugs/enumerator.rb +132 -0
  88. data/spec/filters/bugs/exception.rb +70 -93
  89. data/spec/filters/bugs/float.rb +0 -1
  90. data/spec/filters/bugs/kernel.rb +3 -9
  91. data/spec/filters/bugs/language.rb +15 -58
  92. data/spec/filters/bugs/main.rb +16 -0
  93. data/spec/filters/bugs/matrix.rb +39 -0
  94. data/spec/filters/bugs/method.rb +0 -2
  95. data/spec/filters/bugs/module.rb +36 -79
  96. data/spec/filters/bugs/proc.rb +0 -1
  97. data/spec/filters/bugs/regexp.rb +0 -16
  98. data/spec/filters/bugs/trace_point.rb +12 -0
  99. data/spec/filters/bugs/warnings.rb +0 -4
  100. data/spec/filters/unsupported/freeze.rb +2 -0
  101. data/spec/filters/unsupported/privacy.rb +4 -0
  102. data/spec/lib/compiler_spec.rb +7 -1
  103. data/spec/lib/repl_spec.rb +4 -2
  104. data/spec/lib/source_map/file_spec.rb +1 -1
  105. data/spec/mspec-opal/formatters.rb +18 -4
  106. data/spec/mspec-opal/runner.rb +2 -2
  107. data/spec/opal/core/boolean_spec.rb +44 -0
  108. data/spec/opal/core/hash_spec.rb +8 -0
  109. data/spec/opal/core/number/to_s_spec.rb +11 -0
  110. data/spec/opal/stdlib/json/ext_spec.rb +3 -3
  111. data/spec/opal/stdlib/logger/logger_spec.rb +10 -1
  112. data/spec/ruby_specs +18 -0
  113. data/stdlib/await.rb +83 -0
  114. data/stdlib/base64.rb +4 -4
  115. data/stdlib/bigdecimal/bignumber.js.rb +4 -2
  116. data/stdlib/bigdecimal.rb +1 -0
  117. data/stdlib/gjs/io.rb +33 -0
  118. data/stdlib/gjs/kernel.rb +5 -0
  119. data/stdlib/gjs.rb +2 -0
  120. data/stdlib/js.rb +4 -0
  121. data/stdlib/json.rb +3 -3
  122. data/stdlib/logger.rb +1 -1
  123. data/stdlib/nashorn/file.rb +2 -0
  124. data/stdlib/nodejs/env.rb +7 -0
  125. data/stdlib/nodejs/file.rb +6 -41
  126. data/stdlib/nodejs/io.rb +21 -5
  127. data/stdlib/nodejs/js-yaml-3-6-1.js +2 -2
  128. data/stdlib/opal/miniracer.rb +6 -0
  129. data/stdlib/opal/platform.rb +4 -0
  130. data/stdlib/opal/repl_js.rb +5 -0
  131. data/stdlib/opal/replutils.rb +271 -0
  132. data/stdlib/opal-parser.rb +24 -11
  133. data/stdlib/opal-platform.rb +8 -0
  134. data/stdlib/promise/v2.rb +16 -4
  135. data/stdlib/promise.rb +14 -0
  136. data/stdlib/stringio.rb +13 -110
  137. data/stdlib/thread.rb +29 -0
  138. data/tasks/building.rake +10 -4
  139. data/tasks/linting-parse-eslint-results.js +39 -0
  140. data/tasks/linting.rake +38 -28
  141. data/tasks/performance/asciidoctor_test.rb.erb +6 -0
  142. data/tasks/performance/optimization_status.rb +77 -0
  143. data/tasks/performance.rake +149 -0
  144. data/tasks/testing.rake +9 -1
  145. data/test/nodejs/test_await.rb +169 -0
  146. data/test/opal/promisev2/test_error.rb +9 -3
  147. data/test/opal/unsupported_and_bugs.rb +5 -0
  148. data/vendored-minitest/minitest/benchmark.rb +9 -7
  149. data/vendored-minitest/minitest/test.rb +14 -12
  150. data/vendored-minitest/minitest.rb +19 -16
  151. data/yarn.lock +686 -117
  152. metadata +60 -23
  153. data/.jshintrc +0 -41
  154. data/spec/filters/unsupported/refinements.rb +0 -8
  155. data/vendored-minitest/minitest/hell.rb +0 -11
  156. data/vendored-minitest/minitest/parallel.rb +0 -65
  157. data/vendored-minitest/minitest/pride.rb +0 -4
  158. data/vendored-minitest/minitest/pride_plugin.rb +0 -142
  159. data/vendored-minitest/minitest/unit.rb +0 -45
@@ -727,19 +727,19 @@ var ArraySet = require('./array-set').ArraySet;
727
727
  var base64VLQ = require('./base64-vlq');
728
728
  var quickSort = require('./quick-sort').quickSort;
729
729
 
730
- function SourceMapConsumer(aSourceMap) {
730
+ function SourceMapConsumer(aSourceMap, aSourceMapURL) {
731
731
  var sourceMap = aSourceMap;
732
732
  if (typeof aSourceMap === 'string') {
733
- sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
733
+ sourceMap = util.parseSourceMapInput(aSourceMap);
734
734
  }
735
735
 
736
736
  return sourceMap.sections != null
737
- ? new IndexedSourceMapConsumer(sourceMap)
738
- : new BasicSourceMapConsumer(sourceMap);
737
+ ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
738
+ : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
739
739
  }
740
740
 
741
- SourceMapConsumer.fromSourceMap = function(aSourceMap) {
742
- return BasicSourceMapConsumer.fromSourceMap(aSourceMap);
741
+ SourceMapConsumer.fromSourceMap = function(aSourceMap, aSourceMapURL) {
742
+ return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);
743
743
  }
744
744
 
745
745
  /**
@@ -779,6 +779,8 @@ SourceMapConsumer.prototype._version = 3;
779
779
 
780
780
  SourceMapConsumer.prototype.__generatedMappings = null;
781
781
  Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
782
+ configurable: true,
783
+ enumerable: true,
782
784
  get: function () {
783
785
  if (!this.__generatedMappings) {
784
786
  this._parseMappings(this._mappings, this.sourceRoot);
@@ -790,6 +792,8 @@ Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
790
792
 
791
793
  SourceMapConsumer.prototype.__originalMappings = null;
792
794
  Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
795
+ configurable: true,
796
+ enumerable: true,
793
797
  get: function () {
794
798
  if (!this.__originalMappings) {
795
799
  this._parseMappings(this._mappings, this.sourceRoot);
@@ -857,9 +861,7 @@ SourceMapConsumer.prototype.eachMapping =
857
861
  var sourceRoot = this.sourceRoot;
858
862
  mappings.map(function (mapping) {
859
863
  var source = mapping.source === null ? null : this._sources.at(mapping.source);
860
- if (source != null && sourceRoot != null) {
861
- source = util.join(sourceRoot, source);
862
- }
864
+ source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
863
865
  return {
864
866
  source: source,
865
867
  generatedLine: mapping.generatedLine,
@@ -882,13 +884,16 @@ SourceMapConsumer.prototype.eachMapping =
882
884
  * The only argument is an object with the following properties:
883
885
  *
884
886
  * - source: The filename of the original source.
885
- * - line: The line number in the original source.
887
+ * - line: The line number in the original source. The line number is 1-based.
886
888
  * - column: Optional. the column number in the original source.
889
+ * The column number is 0-based.
887
890
  *
888
891
  * and an array of objects is returned, each with the following properties:
889
892
  *
890
- * - line: The line number in the generated source, or null.
893
+ * - line: The line number in the generated source, or null. The
894
+ * line number is 1-based.
891
895
  * - column: The column number in the generated source, or null.
896
+ * The column number is 0-based.
892
897
  */
893
898
  SourceMapConsumer.prototype.allGeneratedPositionsFor =
894
899
  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -904,13 +909,10 @@ SourceMapConsumer.prototype.allGeneratedPositionsFor =
904
909
  originalColumn: util.getArg(aArgs, 'column', 0)
905
910
  };
906
911
 
907
- if (this.sourceRoot != null) {
908
- needle.source = util.relative(this.sourceRoot, needle.source);
909
- }
910
- if (!this._sources.has(needle.source)) {
912
+ needle.source = this._findSourceIndex(needle.source);
913
+ if (needle.source < 0) {
911
914
  return [];
912
915
  }
913
- needle.source = this._sources.indexOf(needle.source);
914
916
 
915
917
  var mappings = [];
916
918
 
@@ -970,7 +972,7 @@ exports.SourceMapConsumer = SourceMapConsumer;
970
972
  * query for information about the original file positions by giving it a file
971
973
  * position in the generated source.
972
974
  *
973
- * The only parameter is the raw source map (either as a JSON string, or
975
+ * The first parameter is the raw source map (either as a JSON string, or
974
976
  * already parsed to an object). According to the spec, source maps have the
975
977
  * following attributes:
976
978
  *
@@ -993,12 +995,16 @@ exports.SourceMapConsumer = SourceMapConsumer;
993
995
  * mappings: "AA,AB;;ABCDE;"
994
996
  * }
995
997
  *
998
+ * The second parameter, if given, is a string whose value is the URL
999
+ * at which the source map was found. This URL is used to compute the
1000
+ * sources array.
1001
+ *
996
1002
  * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
997
1003
  */
998
- function BasicSourceMapConsumer(aSourceMap) {
1004
+ function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
999
1005
  var sourceMap = aSourceMap;
1000
1006
  if (typeof aSourceMap === 'string') {
1001
- sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
1007
+ sourceMap = util.parseSourceMapInput(aSourceMap);
1002
1008
  }
1003
1009
 
1004
1010
  var version = util.getArg(sourceMap, 'version');
@@ -1017,6 +1023,10 @@ function BasicSourceMapConsumer(aSourceMap) {
1017
1023
  throw new Error('Unsupported version: ' + version);
1018
1024
  }
1019
1025
 
1026
+ if (sourceRoot) {
1027
+ sourceRoot = util.normalize(sourceRoot);
1028
+ }
1029
+
1020
1030
  sources = sources
1021
1031
  .map(String)
1022
1032
  // Some source maps produce relative source paths like "./foo.js" instead of
@@ -1040,24 +1050,57 @@ function BasicSourceMapConsumer(aSourceMap) {
1040
1050
  this._names = ArraySet.fromArray(names.map(String), true);
1041
1051
  this._sources = ArraySet.fromArray(sources, true);
1042
1052
 
1053
+ this._absoluteSources = this._sources.toArray().map(function (s) {
1054
+ return util.computeSourceURL(sourceRoot, s, aSourceMapURL);
1055
+ });
1056
+
1043
1057
  this.sourceRoot = sourceRoot;
1044
1058
  this.sourcesContent = sourcesContent;
1045
1059
  this._mappings = mappings;
1060
+ this._sourceMapURL = aSourceMapURL;
1046
1061
  this.file = file;
1047
1062
  }
1048
1063
 
1049
1064
  BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
1050
1065
  BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
1051
1066
 
1067
+ /**
1068
+ * Utility function to find the index of a source. Returns -1 if not
1069
+ * found.
1070
+ */
1071
+ BasicSourceMapConsumer.prototype._findSourceIndex = function(aSource) {
1072
+ var relativeSource = aSource;
1073
+ if (this.sourceRoot != null) {
1074
+ relativeSource = util.relative(this.sourceRoot, relativeSource);
1075
+ }
1076
+
1077
+ if (this._sources.has(relativeSource)) {
1078
+ return this._sources.indexOf(relativeSource);
1079
+ }
1080
+
1081
+ // Maybe aSource is an absolute URL as returned by |sources|. In
1082
+ // this case we can't simply undo the transform.
1083
+ var i;
1084
+ for (i = 0; i < this._absoluteSources.length; ++i) {
1085
+ if (this._absoluteSources[i] == aSource) {
1086
+ return i;
1087
+ }
1088
+ }
1089
+
1090
+ return -1;
1091
+ };
1092
+
1052
1093
  /**
1053
1094
  * Create a BasicSourceMapConsumer from a SourceMapGenerator.
1054
1095
  *
1055
1096
  * @param SourceMapGenerator aSourceMap
1056
1097
  * The source map that will be consumed.
1098
+ * @param String aSourceMapURL
1099
+ * The URL at which the source map can be found (optional)
1057
1100
  * @returns BasicSourceMapConsumer
1058
1101
  */
1059
1102
  BasicSourceMapConsumer.fromSourceMap =
1060
- function SourceMapConsumer_fromSourceMap(aSourceMap) {
1103
+ function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
1061
1104
  var smc = Object.create(BasicSourceMapConsumer.prototype);
1062
1105
 
1063
1106
  var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -1066,6 +1109,10 @@ BasicSourceMapConsumer.fromSourceMap =
1066
1109
  smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
1067
1110
  smc.sourceRoot);
1068
1111
  smc.file = aSourceMap._file;
1112
+ smc._sourceMapURL = aSourceMapURL;
1113
+ smc._absoluteSources = smc._sources.toArray().map(function (s) {
1114
+ return util.computeSourceURL(smc.sourceRoot, s, aSourceMapURL);
1115
+ });
1069
1116
 
1070
1117
  // Because we are modifying the entries (by converting string sources and
1071
1118
  // names to indices into the sources and names ArraySets), we have to make
@@ -1112,9 +1159,7 @@ BasicSourceMapConsumer.prototype._version = 3;
1112
1159
  */
1113
1160
  Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
1114
1161
  get: function () {
1115
- return this._sources.toArray().map(function (s) {
1116
- return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
1117
- }, this);
1162
+ return this._absoluteSources.slice();
1118
1163
  }
1119
1164
  });
1120
1165
 
@@ -1295,8 +1340,10 @@ BasicSourceMapConsumer.prototype.computeColumnSpans =
1295
1340
  * source's line and column positions provided. The only argument is an object
1296
1341
  * with the following properties:
1297
1342
  *
1298
- * - line: The line number in the generated source.
1299
- * - column: The column number in the generated source.
1343
+ * - line: The line number in the generated source. The line number
1344
+ * is 1-based.
1345
+ * - column: The column number in the generated source. The column
1346
+ * number is 0-based.
1300
1347
  * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
1301
1348
  * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
1302
1349
  * closest element that is smaller than or greater than the one we are
@@ -1306,8 +1353,10 @@ BasicSourceMapConsumer.prototype.computeColumnSpans =
1306
1353
  * and an object is returned with the following properties:
1307
1354
  *
1308
1355
  * - source: The original source file, or null.
1309
- * - line: The line number in the original source, or null.
1310
- * - column: The column number in the original source, or null.
1356
+ * - line: The line number in the original source, or null. The
1357
+ * line number is 1-based.
1358
+ * - column: The column number in the original source, or null. The
1359
+ * column number is 0-based.
1311
1360
  * - name: The original identifier, or null.
1312
1361
  */
1313
1362
  BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -1333,9 +1382,7 @@ BasicSourceMapConsumer.prototype.originalPositionFor =
1333
1382
  var source = util.getArg(mapping, 'source', null);
1334
1383
  if (source !== null) {
1335
1384
  source = this._sources.at(source);
1336
- if (this.sourceRoot != null) {
1337
- source = util.join(this.sourceRoot, source);
1338
- }
1385
+ source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
1339
1386
  }
1340
1387
  var name = util.getArg(mapping, 'name', null);
1341
1388
  if (name !== null) {
@@ -1382,12 +1429,14 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
1382
1429
  return null;
1383
1430
  }
1384
1431
 
1385
- if (this.sourceRoot != null) {
1386
- aSource = util.relative(this.sourceRoot, aSource);
1432
+ var index = this._findSourceIndex(aSource);
1433
+ if (index >= 0) {
1434
+ return this.sourcesContent[index];
1387
1435
  }
1388
1436
 
1389
- if (this._sources.has(aSource)) {
1390
- return this.sourcesContent[this._sources.indexOf(aSource)];
1437
+ var relativeSource = aSource;
1438
+ if (this.sourceRoot != null) {
1439
+ relativeSource = util.relative(this.sourceRoot, relativeSource);
1391
1440
  }
1392
1441
 
1393
1442
  var url;
@@ -1397,15 +1446,15 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
1397
1446
  // many users. We can help them out when they expect file:// URIs to
1398
1447
  // behave like it would if they were running a local HTTP server. See
1399
1448
  // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
1400
- var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
1449
+ var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
1401
1450
  if (url.scheme == "file"
1402
1451
  && this._sources.has(fileUriAbsPath)) {
1403
1452
  return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
1404
1453
  }
1405
1454
 
1406
1455
  if ((!url.path || url.path == "/")
1407
- && this._sources.has("/" + aSource)) {
1408
- return this.sourcesContent[this._sources.indexOf("/" + aSource)];
1456
+ && this._sources.has("/" + relativeSource)) {
1457
+ return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
1409
1458
  }
1410
1459
  }
1411
1460
 
@@ -1417,7 +1466,7 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
1417
1466
  return null;
1418
1467
  }
1419
1468
  else {
1420
- throw new Error('"' + aSource + '" is not in the SourceMap.');
1469
+ throw new Error('"' + relativeSource + '" is not in the SourceMap.');
1421
1470
  }
1422
1471
  };
1423
1472
 
@@ -1427,8 +1476,10 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
1427
1476
  * the following properties:
1428
1477
  *
1429
1478
  * - source: The filename of the original source.
1430
- * - line: The line number in the original source.
1431
- * - column: The column number in the original source.
1479
+ * - line: The line number in the original source. The line number
1480
+ * is 1-based.
1481
+ * - column: The column number in the original source. The column
1482
+ * number is 0-based.
1432
1483
  * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
1433
1484
  * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
1434
1485
  * closest element that is smaller than or greater than the one we are
@@ -1437,23 +1488,22 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
1437
1488
  *
1438
1489
  * and an object is returned with the following properties:
1439
1490
  *
1440
- * - line: The line number in the generated source, or null.
1491
+ * - line: The line number in the generated source, or null. The
1492
+ * line number is 1-based.
1441
1493
  * - column: The column number in the generated source, or null.
1494
+ * The column number is 0-based.
1442
1495
  */
1443
1496
  BasicSourceMapConsumer.prototype.generatedPositionFor =
1444
1497
  function SourceMapConsumer_generatedPositionFor(aArgs) {
1445
1498
  var source = util.getArg(aArgs, 'source');
1446
- if (this.sourceRoot != null) {
1447
- source = util.relative(this.sourceRoot, source);
1448
- }
1449
- if (!this._sources.has(source)) {
1499
+ source = this._findSourceIndex(source);
1500
+ if (source < 0) {
1450
1501
  return {
1451
1502
  line: null,
1452
1503
  column: null,
1453
1504
  lastColumn: null
1454
1505
  };
1455
1506
  }
1456
- source = this._sources.indexOf(source);
1457
1507
 
1458
1508
  var needle = {
1459
1509
  source: source,
@@ -1497,7 +1547,7 @@ exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
1497
1547
  * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
1498
1548
  * input.
1499
1549
  *
1500
- * The only parameter is a raw source map (either as a JSON string, or already
1550
+ * The first parameter is a raw source map (either as a JSON string, or already
1501
1551
  * parsed to an object). According to the spec for indexed source maps, they
1502
1552
  * have the following attributes:
1503
1553
  *
@@ -1534,12 +1584,16 @@ exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
1534
1584
  * }],
1535
1585
  * }
1536
1586
  *
1587
+ * The second parameter, if given, is a string whose value is the URL
1588
+ * at which the source map was found. This URL is used to compute the
1589
+ * sources array.
1590
+ *
1537
1591
  * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
1538
1592
  */
1539
- function IndexedSourceMapConsumer(aSourceMap) {
1593
+ function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
1540
1594
  var sourceMap = aSourceMap;
1541
1595
  if (typeof aSourceMap === 'string') {
1542
- sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
1596
+ sourceMap = util.parseSourceMapInput(aSourceMap);
1543
1597
  }
1544
1598
 
1545
1599
  var version = util.getArg(sourceMap, 'version');
@@ -1579,7 +1633,7 @@ function IndexedSourceMapConsumer(aSourceMap) {
1579
1633
  generatedLine: offsetLine + 1,
1580
1634
  generatedColumn: offsetColumn + 1
1581
1635
  },
1582
- consumer: new SourceMapConsumer(util.getArg(s, 'map'))
1636
+ consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
1583
1637
  }
1584
1638
  });
1585
1639
  }
@@ -1612,14 +1666,18 @@ Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
1612
1666
  * source's line and column positions provided. The only argument is an object
1613
1667
  * with the following properties:
1614
1668
  *
1615
- * - line: The line number in the generated source.
1616
- * - column: The column number in the generated source.
1669
+ * - line: The line number in the generated source. The line number
1670
+ * is 1-based.
1671
+ * - column: The column number in the generated source. The column
1672
+ * number is 0-based.
1617
1673
  *
1618
1674
  * and an object is returned with the following properties:
1619
1675
  *
1620
1676
  * - source: The original source file, or null.
1621
- * - line: The line number in the original source, or null.
1622
- * - column: The column number in the original source, or null.
1677
+ * - line: The line number in the original source, or null. The
1678
+ * line number is 1-based.
1679
+ * - column: The column number in the original source, or null. The
1680
+ * column number is 0-based.
1623
1681
  * - name: The original identifier, or null.
1624
1682
  */
1625
1683
  IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -1703,13 +1761,17 @@ IndexedSourceMapConsumer.prototype.sourceContentFor =
1703
1761
  * the following properties:
1704
1762
  *
1705
1763
  * - source: The filename of the original source.
1706
- * - line: The line number in the original source.
1707
- * - column: The column number in the original source.
1764
+ * - line: The line number in the original source. The line number
1765
+ * is 1-based.
1766
+ * - column: The column number in the original source. The column
1767
+ * number is 0-based.
1708
1768
  *
1709
1769
  * and an object is returned with the following properties:
1710
1770
  *
1711
- * - line: The line number in the generated source, or null.
1771
+ * - line: The line number in the generated source, or null. The
1772
+ * line number is 1-based.
1712
1773
  * - column: The column number in the generated source, or null.
1774
+ * The column number is 0-based.
1713
1775
  */
1714
1776
  IndexedSourceMapConsumer.prototype.generatedPositionFor =
1715
1777
  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -1718,7 +1780,7 @@ IndexedSourceMapConsumer.prototype.generatedPositionFor =
1718
1780
 
1719
1781
  // Only consider this section if the requested source is in the list of
1720
1782
  // sources of the consumer.
1721
- if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) {
1783
+ if (section.consumer._findSourceIndex(util.getArg(aArgs, 'source')) === -1) {
1722
1784
  continue;
1723
1785
  }
1724
1786
  var generatedPosition = section.consumer.generatedPositionFor(aArgs);
@@ -1757,15 +1819,16 @@ IndexedSourceMapConsumer.prototype._parseMappings =
1757
1819
  var mapping = sectionMappings[j];
1758
1820
 
1759
1821
  var source = section.consumer._sources.at(mapping.source);
1760
- if (section.consumer.sourceRoot !== null) {
1761
- source = util.join(section.consumer.sourceRoot, source);
1762
- }
1822
+ source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
1763
1823
  this._sources.add(source);
1764
1824
  source = this._sources.indexOf(source);
1765
1825
 
1766
- var name = section.consumer._names.at(mapping.name);
1767
- this._names.add(name);
1768
- name = this._names.indexOf(name);
1826
+ var name = null;
1827
+ if (mapping.name) {
1828
+ name = section.consumer._names.at(mapping.name);
1829
+ this._names.add(name);
1830
+ name = this._names.indexOf(name);
1831
+ }
1769
1832
 
1770
1833
  // The mappings coming from the consumer for the section have
1771
1834
  // generated positions relative to the start of the section, so we
@@ -1872,6 +1935,15 @@ SourceMapGenerator.fromSourceMap =
1872
1935
  generator.addMapping(newMapping);
1873
1936
  });
1874
1937
  aSourceMapConsumer.sources.forEach(function (sourceFile) {
1938
+ var sourceRelative = sourceFile;
1939
+ if (sourceRoot !== null) {
1940
+ sourceRelative = util.relative(sourceRoot, sourceFile);
1941
+ }
1942
+
1943
+ if (!generator._sources.has(sourceRelative)) {
1944
+ generator._sources.add(sourceRelative);
1945
+ }
1946
+
1875
1947
  var content = aSourceMapConsumer.sourceContentFor(sourceFile);
1876
1948
  if (content != null) {
1877
1949
  generator.setSourceContent(sourceFile, content);
@@ -2315,7 +2387,7 @@ SourceNode.fromStringWithSourceMap =
2315
2387
  // There is no new line in between.
2316
2388
  // Associate the code between "lastGeneratedColumn" and
2317
2389
  // "mapping.generatedColumn" with "lastMapping"
2318
- var nextLine = remainingLines[remainingLinesIndex];
2390
+ var nextLine = remainingLines[remainingLinesIndex] || '';
2319
2391
  var code = nextLine.substr(0, mapping.generatedColumn -
2320
2392
  lastGeneratedColumn);
2321
2393
  remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
@@ -2335,7 +2407,7 @@ SourceNode.fromStringWithSourceMap =
2335
2407
  lastGeneratedLine++;
2336
2408
  }
2337
2409
  if (lastGeneratedColumn < mapping.generatedColumn) {
2338
- var nextLine = remainingLines[remainingLinesIndex];
2410
+ var nextLine = remainingLines[remainingLinesIndex] || '';
2339
2411
  node.add(nextLine.substr(0, mapping.generatedColumn));
2340
2412
  remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
2341
2413
  lastGeneratedColumn = mapping.generatedColumn;
@@ -2659,7 +2731,7 @@ function getArg(aArgs, aName, aDefaultValue) {
2659
2731
  }
2660
2732
  exports.getArg = getArg;
2661
2733
 
2662
- var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
2734
+ var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
2663
2735
  var dataUrlRegexp = /^data:.+\,.+$/;
2664
2736
 
2665
2737
  function urlParse(aUrl) {
@@ -2815,7 +2887,7 @@ function join(aRoot, aPath) {
2815
2887
  exports.join = join;
2816
2888
 
2817
2889
  exports.isAbsolute = function (aPath) {
2818
- return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
2890
+ return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
2819
2891
  };
2820
2892
 
2821
2893
  /**
@@ -2935,7 +3007,7 @@ function isProtoString(s) {
2935
3007
  * stubbed out mapping.
2936
3008
  */
2937
3009
  function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
2938
- var cmp = mappingA.source - mappingB.source;
3010
+ var cmp = strcmp(mappingA.source, mappingB.source);
2939
3011
  if (cmp !== 0) {
2940
3012
  return cmp;
2941
3013
  }
@@ -2960,7 +3032,7 @@ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
2960
3032
  return cmp;
2961
3033
  }
2962
3034
 
2963
- return mappingA.name - mappingB.name;
3035
+ return strcmp(mappingA.name, mappingB.name);
2964
3036
  }
2965
3037
  exports.compareByOriginalPositions = compareByOriginalPositions;
2966
3038
 
@@ -2984,7 +3056,7 @@ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGene
2984
3056
  return cmp;
2985
3057
  }
2986
3058
 
2987
- cmp = mappingA.source - mappingB.source;
3059
+ cmp = strcmp(mappingA.source, mappingB.source);
2988
3060
  if (cmp !== 0) {
2989
3061
  return cmp;
2990
3062
  }
@@ -2999,7 +3071,7 @@ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGene
2999
3071
  return cmp;
3000
3072
  }
3001
3073
 
3002
- return mappingA.name - mappingB.name;
3074
+ return strcmp(mappingA.name, mappingB.name);
3003
3075
  }
3004
3076
  exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
3005
3077
 
@@ -3008,6 +3080,14 @@ function strcmp(aStr1, aStr2) {
3008
3080
  return 0;
3009
3081
  }
3010
3082
 
3083
+ if (aStr1 === null) {
3084
+ return 1; // aStr2 !== null
3085
+ }
3086
+
3087
+ if (aStr2 === null) {
3088
+ return -1; // aStr1 !== null
3089
+ }
3090
+
3011
3091
  if (aStr1 > aStr2) {
3012
3092
  return 1;
3013
3093
  }
@@ -3049,6 +3129,69 @@ function compareByGeneratedPositionsInflated(mappingA, mappingB) {
3049
3129
  }
3050
3130
  exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
3051
3131
 
3132
+ /**
3133
+ * Strip any JSON XSSI avoidance prefix from the string (as documented
3134
+ * in the source maps specification), and then parse the string as
3135
+ * JSON.
3136
+ */
3137
+ function parseSourceMapInput(str) {
3138
+ return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
3139
+ }
3140
+ exports.parseSourceMapInput = parseSourceMapInput;
3141
+
3142
+ /**
3143
+ * Compute the URL of a source given the the source root, the source's
3144
+ * URL, and the source map's URL.
3145
+ */
3146
+ function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
3147
+ sourceURL = sourceURL || '';
3148
+
3149
+ if (sourceRoot) {
3150
+ // This follows what Chrome does.
3151
+ if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
3152
+ sourceRoot += '/';
3153
+ }
3154
+ // The spec says:
3155
+ // Line 4: An optional source root, useful for relocating source
3156
+ // files on a server or removing repeated values in the
3157
+ // “sources” entry. This value is prepended to the individual
3158
+ // entries in the “source” field.
3159
+ sourceURL = sourceRoot + sourceURL;
3160
+ }
3161
+
3162
+ // Historically, SourceMapConsumer did not take the sourceMapURL as
3163
+ // a parameter. This mode is still somewhat supported, which is why
3164
+ // this code block is conditional. However, it's preferable to pass
3165
+ // the source map URL to SourceMapConsumer, so that this function
3166
+ // can implement the source URL resolution algorithm as outlined in
3167
+ // the spec. This block is basically the equivalent of:
3168
+ // new URL(sourceURL, sourceMapURL).toString()
3169
+ // ... except it avoids using URL, which wasn't available in the
3170
+ // older releases of node still supported by this library.
3171
+ //
3172
+ // The spec says:
3173
+ // If the sources are not absolute URLs after prepending of the
3174
+ // “sourceRoot”, the sources are resolved relative to the
3175
+ // SourceMap (like resolving script src in a html document).
3176
+ if (sourceMapURL) {
3177
+ var parsed = urlParse(sourceMapURL);
3178
+ if (!parsed) {
3179
+ throw new Error("sourceMapURL could not be parsed");
3180
+ }
3181
+ if (parsed.path) {
3182
+ // Strip the last path component, but keep the "/".
3183
+ var index = parsed.path.lastIndexOf('/');
3184
+ if (index >= 0) {
3185
+ parsed.path = parsed.path.substring(0, index + 1);
3186
+ }
3187
+ }
3188
+ sourceURL = join(urlGenerate(parsed), sourceURL);
3189
+ }
3190
+
3191
+ return normalize(sourceURL);
3192
+ }
3193
+ exports.computeSourceURL = computeSourceURL;
3194
+
3052
3195
  },{}],12:[function(require,module,exports){
3053
3196
  /*
3054
3197
  * Copyright 2009-2011 Mozilla Foundation and contributors
@@ -3065,7 +3208,7 @@ exports.SourceNode = require('./lib/source-node').SourceNode;
3065
3208
  // to do so, `run bin/build-browser-source-map-support`
3066
3209
 
3067
3210
  // The following is taken and adapted from the work of Evan Wallace
3068
- // https://github.com/evanw/node-source-map-support v0.5.12
3211
+ // https://github.com/evanw/node-source-map-support v0.5.19
3069
3212
 
3070
3213
  // The MIT License (MIT)
3071
3214
  //
@@ -3105,6 +3248,16 @@ try {
3105
3248
 
3106
3249
  var bufferFrom = require('buffer-from');
3107
3250
 
3251
+ /**
3252
+ * Requires a module which is protected against bundler minification.
3253
+ *
3254
+ * @param {NodeModule} mod
3255
+ * @param {string} request
3256
+ */
3257
+ function dynamicRequire(mod, request) {
3258
+ return mod.require(request);
3259
+ }
3260
+
3108
3261
  // Only install once if called multiple times
3109
3262
  var errorFormatterInstalled = false;
3110
3263
  var uncaughtShimInstalled = false;
@@ -3231,7 +3384,7 @@ function retrieveSourceMapURL(source) {
3231
3384
 
3232
3385
  // Get the URL of the source map
3233
3386
  fileData = retrieveFile(source);
3234
- var re = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/)[ \t]*$)/mg;
3387
+ var re = /(?:\/\/[@#][\s]*sourceMappingURL=([^\s'"]+)[\s]*$)|(?:\/\*[@#][\s]*sourceMappingURL=([^\s*'"]+)[\s]*(?:\*\/)[\s]*$)/mg;
3235
3388
  // Keep executing the search to find the *last* sourceMappingURL to avoid
3236
3389
  // picking up sourceMappingURLs from comments, strings, etc.
3237
3390
  var lastMatch, match;
@@ -3442,8 +3595,13 @@ function cloneCallSite(frame) {
3442
3595
  return object;
3443
3596
  }
3444
3597
 
3445
- function wrapCallSite(frame) {
3598
+ function wrapCallSite(frame, state) {
3599
+ // provides interface backward compatibility
3600
+ if (state === undefined) {
3601
+ state = { nextPosition: null, curPosition: null }
3602
+ }
3446
3603
  if(frame.isNative()) {
3604
+ state.curPosition = null;
3447
3605
  return frame;
3448
3606
  }
3449
3607
 
@@ -3457,7 +3615,11 @@ function wrapCallSite(frame) {
3457
3615
 
3458
3616
  // Fix position in Node where some (internal) code is prepended.
3459
3617
  // See https://github.com/evanw/node-source-map-support/issues/36
3460
- var headerLength = 62;
3618
+ // Header removed in node at ^10.16 || >=11.11.0
3619
+ // v11 is not an LTS candidate, we can just test the one version with it.
3620
+ // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11
3621
+ var noHeader = /^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;
3622
+ var headerLength = noHeader.test(process.version) ? 0 : 62;
3461
3623
  if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) {
3462
3624
  column -= headerLength;
3463
3625
  }
@@ -3467,9 +3629,15 @@ function wrapCallSite(frame) {
3467
3629
  line: line,
3468
3630
  column: column
3469
3631
  });
3632
+ state.curPosition = position;
3470
3633
  frame = cloneCallSite(frame);
3471
3634
  var originalFunctionName = frame.getFunctionName;
3472
- frame.getFunctionName = function() { return position.name || originalFunctionName(); };
3635
+ frame.getFunctionName = function() {
3636
+ if (state.nextPosition == null) {
3637
+ return originalFunctionName();
3638
+ }
3639
+ return state.nextPosition.name || originalFunctionName();
3640
+ };
3473
3641
  frame.getFileName = function() { return position.source; };
3474
3642
  frame.getLineNumber = function() { return position.line; };
3475
3643
  frame.getColumnNumber = function() { return position.column + 1; };
@@ -3502,9 +3670,14 @@ function prepareStackTrace(error, stack) {
3502
3670
  var message = error.message || '';
3503
3671
  var errorString = name + ": " + message;
3504
3672
 
3505
- return errorString + stack.map(function(frame) {
3506
- return '\n from ' + wrapCallSite(frame);
3507
- }).join('');
3673
+ var state = { nextPosition: null, curPosition: null };
3674
+ var processedStack = [];
3675
+ for (var i = stack.length - 1; i >= 0; i--) {
3676
+ processedStack.push('\n from ' + wrapCallSite(stack[i], state));
3677
+ state.nextPosition = state.curPosition;
3678
+ }
3679
+ state.curPosition = state.nextPosition = null;
3680
+ return errorString + processedStack.reverse().join('');
3508
3681
  }
3509
3682
 
3510
3683
  // Generate position and snippet of original source with pointer
@@ -3547,12 +3720,17 @@ function printErrorAndExit (error) {
3547
3720
  process.stderr._handle.setBlocking(true);
3548
3721
  }
3549
3722
 
3550
- if (source) {
3551
- console.error();
3552
- console.error(source);
3723
+ if (typeof error.$full_message === 'function') {
3724
+ console.error(error.$full_message().$chomp());
3553
3725
  }
3726
+ else {
3727
+ if (source) {
3728
+ console.error();
3729
+ console.error(source);
3730
+ }
3554
3731
 
3555
- console.error(error.stack);
3732
+ console.error(error.stack);
3733
+ }
3556
3734
  process.exit(1);
3557
3735
  }
3558
3736
 
@@ -3613,12 +3791,8 @@ exports.install = function(options) {
3613
3791
 
3614
3792
  // Support runtime transpilers that include inline source maps
3615
3793
  if (options.hookRequire && !isInBrowser()) {
3616
- var Module;
3617
- try {
3618
- Module = require('module');
3619
- } catch (err) {
3620
- // NOP: Loading in catch block to convert webpack error to warning.
3621
- }
3794
+ // Use dynamicRequire to avoid including in browser bundles
3795
+ var Module = dynamicRequire(module, 'module');
3622
3796
  var $compile = Module.prototype._compile;
3623
3797
 
3624
3798
  if (!$compile.__sourceMapSupport) {
@@ -3648,6 +3822,17 @@ exports.install = function(options) {
3648
3822
  var installHandler = 'handleUncaughtExceptions' in options ?
3649
3823
  options.handleUncaughtExceptions : true;
3650
3824
 
3825
+ // Do not override 'uncaughtException' with our own handler in Node.js
3826
+ // Worker threads. Workers pass the error to the main thread as an event,
3827
+ // rather than printing something to stderr and exiting.
3828
+ try {
3829
+ // We need to use `dynamicRequire` because `require` on it's own will be optimized by WebPack/Browserify.
3830
+ var worker_threads = dynamicRequire(module, 'worker_threads');
3831
+ if (worker_threads.isMainThread === false) {
3832
+ installHandler = false;
3833
+ }
3834
+ } catch(e) {}
3835
+
3651
3836
  // Provide the option to not install the uncaught exception handler. This is
3652
3837
  // to support other uncaught exception handlers (in test frameworks, for
3653
3838
  // example). If this handler is not installed and there are no other uncaught
@@ -3702,5 +3887,5 @@ exports.install();
3702
3887
  // 1: from /Users/elia/Code/opal/lib/opal/cli_runners/nodejs.rb:14:in `call'
3703
3888
  // /Users/elia/Code/opal/lib/opal/cli_runners/system_runner.rb:43:in `call': unhandled exception
3704
3889
 
3705
- },{"buffer-from":1,"fs":undefined,"module":undefined,"path":undefined,"source-map":12}]},{},[])("/lib/opal/cli_runners/source-map-support.js")
3890
+ },{"buffer-from":1,"fs":undefined,"path":undefined,"source-map":12}]},{},[])("/lib/opal/cli_runners/source-map-support.js")
3706
3891
  });