opal 1.2.0 → 1.3.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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