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
@@ -3,7 +3,7 @@
3
3
  // to do so, `run bin/build-browser-source-map-support`
4
4
 
5
5
  // The following is taken and adapted from the work of Evan Wallace
6
- // https://github.com/evanw/node-source-map-support v0.5.12
6
+ // https://github.com/evanw/node-source-map-support v0.5.19
7
7
 
8
8
  // The MIT License (MIT)
9
9
  //
@@ -43,6 +43,16 @@ try {
43
43
 
44
44
  var bufferFrom = require('buffer-from');
45
45
 
46
+ /**
47
+ * Requires a module which is protected against bundler minification.
48
+ *
49
+ * @param {NodeModule} mod
50
+ * @param {string} request
51
+ */
52
+ function dynamicRequire(mod, request) {
53
+ return mod.require(request);
54
+ }
55
+
46
56
  // Only install once if called multiple times
47
57
  var errorFormatterInstalled = false;
48
58
  var uncaughtShimInstalled = false;
@@ -169,7 +179,7 @@ function retrieveSourceMapURL(source) {
169
179
 
170
180
  // Get the URL of the source map
171
181
  fileData = retrieveFile(source);
172
- var re = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/)[ \t]*$)/mg;
182
+ var re = /(?:\/\/[@#][\s]*sourceMappingURL=([^\s'"]+)[\s]*$)|(?:\/\*[@#][\s]*sourceMappingURL=([^\s*'"]+)[\s]*(?:\*\/)[\s]*$)/mg;
173
183
  // Keep executing the search to find the *last* sourceMappingURL to avoid
174
184
  // picking up sourceMappingURLs from comments, strings, etc.
175
185
  var lastMatch, match;
@@ -380,8 +390,13 @@ function cloneCallSite(frame) {
380
390
  return object;
381
391
  }
382
392
 
383
- function wrapCallSite(frame) {
393
+ function wrapCallSite(frame, state) {
394
+ // provides interface backward compatibility
395
+ if (state === undefined) {
396
+ state = { nextPosition: null, curPosition: null }
397
+ }
384
398
  if(frame.isNative()) {
399
+ state.curPosition = null;
385
400
  return frame;
386
401
  }
387
402
 
@@ -395,7 +410,11 @@ function wrapCallSite(frame) {
395
410
 
396
411
  // Fix position in Node where some (internal) code is prepended.
397
412
  // See https://github.com/evanw/node-source-map-support/issues/36
398
- var headerLength = 62;
413
+ // Header removed in node at ^10.16 || >=11.11.0
414
+ // v11 is not an LTS candidate, we can just test the one version with it.
415
+ // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11
416
+ 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)/;
417
+ var headerLength = noHeader.test(process.version) ? 0 : 62;
399
418
  if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) {
400
419
  column -= headerLength;
401
420
  }
@@ -405,9 +424,15 @@ function wrapCallSite(frame) {
405
424
  line: line,
406
425
  column: column
407
426
  });
427
+ state.curPosition = position;
408
428
  frame = cloneCallSite(frame);
409
429
  var originalFunctionName = frame.getFunctionName;
410
- frame.getFunctionName = function() { return position.name || originalFunctionName(); };
430
+ frame.getFunctionName = function() {
431
+ if (state.nextPosition == null) {
432
+ return originalFunctionName();
433
+ }
434
+ return state.nextPosition.name || originalFunctionName();
435
+ };
411
436
  frame.getFileName = function() { return position.source; };
412
437
  frame.getLineNumber = function() { return position.line; };
413
438
  frame.getColumnNumber = function() { return position.column + 1; };
@@ -440,9 +465,14 @@ function prepareStackTrace(error, stack) {
440
465
  var message = error.message || '';
441
466
  var errorString = name + ": " + message;
442
467
 
443
- return errorString + stack.map(function(frame) {
444
- return '\n from ' + wrapCallSite(frame);
445
- }).join('');
468
+ var state = { nextPosition: null, curPosition: null };
469
+ var processedStack = [];
470
+ for (var i = stack.length - 1; i >= 0; i--) {
471
+ processedStack.push('\n from ' + wrapCallSite(stack[i], state));
472
+ state.nextPosition = state.curPosition;
473
+ }
474
+ state.curPosition = state.nextPosition = null;
475
+ return errorString + processedStack.reverse().join('');
446
476
  }
447
477
 
448
478
  // Generate position and snippet of original source with pointer
@@ -485,12 +515,17 @@ function printErrorAndExit (error) {
485
515
  process.stderr._handle.setBlocking(true);
486
516
  }
487
517
 
488
- if (source) {
489
- console.error();
490
- console.error(source);
518
+ if (typeof error.$full_message === 'function') {
519
+ console.error(error.$full_message().$chomp());
491
520
  }
521
+ else {
522
+ if (source) {
523
+ console.error();
524
+ console.error(source);
525
+ }
492
526
 
493
- console.error(error.stack);
527
+ console.error(error.stack);
528
+ }
494
529
  process.exit(1);
495
530
  }
496
531
 
@@ -551,12 +586,8 @@ exports.install = function(options) {
551
586
 
552
587
  // Support runtime transpilers that include inline source maps
553
588
  if (options.hookRequire && !isInBrowser()) {
554
- var Module;
555
- try {
556
- Module = require('module');
557
- } catch (err) {
558
- // NOP: Loading in catch block to convert webpack error to warning.
559
- }
589
+ // Use dynamicRequire to avoid including in browser bundles
590
+ var Module = dynamicRequire(module, 'module');
560
591
  var $compile = Module.prototype._compile;
561
592
 
562
593
  if (!$compile.__sourceMapSupport) {
@@ -586,6 +617,17 @@ exports.install = function(options) {
586
617
  var installHandler = 'handleUncaughtExceptions' in options ?
587
618
  options.handleUncaughtExceptions : true;
588
619
 
620
+ // Do not override 'uncaughtException' with our own handler in Node.js
621
+ // Worker threads. Workers pass the error to the main thread as an event,
622
+ // rather than printing something to stderr and exiting.
623
+ try {
624
+ // We need to use `dynamicRequire` because `require` on it's own will be optimized by WebPack/Browserify.
625
+ var worker_threads = dynamicRequire(module, 'worker_threads');
626
+ if (worker_threads.isMainThread === false) {
627
+ installHandler = false;
628
+ }
629
+ } catch(e) {}
630
+
589
631
  // Provide the option to not install the uncaught exception handler. This is
590
632
  // to support other uncaught exception handlers (in test frameworks, for
591
633
  // example). If this handler is not installed and there are no other uncaught
@@ -65,6 +65,8 @@ module Opal
65
65
  register_runner :compiler, :Compiler, 'opal/cli_runners/compiler'
66
66
  register_runner :nashorn, :Nashorn, 'opal/cli_runners/nashorn'
67
67
  register_runner :nodejs, :Nodejs, 'opal/cli_runners/nodejs'
68
+ register_runner :gjs, :Gjs, 'opal/cli_runners/gjs'
69
+ register_runner :miniracer, :MiniRacer, 'opal/cli_runners/mini_racer'
68
70
  register_runner :server, :Server, 'opal/cli_runners/server'
69
71
 
70
72
  alias_runner :osascript, :applescript
data/lib/opal/compiler.rb CHANGED
@@ -137,6 +137,11 @@ module Opal
137
137
  # Prepare the code for future requires
138
138
  compiler_option :requirable, default: false, as: :requirable?
139
139
 
140
+ # @!method esm?
141
+ #
142
+ # Wrap compiler result as self contained ES6 module
143
+ compiler_option :esm, default: false, as: :esm?
144
+
140
145
  # @!method inline_operators?
141
146
  #
142
147
  # are operators compiled inline
@@ -159,6 +164,38 @@ module Opal
159
164
  # Adds comments for every method definition
160
165
  compiler_option :parse_comments, default: false, as: :parse_comments?
161
166
 
167
+ compiler_option :scope_variables, default: []
168
+
169
+ # @!method async_await
170
+ #
171
+ # Enable async/await support and optionally enable auto-await.
172
+ #
173
+ # Use either true, false, an Array of Symbols, a String containing names
174
+ # to auto-await separated by a comma or a Regexp.
175
+ #
176
+ # Auto-await awaits provided methods by default as if .__await__ was added to
177
+ # them automatically.
178
+ #
179
+ # By default, the support is disabled (set to false).
180
+ #
181
+ # If the config value is not set to false, any calls to #__await__ will be
182
+ # translated to ES8 await keyword which makes the scope return a Promise
183
+ # and a containing scope will be async (instead of a value, it will return
184
+ # a Promise).
185
+ #
186
+ # If the config value is an array, or a String separated by a comma,
187
+ # auto-await is also enabled.
188
+ #
189
+ # A member of this collection can contain a wildcard character * in which
190
+ # case all methods containing a given substring will be awaited.
191
+ #
192
+ # It can be used as a magic comment, examples:
193
+ # ```
194
+ # # await: true
195
+ # # await: *await*
196
+ # # await: *await*, sleep, gets
197
+ compiler_option :await, default: false, as: :async_await, magic_comment: true
198
+
162
199
  # @return [String] The compiled ruby code
163
200
  attr_reader :result
164
201
 
@@ -211,7 +248,16 @@ module Opal
211
248
 
212
249
  sexp, comments, tokens = re_raise_with_location { @parser.tokenize(@buffer) }
213
250
 
214
- @sexp = s(:top, sexp || s(:nil))
251
+ kind = case
252
+ when requirable?
253
+ :require
254
+ when eval?
255
+ :eval
256
+ else
257
+ :main
258
+ end
259
+
260
+ @sexp = s(:top, sexp || s(:nil)).tap { |i| i.meta[:kind] = kind }
215
261
  @comments = ::Parser::Source::Comment.associate_locations(sexp, comments)
216
262
  @magic_comments = MagicComments.parse(sexp, comments)
217
263
  @eof_content = EofContent.new(tokens, @source).eof
@@ -223,7 +269,8 @@ module Opal
223
269
  # @param source_file [String] optional source_file to reference ruby source
224
270
  # @return [Opal::SourceMap]
225
271
  def source_map
226
- ::Opal::SourceMap::File.new(@fragments, file, @source)
272
+ # We only use @source_map if compiler is cached.
273
+ @source_map || ::Opal::SourceMap::File.new(@fragments, file, @source, @result)
227
274
  end
228
275
 
229
276
  # Any helpers required by this file. Used by {Opal::Nodes::Top} to reference
@@ -247,6 +294,32 @@ module Opal
247
294
  @method_calls ||= Set.new
248
295
  end
249
296
 
297
+ alias async_await_before_typecasting async_await
298
+ def async_await
299
+ if defined? @async_await
300
+ @async_await
301
+ else
302
+ original = async_await_before_typecasting
303
+ @async_await = case original
304
+ when String
305
+ async_await_set_to_regexp(original.split(',').map { |h| h.strip.to_sym })
306
+ when Array, Set
307
+ async_await_set_to_regexp(original.to_a.map(&:to_sym))
308
+ when Regexp, true, false
309
+ original
310
+ else
311
+ raise 'A value of await compiler option can be either ' \
312
+ 'a Set, an Array, a String or a Boolean.'
313
+ end
314
+ end
315
+ end
316
+
317
+ def async_await_set_to_regexp(set)
318
+ set = set.map { |name| Regexp.escape(name.to_s).gsub('\*', '.*?') }
319
+ set = set.join('|')
320
+ /^(#{set})$/
321
+ end
322
+
250
323
  # This is called when a parsing/processing error occurs. This
251
324
  # method simply appends the filename and curent line number onto
252
325
  # the message and raises it.
@@ -421,8 +494,8 @@ module Opal
421
494
  case sexp.type
422
495
  when :undef
423
496
  # undef :method_name always returns nil
424
- returns s(:begin, sexp, s(:nil))
425
- when :break, :next, :redo
497
+ returns sexp.updated(:begin, [sexp, s(:nil)])
498
+ when :break, :next, :redo, :retry
426
499
  sexp
427
500
  when :yield
428
501
  sexp.updated(:returnable_yield, nil)
@@ -453,7 +526,7 @@ module Opal
453
526
  when :ensure
454
527
  rescue_sexp, ensure_body = *sexp
455
528
  sexp = sexp.updated(nil, [returns(rescue_sexp), ensure_body])
456
- s(:js_return, sexp)
529
+ sexp.updated(:js_return, [sexp])
457
530
  when :begin, :kwbegin
458
531
  # Wrapping last expression with s(:js_return, ...)
459
532
  *rest, last = *sexp
@@ -474,11 +547,14 @@ module Opal
474
547
  ]
475
548
  )
476
549
  else
477
- s(:js_return, sexp).updated(
478
- nil,
479
- nil,
480
- location: sexp.loc,
481
- )
550
+ if sexp.type == :send && sexp.children[1] == :debugger
551
+ # debugger is a statement, so it doesn't return a value
552
+ # and returning it is invalid. Therefore we update it
553
+ # to do `debugger; return nil`.
554
+ sexp.updated(:begin, [sexp, s(:js_return, s(:nil))])
555
+ else
556
+ sexp.updated(:js_return, [sexp])
557
+ end
482
558
  end
483
559
  end
484
560
 
@@ -492,5 +568,18 @@ module Opal
492
568
  fragment('false', scope, sexp)
493
569
  end
494
570
  end
571
+
572
+ # Marshalling for cache shortpath
573
+ def marshal_dump
574
+ [@options, @option_values, @source_map ||= source_map.cache,
575
+ @magic_comments, @result,
576
+ @required_trees, @requires]
577
+ end
578
+
579
+ def marshal_load(src)
580
+ @options, @option_values, @source_map,
581
+ @magic_comments, @result,
582
+ @required_trees, @requires = src
583
+ end
495
584
  end
496
585
  end
data/lib/opal/fragment.rb CHANGED
@@ -28,41 +28,104 @@ module Opal
28
28
  "f(#{@code.inspect})"
29
29
  end
30
30
 
31
- def source_map_name
32
- case @sexp.type
33
- when :top, :begin, :newline, :js_return
34
- nil
31
+ def source_map_name_for(sexp)
32
+ case sexp.type
33
+ when :top
34
+ case sexp.meta[:kind]
35
+ when :require
36
+ '<top (required)>'
37
+ when :eval
38
+ '(eval)'
39
+ when :main
40
+ '<main>'
41
+ end
42
+ when :begin, :newline, :js_return
43
+ source_map_name_for(@scope.sexp) if @scope
44
+ when :iter
45
+ scope = @scope
46
+ iters = 1
47
+ while scope
48
+ if scope.class == Nodes::IterNode
49
+ iters += 1
50
+ scope = scope.parent
51
+ else
52
+ break
53
+ end
54
+ end
55
+ level = " (#{iters} levels)" if iters > 1
56
+ "block#{level} in #{source_map_name_for(scope.sexp)}"
35
57
  when :self
36
58
  'self'
37
59
  when :module
38
- 'module'
60
+ const, = *sexp
61
+ "<module:#{source_map_name_for(const)}>"
39
62
  when :class
40
- 'class'
63
+ const, = *sexp
64
+ "<class:#{source_map_name_for(const)}>"
65
+ when :const
66
+ scope, name = *sexp
67
+ if !scope || scope.type == :cbase
68
+ name.to_s
69
+ else
70
+ "#{source_map_name_for(scope)}::#{name}"
71
+ end
41
72
  when :int
42
- @sexp.children.first
73
+ sexp.children.first
43
74
  when :def
44
- @sexp.children.first
75
+ sexp.children.first
45
76
  when :defs
46
- @sexp.children[1]
77
+ sexp.children[1]
47
78
  when :send
48
- @sexp.children[1]
49
- when :lvar, :lvasgn, :lvdeclare, :ivar, :ivasgn, :gvar, :cvar, :cvasgn, :gvars, :gvasgn
50
- @sexp.children.first
79
+ sexp.children[1]
80
+ when :lvar, :lvasgn, :lvdeclare, :ivar, :ivasgn, :gvar, :cvar, :cvasgn, :gvars, :gvasgn, :arg
81
+ sexp.children.first
82
+ when :str, :xstr # Inside xstr - JS calls
83
+ source_map_name_for(@scope.sexp)
51
84
  else
52
85
  # nil
53
86
  end
54
87
  end
55
88
 
89
+ def source_map_name
90
+ return nil unless @sexp
91
+
92
+ source_map_name_for(@sexp)
93
+ end
94
+
95
+ def location
96
+ case
97
+ when !@sexp
98
+ nil
99
+ when @sexp.type == :send
100
+ loc = @sexp.loc
101
+ if loc.respond_to? :dot # a>.b || a>+b / >a / a>[b]
102
+ loc.dot || loc.selector
103
+ elsif loc.respond_to? :operator # a >|= b
104
+ loc.operator
105
+ else
106
+ @sexp
107
+ end
108
+ when @sexp.type == :iter
109
+ @sexp.loc.begin # [1,2].each >{ }
110
+ else
111
+ @sexp
112
+ end
113
+ end
114
+
56
115
  # Original line this fragment was created from
57
116
  # @return [Integer, nil]
58
117
  def line
59
- @sexp.line if @sexp
118
+ location&.line
60
119
  end
61
120
 
62
121
  # Original column this fragment was created from
63
122
  # @return [Integer, nil]
64
123
  def column
65
- @sexp.column if @sexp
124
+ location&.column
125
+ end
126
+
127
+ def skip_source_map?
128
+ @sexp == false
66
129
  end
67
130
  end
68
131
  end
@@ -16,10 +16,12 @@ module Opal
16
16
  children :name
17
17
 
18
18
  def compile
19
- if name
20
- add_temp name
21
- line "#{name} = Opal.kwrestargs($kwargs, #{used_kwargs});"
22
- end
19
+ # def m(**)
20
+ # arguments are assigned to `$kw_rest_arg` for super call
21
+ name = self.name || '$kw_rest_arg'
22
+
23
+ add_temp name
24
+ line "#{name} = Opal.kwrestargs($kwargs, #{used_kwargs});"
23
25
  end
24
26
 
25
27
  def used_kwargs
@@ -19,19 +19,17 @@ module Opal
19
19
  children :name, :args_to_keep
20
20
 
21
21
  def compile
22
- if name
23
- add_temp name
22
+ # def m(*)
23
+ # arguments are assigned to `$rest_arg` for super call
24
+ name = self.name || '$rest_arg'
24
25
 
25
- if args_to_keep == 0
26
- # no post-args, we are free to grab everything
27
- line "#{name} = $post_args;"
28
- else
29
- line "#{name} = $post_args.splice(0, $post_args.length - #{args_to_keep});"
30
- end
31
- elsif args_to_keep != 0
32
- # def m(*, a)
33
- # We still have to "cut" our splat
34
- line "$post_args.splice(0, $post_args.length - #{args_to_keep});"
26
+ add_temp name
27
+
28
+ if args_to_keep == 0
29
+ # no post-args, we are free to grab everything
30
+ line "#{name} = $post_args;"
31
+ else
32
+ line "#{name} = $post_args.splice(0, $post_args.length - #{args_to_keep});"
35
33
  end
36
34
  end
37
35
  end
@@ -25,12 +25,40 @@ module Opal
25
25
  class ArgsNode < Base
26
26
  handle :args
27
27
 
28
+ # ruby allows for args with the same name, if the arg starts with a '_', like:
29
+ # def funny_method_name(_, _)
30
+ # puts _
31
+ # end
32
+ # but javascript in strict mode does not allow for args with the same name
33
+ # ruby assigns the value of the first arg given
34
+ # funny_method_name(1, 2) => 1
35
+ # 1. check for args starting with '_' and check if they appear multiple times
36
+ # 2. leave the first appearance as it is and rename the other ones
37
+ # compiler result:
38
+ # function $$funny_method_name(_, __$2)
39
+
28
40
  def compile
41
+ same_arg_counter = {}
29
42
  children.each_with_index do |arg, idx|
43
+ if multiple_underscore?(arg)
44
+ same_arg_counter[arg] ||= 0
45
+ same_arg_counter[arg] += 1
46
+ if same_arg_counter[arg] > 1
47
+ arg = s(arg.type, :"#{arg.children[0]}_$#{same_arg_counter[arg]}")
48
+ end
49
+ end
50
+
30
51
  push ', ' if idx != 0
31
52
  push process(arg)
32
53
  end
33
54
  end
55
+
56
+ def multiple_underscore?(arg)
57
+ arg.type == :arg &&
58
+ arg.children.count == 1 &&
59
+ arg.children.first.to_s.start_with?('_') &&
60
+ children.count(arg) > 1
61
+ end
34
62
  end
35
63
  end
36
64
  end
@@ -29,7 +29,7 @@ module Opal
29
29
  false
30
30
  end
31
31
 
32
- attr_reader :compiler, :type
32
+ attr_reader :compiler, :type, :sexp
33
33
 
34
34
  def initialize(sexp, level, compiler)
35
35
  @sexp = sexp
@@ -73,8 +73,8 @@ module Opal
73
73
  push post
74
74
  end
75
75
 
76
- def fragment(str)
77
- Opal::Fragment.new str, scope, @sexp
76
+ def fragment(str, loc: true)
77
+ Opal::Fragment.new str, scope, loc && @sexp
78
78
  end
79
79
 
80
80
  def error(msg)
@@ -85,8 +85,12 @@ module Opal
85
85
  @compiler.scope
86
86
  end
87
87
 
88
- def s(*args)
89
- @compiler.s(*args)
88
+ def top_scope
89
+ scope.top_scope
90
+ end
91
+
92
+ def s(type, *children)
93
+ ::Opal::AST::Node.new(type, children, location: @sexp.loc)
90
94
  end
91
95
 
92
96
  def expr?
@@ -165,6 +169,18 @@ module Opal
165
169
  scope.in_ensure?
166
170
  end
167
171
 
172
+ def in_resbody(&block)
173
+ scope.in_resbody(&block)
174
+ end
175
+
176
+ def in_resbody?
177
+ scope.in_resbody?
178
+ end
179
+
180
+ def in_rescue(node, &block)
181
+ scope.in_rescue(node, &block)
182
+ end
183
+
168
184
  def class_variable_owner_nesting_level
169
185
  cvar_scope = scope
170
186
  nesting_level = 0
@@ -192,6 +208,14 @@ module Opal
192
208
  def comments
193
209
  compiler.comments[@sexp.loc]
194
210
  end
211
+
212
+ def source_location
213
+ file = @sexp.loc.expression.source_buffer.name
214
+ file = "<internal:#{file}>" if file.start_with?("corelib/")
215
+ file = "<js:#{file}>" if file.end_with?(".js")
216
+ line = @sexp.loc.line
217
+ "['#{file}', #{line}]"
218
+ end
195
219
  end
196
220
  end
197
221
  end