nadesiko3 3.7.21 → 3.7.23

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 (212) hide show
  1. package/README.md +20 -1
  2. package/batch/command.txt +370 -334
  3. package/batch/generate_command_list.mjs +102 -0
  4. package/batch/jsplugin2text.nako3 +1 -1
  5. package/batch/pickup_command.nako3 +12 -0
  6. package/batch/search_command.mjs +34 -0
  7. package/batch/search_command.nako3 +430 -0
  8. package/core/command/snako.mts +5 -1
  9. package/core/deno/snako.ts +5 -1
  10. package/core/package-lock.json +382 -21
  11. package/core/package.json +7 -4
  12. package/core/sample/algorithms/README.md +43 -0
  13. package/core/sample/algorithms/graph/dijkstra.nako3 +47 -0
  14. package/core/sample/algorithms/graph/kruskal.nako3 +69 -0
  15. package/core/sample/algorithms/math/euclidean_gcd.nako3 +15 -0
  16. package/core/sample/algorithms/math/sieve_of_eratosthenes.nako3 +26 -0
  17. package/core/sample/algorithms/search/binary_search.nako3 +17 -0
  18. package/core/sample/algorithms/search/breadth_first_search.nako3 +33 -0
  19. package/core/sample/algorithms/search/depth_first_search.nako3 +25 -0
  20. package/core/sample/algorithms/sort/heap_sort.nako3 +37 -0
  21. package/core/sample/algorithms/sort/insertion_sort.nako3 +18 -0
  22. package/core/sample/algorithms/sort/merge_sort.nako3 +40 -0
  23. package/core/sample/algorithms/sort/quick_sort.nako3 +36 -0
  24. package/core/sample/algorithms/string/knuth_morris_pratt.nako3 +50 -0
  25. package/core/sample/algorithms/string/run_length_encoding.nako3 +25 -0
  26. package/core/src/nako3.mts +178 -614
  27. package/core/src/nako_basic_plugins.mts +39 -0
  28. package/core/src/nako_core_version.mts +2 -2
  29. package/core/src/nako_csv.mts +0 -1
  30. package/core/src/nako_event.mts +49 -0
  31. package/core/src/nako_gen.mts +320 -209
  32. package/core/src/nako_global.mts +18 -0
  33. package/core/src/nako_indent_inline.mts +2 -2
  34. package/core/src/nako_lex_rules.mts +1 -1
  35. package/core/src/nako_parser3.mts +111 -166
  36. package/core/src/nako_parser_async.mts +165 -0
  37. package/core/src/nako_parser_base.mts +4 -55
  38. package/core/src/nako_parser_message.mts +105 -0
  39. package/core/src/nako_parser_operator.mts +93 -0
  40. package/core/src/nako_plugin_manager.mts +260 -0
  41. package/core/src/nako_require.mts +292 -0
  42. package/core/src/nako_runner.mts +208 -0
  43. package/core/src/nako_tokenizer.mts +221 -0
  44. package/core/src/plugin_csv.mts +1 -1
  45. package/core/src/plugin_system.mts +34 -3259
  46. package/core/src/plugin_system_array.mts +699 -0
  47. package/core/src/plugin_system_datetime.mts +368 -0
  48. package/core/src/plugin_system_debug.mts +403 -0
  49. package/core/src/plugin_system_dict.mts +85 -0
  50. package/core/src/plugin_system_json.mts +73 -0
  51. package/core/src/plugin_system_math.mts +383 -0
  52. package/core/src/plugin_system_regexp.mts +120 -0
  53. package/core/src/plugin_system_stdio.mts +86 -0
  54. package/core/src/plugin_system_string.mts +666 -0
  55. package/core/src/plugin_system_timer.mts +152 -0
  56. package/core/src/plugin_system_types.mts +151 -0
  57. package/core/src/plugin_system_url.mts +193 -0
  58. package/core/src/plugin_toml.mts +3 -3
  59. package/core/test/algorithm_samples_test.mjs +58 -0
  60. package/core/test/algorithms/graph/dijkstra_test.nako3 +21 -0
  61. package/core/test/algorithms/graph/kruskal_test.nako3 +21 -0
  62. package/core/test/algorithms/helper.mjs +13 -0
  63. package/core/test/algorithms/math/euclidean_gcd_test.nako3 +13 -0
  64. package/core/test/algorithms/math/sieve_of_eratosthenes_test.nako3 +10 -0
  65. package/core/test/algorithms/search/binary_search_test.nako3 +15 -0
  66. package/core/test/algorithms/search/breadth_first_search_test.nako3 +13 -0
  67. package/core/test/algorithms/search/depth_first_search_test.nako3 +13 -0
  68. package/core/test/algorithms/sort/heap_sort_test.nako3 +13 -0
  69. package/core/test/algorithms/sort/insertion_sort_test.nako3 +13 -0
  70. package/core/test/algorithms/sort/merge_sort_test.nako3 +18 -0
  71. package/core/test/algorithms/sort/quick_sort_test.nako3 +34 -0
  72. package/core/test/algorithms/string/knuth_morris_pratt_test.nako3 +16 -0
  73. package/core/test/algorithms/string/run_length_encoding_test.nako3 +13 -0
  74. package/core/test/array_test.mjs +3 -0
  75. package/core/test/fixtures/README.md +39 -0
  76. package/core/test/fixtures/make_parser_ast_golden.mjs +31 -0
  77. package/core/test/fixtures/parser_ast_golden.json +8027 -0
  78. package/core/test/fixtures/parser_corpus.mjs +120 -0
  79. package/core/test/func_call.mjs +18 -0
  80. package/core/test/func_test.mjs +28 -0
  81. package/core/test/indent_test.mjs +6 -0
  82. package/core/test/nako_basic_plugins_test.mjs +44 -0
  83. package/core/test/nako_event_test.mjs +127 -0
  84. package/core/test/nako_gen_perf_test.mjs +178 -0
  85. package/core/test/nako_parser_async_test.mjs +385 -0
  86. package/core/test/nako_parser_test.mjs +160 -0
  87. package/core/test/nako_plugin_manager_test.mjs +185 -0
  88. package/core/test/nako_require_test.mjs +153 -0
  89. package/core/test/nako_runner_test.mjs +174 -0
  90. package/core/test/nako_tokenizer_test.mjs +115 -0
  91. package/core/test/plugin_system_debug_test.mjs +138 -0
  92. package/core/test/plugin_system_split_test.mjs +179 -0
  93. package/core/test/plugin_system_test.mjs +6 -0
  94. package/core/tsconfig.json +0 -1
  95. package/demo/browsers.html +1 -1
  96. package/doc/SETUP.md +1 -1
  97. package/doc/ai-code-generation.md +136 -0
  98. package/doc/browsers.md +1 -1
  99. package/doc/command_list.json +16842 -0
  100. package/doc/search_command.md +222 -0
  101. package/doc/syntax-nako3.md +344 -0
  102. package/package.json +29 -26
  103. package/release/_hash.txt +40 -40
  104. package/release/_script-tags.txt +16 -16
  105. package/release/command.json +1 -1
  106. package/release/command.json.js +1 -1
  107. package/release/command_cnako3.json +1 -1
  108. package/release/command_list.json +1 -1
  109. package/release/edit_main.js +6 -6
  110. package/release/edit_main.js.map +3 -3
  111. package/release/editor.js +6 -6
  112. package/release/plugin_caniuse.js +1 -1
  113. package/release/plugin_caniuse.js.map +2 -2
  114. package/release/plugin_keigo.js.map +3 -3
  115. package/release/plugin_markup.js +46 -46
  116. package/release/plugin_markup.js.map +3 -3
  117. package/release/plugin_weykturtle3d.js +1 -1
  118. package/release/plugin_weykturtle3d.js.map +3 -3
  119. package/release/version.js +2 -2
  120. package/release/version_main.js +2 -2
  121. package/release/version_main.js.map +1 -1
  122. package/release/wnako3.js +219 -230
  123. package/release/wnako3.js.map +4 -4
  124. package/release/wnako3webworker.js +193 -204
  125. package/release/wnako3webworker.js.map +4 -4
  126. package/src/browsers.mjs +1 -1
  127. package/src/browsers.txt +0 -1
  128. package/src/cnako3mod.mjs +7 -2
  129. package/src/cnako3mod.mts +7 -2
  130. package/src/nako_version.mjs +2 -2
  131. package/src/nako_version.mts +2 -2
  132. package/src/plugin_browser_ajax.mjs +4 -4
  133. package/src/plugin_browser_ajax.mts +4 -4
  134. package/src/plugin_browser_audio.mjs +10 -10
  135. package/src/plugin_browser_audio.mts +10 -10
  136. package/src/plugin_browser_camera.mjs +4 -4
  137. package/src/plugin_browser_camera.mts +4 -4
  138. package/src/plugin_browser_canvas.mjs +2 -2
  139. package/src/plugin_browser_canvas.mts +2 -2
  140. package/src/plugin_browser_crypto.mjs +3 -3
  141. package/src/plugin_browser_crypto.mts +3 -3
  142. package/src/plugin_browser_dom_event.mjs +1 -1
  143. package/src/plugin_browser_dom_event.mts +1 -1
  144. package/src/plugin_browser_geolocation.mjs +1 -1
  145. package/src/plugin_browser_geolocation.mts +1 -1
  146. package/src/plugin_browser_hotkey.mjs +1 -1
  147. package/src/plugin_browser_hotkey.mts +1 -1
  148. package/src/plugin_browser_html.mjs +1 -1
  149. package/src/plugin_browser_html.mts +1 -1
  150. package/src/plugin_browser_location.mjs +1 -1
  151. package/src/plugin_browser_location.mts +1 -1
  152. package/src/plugin_browser_speech.mjs +1 -1
  153. package/src/plugin_browser_speech.mts +1 -1
  154. package/src/plugin_browser_storage.mjs +2 -2
  155. package/src/plugin_browser_storage.mts +2 -2
  156. package/src/plugin_httpserver.mjs +3 -3
  157. package/src/plugin_httpserver.mts +3 -3
  158. package/src/plugin_keigo.mjs +2 -2
  159. package/src/plugin_keigo.mts +2 -2
  160. package/src/plugin_node.mjs +48 -48
  161. package/src/plugin_node.mts +53 -53
  162. package/src/plugin_weykturtle3d.mjs +56 -56
  163. package/src/plugin_weykturtle3d.mts +56 -56
  164. package/src/wnako3.mjs +1 -1
  165. package/src/wnako3.mts +1 -1
  166. package/src/wnako3_editor.mjs +5 -5
  167. package/src/wnako3_editor.mts +5 -5
  168. package/src/wnako3mod.mjs +2 -2
  169. package/src/wnako3mod.mts +2 -2
  170. package/test/nako3edit/index_nako3_test.mjs +282 -0
  171. package/test/nako3server/index_nako3_test.mjs +239 -0
  172. package/test/node/ai_code_generation_guide_test.mjs +29 -0
  173. package/test/node/node_version_support_test.mjs +45 -0
  174. package/test/node/search_command_test.mjs +174 -0
  175. package/tools/nako3edit/AGENTS.md +23 -0
  176. package/tools/nako3edit/index.nako3 +404 -0
  177. package/tools/nako3server/AGENTS.md +23 -0
  178. package/tools/nako3server/index.nako3 +156 -23
  179. package/core/src/nako3.mjs +0 -1021
  180. package/core/src/nako_ast.mjs +0 -4
  181. package/core/src/nako_colors.mjs +0 -77
  182. package/core/src/nako_core_version.mjs +0 -8
  183. package/core/src/nako_csv.mjs +0 -193
  184. package/core/src/nako_errors.mjs +0 -166
  185. package/core/src/nako_from_dncl.mjs +0 -285
  186. package/core/src/nako_from_dncl2.mjs +0 -347
  187. package/core/src/nako_gen.mjs +0 -2500
  188. package/core/src/nako_global.mjs +0 -138
  189. package/core/src/nako_indent.mjs +0 -442
  190. package/core/src/nako_indent_chars.mjs +0 -29
  191. package/core/src/nako_indent_inline.mjs +0 -361
  192. package/core/src/nako_josi_list.mjs +0 -47
  193. package/core/src/nako_lex_rules.mjs +0 -319
  194. package/core/src/nako_lexer.mjs +0 -794
  195. package/core/src/nako_logger.mjs +0 -221
  196. package/core/src/nako_parser3.mjs +0 -3250
  197. package/core/src/nako_parser_base.mjs +0 -403
  198. package/core/src/nako_parser_const.mjs +0 -37
  199. package/core/src/nako_prepare.mjs +0 -329
  200. package/core/src/nako_reserved_words.mjs +0 -42
  201. package/core/src/nako_source_mapping.mjs +0 -207
  202. package/core/src/nako_test.mjs +0 -37
  203. package/core/src/nako_token.mjs +0 -1
  204. package/core/src/nako_tools.mjs +0 -53
  205. package/core/src/nako_types.mjs +0 -14
  206. package/core/src/plugin_api.mjs +0 -4
  207. package/core/src/plugin_csv.mjs +0 -97
  208. package/core/src/plugin_math.mjs +0 -352
  209. package/core/src/plugin_promise.mjs +0 -102
  210. package/core/src/plugin_system.mjs +0 -3810
  211. package/core/src/plugin_test.mjs +0 -52
  212. package/core/src/plugin_toml.mjs +0 -39
@@ -37,6 +37,37 @@ interface FunctionContext {
37
37
  varsIndex: number;
38
38
  usesClosure: boolean;
39
39
  }
40
+
41
+ /** 生成したコードの各行を n段だけインデントする (空行は捨てる) */
42
+ function indentLines (text: string, n: number): string {
43
+ let result = ''
44
+ for (const line of text.split('\n')) {
45
+ if (line !== '') {
46
+ result += ' '.repeat(n) + line + '\n'
47
+ }
48
+ }
49
+ return result
50
+ }
51
+
52
+ /**
53
+ * パフォーマンスモニタの計測結果を `__self.__performance_monitor[key]` に記録するコードを生成する。
54
+ * ユーザ関数・システム関数本体・システム関数の3箇所で共通して使う。(#2333)
55
+ * @param timeVar 経過時間(マイクロ秒)が入っているJS変数名
56
+ */
57
+ function genPerfMonitorUpdate (timeVar: string): string {
58
+ const rec = `{ called:1, totel_usec: ${timeVar}, min_usec: ${timeVar}, max_usec: ${timeVar}, type: type }`
59
+ return 'if (!__self.__performance_monitor) {\n' +
60
+ '__self.__performance_monitor={};\n' +
61
+ `__self.__performance_monitor[key] = ${rec};\n` +
62
+ '} else if (!__self.__performance_monitor[key]) {\n' +
63
+ `__self.__performance_monitor[key] = ${rec};\n` +
64
+ '} else {\n' +
65
+ '__self.__performance_monitor[key].called++;\n' +
66
+ `__self.__performance_monitor[key].totel_usec+=${timeVar};\n` +
67
+ `if(__self.__performance_monitor[key].min_usec>${timeVar}){__self.__performance_monitor[key].min_usec=${timeVar};}\n` +
68
+ `if(__self.__performance_monitor[key].max_usec<${timeVar}){__self.__performance_monitor[key].max_usec=${timeVar};}\n` +
69
+ '}'
70
+ }
40
71
  interface FindVarResult {
41
72
  i: number;
42
73
  name: string;
@@ -44,6 +75,15 @@ interface FindVarResult {
44
75
  js: string,
45
76
  js_set: string
46
77
  }
78
+ /** 関数呼び出しコードを組み立てるための部品 (convCallFunc) */
79
+ interface CallCodeParts {
80
+ funcDef: string; // 'function' または 'async function'
81
+ funcCall: string; // 関数を呼び出す式
82
+ funcBegin: string; // 呼び出しの直前に実行するコード
83
+ funcEnd: string; // 呼び出しの後に必ず実行するコード
84
+ isAsync: boolean; // 非同期関数の呼び出しか
85
+ sysPerfKey: string | null; // システム関数の計測キー。計測しないときはnull
86
+ }
47
87
  /** コード生成オプション */
48
88
  export class NakoGenOptions {
49
89
  isTest: boolean
@@ -340,6 +380,23 @@ export class NakoGen {
340
380
  code += 'const __v0 = __self.__v0 = __self.__varslist[0];\n'
341
381
  code += 'const __v1 = __self.__v1 = __self.__varslist[1];\n'
342
382
  code += 'const __vars = __self.__vars = __self.__varslist[2];\n'
383
+ code += 'const __nako_scope_parents = new WeakMap();\n' +
384
+ 'const __nako_active_scopes = new WeakSet();\n' +
385
+ 'const __nako_scope_enter = () => {\n' +
386
+ ' const local = new Map();\n' +
387
+ ' __nako_scope_parents.set(local, __self.__vars);\n' +
388
+ ' __nako_active_scopes.add(local);\n' +
389
+ ' __self.__vars = local;\n' +
390
+ ' return local;\n' +
391
+ '};\n' +
392
+ 'const __nako_scope_leave = (local) => {\n' +
393
+ ' __nako_active_scopes.delete(local);\n' +
394
+ ' let parent = __nako_scope_parents.get(local) || __vars;\n' +
395
+ ' while (parent !== __vars && !__nako_active_scopes.has(parent)) {\n' +
396
+ ' parent = __nako_scope_parents.get(parent) || __vars;\n' +
397
+ ' }\n' +
398
+ ' __self.__vars = parent;\n' +
399
+ '};\n'
343
400
  code += 'const __nako_make_closure = (local, parent) => ({\n' +
344
401
  ' has: (key) => local.has(key) || (parent !== null && parent.has(key)),\n' +
345
402
  ' get: (key) => local.has(key) ? local.get(key) : (parent !== null ? parent.get(key) : undefined),\n' +
@@ -872,31 +929,9 @@ export class NakoGen {
872
929
  let performanceMonitorInjectAtStart = ''
873
930
  let performanceMonitorInjectAtEnd = ''
874
931
  if (this.performanceMonitor.userFunction !== 0) {
875
- let key = name
876
- if (!key) {
877
- if (typeof this.performanceMonitor.mumeiId === 'undefined') {
878
- this.performanceMonitor.mumeiId = 0
879
- }
880
- this.performanceMonitor.mumeiId++
881
- key = `anous_${this.performanceMonitor.mumeiId}`
882
- }
883
- performanceMonitorInjectAtStart = 'const performanceMonitorEnd = (function (key, type) {\n' +
884
- 'const uf_start = performance.now() * 1000;\n' +
885
- 'return function () {\n' +
886
- 'const el_time = performance.now() * 1000 - uf_start;\n' +
887
- 'if (!__self.__performance_monitor) {\n' +
888
- '__self.__performance_monitor={};\n' +
889
- '__self.__performance_monitor[key] = { called:1, totel_usec: el_time, min_usec: el_time, max_usec: el_time, type: type };\n' +
890
- '} else if (!__self.__performance_monitor[key]) {\n' +
891
- '__self.__performance_monitor[key] = { called:1, totel_usec: el_time, min_usec: el_time, max_usec: el_time, type: type };\n' +
892
- '} else {\n' +
893
- '__self.__performance_monitor[key].called++;\n' +
894
- '__self.__performance_monitor[key].totel_usec+=el_time;\n' +
895
- 'if(__self.__performance_monitor[key].min_usec>el_time){__self.__performance_monitor[key].min_usec=el_time;}\n' +
896
- 'if(__self.__performance_monitor[key].max_usec<el_time){__self.__performance_monitor[key].max_usec=el_time;}\n' +
897
- `}};})('${key}', 'user');` +
898
- 'try {\n'
899
- performanceMonitorInjectAtEnd = '} finally { performanceMonitorEnd(); }\n'
932
+ const inject = this.genPerfMonitorInject(this.getPerfMonitorKey(name))
933
+ performanceMonitorInjectAtStart = inject[0]
934
+ performanceMonitorInjectAtEnd = inject[1]
900
935
  }
901
936
  let variableDeclarations = ''
902
937
  const indent = ' '
@@ -923,16 +958,16 @@ export class NakoGen {
923
958
  }
924
959
 
925
960
  // ローカル変数を生成 (再帰関数呼び出しで引数の値が壊れる問題があるので修正 #1663 / タイミングによって壊れるので修理 #1758)
926
- // 暫定変数__localVarsに現在のローカル変数の値をPUSHし、変数を抜ける時にPOPする)
961
+ // 呼び出しごとのローカル変数を登録し、関数を抜ける時に有効な呼び出し元へ戻す。
962
+ // 非同期関数が開始順と異なる順番で終了しても、終了済みのスコープは復元しない。
927
963
  // 関数として宣言しているが、JS関数となでしこ関数では変数管理の方法が異なるため、完全なローカル変数としては使えない
928
964
  // 必ず、pushStack/popStack する必要がある
929
965
  pushStack += '\n// PUSH STACK\n'
930
- pushStack += 'const __localvars = __self.__vars;\n'
931
- pushStack += '__self.__vars = new Map();\n'
966
+ pushStack += 'const __localvars = __nako_scope_enter();\n'
932
967
  pushStack += 'try {\n'
933
968
  popStack += '} finally {\n'
934
969
  popStack += indent + '// POP STACK\n'
935
- popStack += indent + 'self.__vars = __localvars;\n'
970
+ popStack += indent + '__nako_scope_leave(__localvars);\n'
936
971
  popStack += '}\n'
937
972
 
938
973
  // 宣言済みの名前を保存
@@ -1019,7 +1054,9 @@ export class NakoGen {
1019
1054
  const tof = (this.usedAsyncFn) ? topOfFunctionAsync : topOfFunction
1020
1055
  // 関数コード全体を構築
1021
1056
  const lineInfo = ' ' + this.convLineno(node, true, 1) + '\n'
1022
- code = tof + performanceMonitorInjectAtStart + pushStack + variableDeclarations + lineInfo + code + popStack
1057
+ // パフォーマンスモニタのinjectは、PUSH STACKのtry/finallyの内側に入れる。
1058
+ // 外側に置くと __localvars の宣言をまたいでしまい ReferenceError になる (#2333)
1059
+ code = tof + pushStack + performanceMonitorInjectAtStart + variableDeclarations + lineInfo + code + popStack
1023
1060
  code += endOfFunction
1024
1061
  if (funcContext.isAnonymous && funcContext.usesClosure) {
1025
1062
  const parentClosure = `(typeof __nako_closure === 'undefined' ? null : __nako_closure)`
@@ -1412,8 +1449,8 @@ export class NakoGen {
1412
1449
  * @param {boolean} isExpression
1413
1450
  */
1414
1451
  convPerformanceMonitor(node: AstBlocks, isExpression: boolean): string {
1415
- const prev = { ...this.performanceMonitor }
1416
1452
  if (!node.options) { return '' }
1453
+ const prev = { ...this.performanceMonitor }
1417
1454
  if (node.options['ユーザ関数']) {
1418
1455
  this.performanceMonitor.userFunction++
1419
1456
  }
@@ -1430,6 +1467,63 @@ export class NakoGen {
1430
1467
  }
1431
1468
  }
1432
1469
 
1470
+ /**
1471
+ * パフォーマンスモニタの計測キーを決める。名前が無ければ無名関数用のIDを採番する。(#2333)
1472
+ * @param name 関数名
1473
+ * @param suffix キーに付ける接尾辞 ('_body' / '_sys' など)
1474
+ */
1475
+ private getPerfMonitorKey (name: string, suffix = ''): string {
1476
+ if (name) { return `${name}${suffix}` }
1477
+ this.performanceMonitor.mumeiId++
1478
+ return `anous_${this.performanceMonitor.mumeiId}${suffix}`
1479
+ }
1480
+
1481
+ /**
1482
+ * コードを、実行時間を計測する即時実行関数で包む。(#2333)
1483
+ * システム関数本体・システム関数の計測で使う。
1484
+ * @param body 計測対象。isExprなら式、そうでなければ文の並び
1485
+ * @param key 計測結果を記録するキー
1486
+ * @param type 計測の種別
1487
+ * @param opts isExpr:bodyが式か / isAsync:非同期か / startVar,timeVar:使用するJS変数名
1488
+ */
1489
+ private wrapPerfMonitor (body: string, key: string, type: string,
1490
+ opts: { isExpr: boolean, isAsync: boolean, startVar: string, timeVar: string }): string {
1491
+ const funcDef = opts.isAsync ? 'async function' : 'function'
1492
+ // 式なら値を返す必要がある。文ならそのまま実行する。
1493
+ const inner = opts.isExpr ? `return ${body};\n` : `${body}\n`
1494
+ // 末尾に改行を置かないこと。行頭のセミコロンは cleanGeneratedCode で消えてしまい、
1495
+ // 直後の文が `(` で始まると関数呼び出しとして繋がってしまう (#2333)
1496
+ let code = `(${funcDef} (key, type) {\n` +
1497
+ `const ${opts.startVar} = performance.now() * 1000;\n` +
1498
+ 'try {\n' +
1499
+ inner +
1500
+ '} finally {\n' +
1501
+ `const ${opts.timeVar} = performance.now() * 1000 - ${opts.startVar};\n` +
1502
+ genPerfMonitorUpdate(opts.timeVar) +
1503
+ `}})('${key}', '${type}')`
1504
+ // 非同期関数を包んだ場合、待たないと計測対象が完了しない (#2333)
1505
+ if (opts.isAsync) { code = `await ${code}` }
1506
+ // 文として包んだ場合は、ここで文を閉じる
1507
+ return opts.isExpr ? code : code + ';\n'
1508
+ }
1509
+
1510
+ /**
1511
+ * ユーザ関数の実行時間を計測するために、関数の本体の前後へ挿入するコードを返す。(#2333)
1512
+ * @param key 計測結果を記録するキー
1513
+ * @returns [関数本体の直前に挿入するコード, 関数本体の直後に挿入するコード]
1514
+ */
1515
+ private genPerfMonitorInject (key: string): [string, string] {
1516
+ const injectAtStart = 'const performanceMonitorEnd = (function (key, type) {\n' +
1517
+ 'const uf_start = performance.now() * 1000;\n' +
1518
+ 'return function () {\n' +
1519
+ 'const el_time = performance.now() * 1000 - uf_start;\n' +
1520
+ genPerfMonitorUpdate('el_time') +
1521
+ `};})('${key}', 'user');` +
1522
+ 'try {\n'
1523
+ const injectAtEnd = '} finally { performanceMonitorEnd(); }\n'
1524
+ return [injectAtStart, injectAtEnd]
1525
+ }
1526
+
1433
1527
  convWhile(node: AstWhile): string {
1434
1528
  const exprAst = node.blocks[0]
1435
1529
  const blockAst = node.blocks[1]
@@ -1534,18 +1628,14 @@ export class NakoGen {
1534
1628
  }
1535
1629
 
1536
1630
  /**
1537
- * 関数の呼び出し
1538
- * @param {Ast} node
1539
- * @param {boolean} isExpression
1540
- * @returns string コード
1631
+ * どの関数を呼び出すのか関数を特定する (convCallFunc用)
1632
+ * @returns 変数の検索結果 res と、関数の定義 func
1541
1633
  */
1542
- convCallFunc(node: AstCallFunc, isExpression: boolean): string {
1543
- const funcName = NakoGen.getFuncName(node.name)
1634
+ private resolveCallTarget (funcName: string, node: AstCallFunc): { res: FindVarResult, func: any } {
1544
1635
  const res = this.findVar(funcName)
1545
1636
  if (res === null) {
1546
1637
  throw NakoSyntaxError.fromNode(`関数『${funcName}』が見当たりません。有効プラグイン=[` + this.getPluginList().join(', ') + ']', node)
1547
1638
  }
1548
- // どの関数を呼び出すのか関数を特定する
1549
1639
  let func
1550
1640
  if (res.i === 0) { // plugin function
1551
1641
  func = this.__self.getFunc(funcName)
@@ -1558,6 +1648,156 @@ export class NakoGen {
1558
1648
  // 無名関数の可能性
1559
1649
  if (func === undefined) { func = { return_none: false, asyncFn: !!node.asyncFn } }
1560
1650
  }
1651
+ return { res, func }
1652
+ }
1653
+
1654
+ /**
1655
+ * 関数内からpureでないプラグイン関数を呼び出すとき、呼び出しの前後で
1656
+ * ローカル変数を __self.__locals と同期するコードを生成する。
1657
+ * @returns 呼び出し前に実行するコード begin と、呼び出し後に実行するコード end
1658
+ */
1659
+ private genLocalVarsSyncCode (): { begin: string, end: string } {
1660
+ let begin = ''
1661
+ let end = ''
1662
+ // 展開されたローカル変数の列挙
1663
+ const localVars = []
1664
+ for (const name of Array.from(this.varsSet.names.values())) {
1665
+ if (NakoGen.isValidIdentifier(name)) {
1666
+ localVars.push({ str: JSON.stringify(name), js: this.varname_get(name) })
1667
+ }
1668
+ }
1669
+
1670
+ // --- 実行前 ---
1671
+ // 全ての展開されていないローカル変数を __self.__locals にコピーする
1672
+ begin += '__self.__locals = __vars;\n'
1673
+ // 全ての展開されたローカル変数を __self.__locals に保存する
1674
+ if (localVars.length > 0) {
1675
+ begin += '/* 全ての展開されたローカル変数を __self.__locals に保存 */\n'
1676
+ for (const v of localVars) {
1677
+ begin += `__self.__locals.set(${v.str}, ${v.js});\n`
1678
+ }
1679
+ }
1680
+
1681
+ // --- 実行後 ---
1682
+ // 全ての展開されたローカル変数を __self.__locals から受け取る
1683
+ // 「それ」は関数の実行結果を受け取るために使うためスキップ。
1684
+ if (localVars.length > 0) {
1685
+ end += '/* 全ての展開されたローカル変数を __self.__locals から受け取る */\n'
1686
+ for (const v of localVars) {
1687
+ if (v.js !== 'それ') {
1688
+ end += `__self.__varslist[2].set(${v.str}, __self.__locals.get(${v.str}));\n`
1689
+ }
1690
+ }
1691
+ }
1692
+ return { begin, end }
1693
+ }
1694
+
1695
+ /**
1696
+ * 引数のリストを連結してJSの実引数のコードにする。
1697
+ * 必要に応じて、引数のundefinedチェックのコードを挟む。
1698
+ */
1699
+ private genCallArgsCode (funcName: string, res: FindVarResult, args: string[], node: AstCallFunc): string {
1700
+ if ((!this.warnUndefinedCallingUserFunc && res.i !== 0) || (!this.warnUndefinedCallingSystemFunc && res.i === 0)) {
1701
+ return args.join(',')
1702
+ }
1703
+ // 引数チェックの例外 #1260
1704
+ const noCheckFuncs: {[key: string]: boolean} = { 'TYPEOF': true, '変数型確認': true }
1705
+ const argsA: string[] = []
1706
+ args.forEach((arg: string) => {
1707
+ if (arg === '__self' || noCheckFuncs[funcName] === true) { // #1260
1708
+ argsA.push(`${arg}`)
1709
+ } else {
1710
+ // 引数のundefinedチェックのコードを入れる
1711
+ const msg = (res.i === 0) ? '命令『$0』の引数にundefinedを渡しています。' : 'ユーザ命令『$0』の引数にundefinedを渡しています。'
1712
+ const poolIndex = this.addConstPool(msg, [funcName], node.file, node.line)
1713
+ // argが空になる対策 #1315
1714
+ const argStr = (arg === '') ? '""' : arg
1715
+ argsA.push(`(__self.chk(${argStr}, ${poolIndex}))`)
1716
+ }
1717
+ })
1718
+ return argsA.join(', ')
1719
+ }
1720
+
1721
+ /**
1722
+ * 関数の戻り値を変数「それ」に代入するためのラッパを返す。
1723
+ * @returns [前置するコード, 後置するコード]
1724
+ */
1725
+ private getSoreWrap (): [string, string] {
1726
+ if (this.speedMode.invalidSore !== 0) { return ['', ''] }
1727
+ return ['__self.__setSore(', ')']
1728
+ }
1729
+
1730
+ /** 戻り値のない関数呼び出しのコードを組み立てる */
1731
+ private genVoidCallCode (node: AstCallFunc, parts: CallCodeParts): string {
1732
+ const { funcCall, funcBegin, funcEnd } = parts
1733
+ let code: string
1734
+ if (funcEnd === '') {
1735
+ code = `/*VOID関数呼出*/${funcBegin}${funcCall}\n`
1736
+ } else {
1737
+ code = `/*VOID関数呼出(前後処理付)*/${funcBegin}try {\n${indentLines(funcCall, 1)};\n} finally {\n${indentLines(funcEnd, 1)}}\n`
1738
+ }
1739
+ // パフォーマンスモニタ:システム関数。ここでのcodeは式ではなく文なので、文として包む (#2333)
1740
+ if (parts.sysPerfKey) {
1741
+ code = this.wrapPerfMonitor(code, parts.sysPerfKey, 'system',
1742
+ { isExpr: false, isAsync: parts.isAsync, startVar: 'sf_start', timeVar: 'sl_time' })
1743
+ }
1744
+ // 行番号を追加
1745
+ return this.convLineno(node, false) + code
1746
+ }
1747
+
1748
+ /** 戻り値のある関数呼び出しのコードを組み立てる */
1749
+ private genValueCallCode (node: AstCallFunc, isExpression: boolean, parts: CallCodeParts): string {
1750
+ const { funcDef, funcCall, funcBegin, funcEnd, isAsync } = parts
1751
+ // 関数の戻り値を「それ」に記録する
1752
+ const [sorePrefix, sorePostfix] = this.getSoreWrap()
1753
+ let code: string
1754
+ if (funcBegin === '' && funcEnd === '') {
1755
+ code = `${sorePrefix}${funcCall}${sorePostfix}`
1756
+ } else if (funcEnd === '') {
1757
+ const funcBody = `${sorePrefix}${funcCall}${sorePostfix}`
1758
+ const funcObj = `${funcDef}(){ return ${funcBody} }`
1759
+ const funcCallThis = `(${funcObj}).call(this)`
1760
+ code = `/* funcCallThis1 */${funcCallThis}`
1761
+ } else { // つまり、pure=falseの場合
1762
+ const varI = `$nako_i${this.loopId}`
1763
+ this.loopId++
1764
+ code = `/* funcCallThis2 */(${funcDef}(){\n` +
1765
+ indentLines(funcBegin, 1) + '\n' +
1766
+ indentLines('try {', 1) + '\n' +
1767
+ indentLines(`let ${varI} = ${funcCall};`, 2) + '\n' +
1768
+ indentLines(`return ${varI};`, 2) + '\n' +
1769
+ indentLines('} finally {', 2) + '\n' +
1770
+ indentLines(funcEnd, 1) + '\n' +
1771
+ indentLines('}', 1) + '\n' +
1772
+ '}).call(this)'
1773
+ if (isAsync) {
1774
+ code = `await (${code})`
1775
+ }
1776
+ code = `${sorePrefix}${code}${sorePostfix}`
1777
+ }
1778
+ // パフォーマンスモニタ:システム関数。ここでのcodeは式なので、値を返す形で包む (#2333)
1779
+ if (parts.sysPerfKey) {
1780
+ code = this.wrapPerfMonitor(code, parts.sysPerfKey, 'system',
1781
+ { isExpr: true, isAsync, startVar: 'sf_start', timeVar: 'sl_time' })
1782
+ }
1783
+ // ...して
1784
+ // (メモ) 式の中では文末の『;』を付けてはいけない。付けると不正なJSになる (#2064)
1785
+ if (!isExpression && (node.josi === 'して' || node.josi === '')) {
1786
+ code = this.convLineno(node, false) + code
1787
+ code += ';\n'
1788
+ }
1789
+ return code
1790
+ }
1791
+
1792
+ /**
1793
+ * 関数の呼び出し
1794
+ * @param {Ast} node
1795
+ * @param {boolean} isExpression
1796
+ * @returns string コード
1797
+ */
1798
+ convCallFunc(node: AstCallFunc, isExpression: boolean): string {
1799
+ const funcName = NakoGen.getFuncName(node.name)
1800
+ const { res, func } = this.resolveCallTarget(funcName, node)
1561
1801
  // 関数の参照渡しか?
1562
1802
  if (node.type === 'func_pointer') {
1563
1803
  return res.js
@@ -1589,73 +1829,15 @@ export class NakoGen {
1589
1829
  // 関数内 (__varslist.length > 3) からプラグイン関数 (res.i === 0) を呼び出すとき、 そのプラグイン関数がpureでなければ
1590
1830
  // 呼び出しの直前に全てのローカル変数をthis.__localsに入れる。
1591
1831
  if (res.i === 0 && this.varslistSet.length > 3 && func.pure !== true && this.speedMode.forcePure === 0) { // undefinedはfalseとみなす
1592
- // 展開されたローカル変数の列挙
1593
- const localVars = []
1594
- for (const name of Array.from(this.varsSet.names.values())) {
1595
- if (NakoGen.isValidIdentifier(name)) {
1596
- localVars.push({ str: JSON.stringify(name), js: this.varname_get(name) })
1597
- }
1598
- }
1599
-
1600
- // --- 実行前 ---
1601
- // 全ての展開されていないローカル変数を __self.__locals にコピーする
1602
- funcBegin += '__self.__locals = __vars;\n'
1603
- // 全ての展開されたローカル変数を __self.__locals に保存する
1604
- if (localVars.length > 0) {
1605
- funcBegin += '/* 全ての展開されたローカル変数を __self.__locals に保存 */\n'
1606
- for (const v of localVars) {
1607
- funcBegin += `__self.__locals.set(${v.str}, ${v.js});\n`
1608
- }
1609
- }
1610
-
1611
- // --- 実行後 ---
1612
- // 全ての展開されたローカル変数を __self.__locals から受け取る
1613
- // 「それ」は関数の実行結果を受け取るために使うためスキップ。
1614
- if (localVars.length > 0) {
1615
- funcEnd += '/* 全ての展開されたローカル変数を __self.__locals から受け取る */\n'
1616
- for (const v of localVars) {
1617
- if (v.js !== 'それ') {
1618
- funcEnd += `__self.__varslist[2].set(${v.str}, __self.__locals[${v.str}]);\n`
1619
- }
1620
- }
1621
- }
1832
+ const sync = this.genLocalVarsSyncCode()
1833
+ funcBegin += sync.begin
1834
+ funcEnd += sync.end
1622
1835
  }
1623
1836
  // 変数「それ」が補完されていることをヒントとして出力
1624
1837
  if (argsOpts.sore) { funcBegin += '/*[sore]*/' }
1625
1838
 
1626
- const indent = (text: string, n: number) => {
1627
- let result = ''
1628
- for (const line of text.split('\n')) {
1629
- if (line !== '') {
1630
- result += ' '.repeat(n) + line + '\n'
1631
- }
1632
- }
1633
- return result
1634
- }
1635
-
1636
- // 引数チェックの例外 #1260
1637
- const noCheckFuncs: {[key: string]: boolean} = { 'TYPEOF': true, '変数型確認': true }
1638
1839
  // 関数呼び出しコードの構築
1639
- let argsCode: string
1640
- if ((!this.warnUndefinedCallingUserFunc && res.i !== 0) || (!this.warnUndefinedCallingSystemFunc && res.i === 0)) {
1641
- argsCode = args.join(',')
1642
- } else {
1643
- const argsA: string[] = []
1644
- args.forEach((arg: string) => {
1645
- if (arg === '__self' || noCheckFuncs[funcName] === true) { // #1260
1646
- argsA.push(`${arg}`)
1647
- } else {
1648
- // 引数のundefinedチェックのコードを入れる
1649
- const msg = (res.i === 0) ? '命令『$0』の引数にundefinedを渡しています。' : 'ユーザ命令『$0』の引数にundefinedを渡しています。'
1650
- const poolIndex = this.addConstPool(msg, [funcName], node.file, node.line)
1651
- // argが空になる対策 #1315
1652
- const argStr = (arg === '') ? '""' : arg
1653
- argsA.push(`(__self.chk(${argStr}, ${poolIndex}))`)
1654
- }
1655
- })
1656
- argsCode = argsA.join(', ')
1657
- }
1658
-
1840
+ const argsCode = this.genCallArgsCode(funcName, res, args, node)
1659
1841
  let funcCall = `${res.js}(${argsCode})`
1660
1842
  if (func.asyncFn) {
1661
1843
  funcDef = `async ${funcDef}`
@@ -1668,111 +1850,20 @@ export class NakoGen {
1668
1850
  funcBegin += `const __local_async${varI} = __self.__vars;\n`
1669
1851
  funcEnd += `__self.__vars = __local_async${varI};\n`
1670
1852
  }
1853
+ // パフォーマンスモニタ:システム関数本体 (呼び出しコードを除く)
1671
1854
  if (res.i === 0 && this.performanceMonitor.systemFunctionBody !== 0) {
1672
- let key = funcName
1673
- if (!key) {
1674
- if (typeof this.performanceMonitor.mumeiId === 'undefined') {
1675
- this.performanceMonitor.mumeiId = 0
1676
- }
1677
- this.performanceMonitor.mumeiId++
1678
- key = `anous_${this.performanceMonitor.mumeiId}`
1679
- }
1680
- funcCall = `(${funcDef} (key, type) {\n` +
1681
- 'const sbf_start = performance.now() * 1000;\n' +
1682
- 'try {\n' +
1683
- 'return ' + funcCall + ';\n' +
1684
- '} finally {\n' +
1685
- 'const sbl_time = performance.now() * 1000 - sbf_start;\n' +
1686
- 'if (!__self.__performance_monitor) {\n' +
1687
- '__self.__performance_monitor={};\n' +
1688
- '__self.__performance_monitor[key] = { called:1, totel_usec: sbl_time, min_usec: sbl_time, max_usec: sbl_time, type: type };\n' +
1689
- '} else if (!__self.__performance_monitor[key]) {\n' +
1690
- '__self.__performance_monitor[key] = { called:1, totel_usec: sbl_time, min_usec: sbl_time, max_usec: sbl_time, type: type };\n' +
1691
- '} else {\n' +
1692
- '__self.__performance_monitor[key].called++;\n' +
1693
- '__self.__performance_monitor[key].totel_usec+=sbl_time;\n' +
1694
- 'if(__self.__performance_monitor[key].min_usec>sbl_time){__self.__performance_monitor[key].min_usec=sbl_time;}\n' +
1695
- 'if(__self.__performance_monitor[key].max_usec<sbl_time){__self.__performance_monitor[key].max_usec=sbl_time;}\n' +
1696
- `}}})('${funcName}_body', 'sysbody')\n`
1697
- }
1698
-
1699
- let code = ''
1700
- if (func.return_none) {
1701
- // ------------------------------------
1702
- // 戻り値のない関数の場合
1703
- // ------------------------------------
1704
- if (funcEnd === '') {
1705
- code = `/*VOID関数呼出*/${funcBegin}${funcCall}\n`
1706
- } else {
1707
- code = `/*VOID関数呼出(前後処理付)*/${funcBegin}try {\n${indent(funcCall, 1)};\n} finally {\n${indent(funcEnd, 1)}}\n`
1708
- }
1709
- // 行番号を追加
1710
- code = this.convLineno(node, false) + code
1711
- } else {
1712
- // ------------------------------------
1713
- // 戻り値のある関数の場合
1714
- // ------------------------------------
1715
- let sorePrefex = ''
1716
- let sorePostfix = ''
1717
- if (this.speedMode.invalidSore === 0) {
1718
- // 関数の戻り値を記録
1719
- sorePrefex = '__self.__setSore('
1720
- sorePostfix = ')'
1721
- }
1722
- if (funcBegin === '' && funcEnd === '') {
1723
- code = `${sorePrefex}${funcCall}${sorePostfix}`
1724
- } else {
1725
- if (funcEnd === '') {
1726
- const funcBody = `${sorePrefex}${funcCall}${sorePostfix}`
1727
- const funcObj = `${funcDef}(){ return ${funcBody} }`
1728
- const funcCallThis = `(${funcObj}).call(this)`
1729
- code = `/* funcCallThis1 */${funcCallThis}`
1730
- } else { // つまり、pure=falseの場合
1731
- const varI = `$nako_i${this.loopId}`
1732
- this.loopId++
1733
- code = `/* funcCallThis2 */(${funcDef}(){\n` +
1734
- indent(funcBegin, 1) + '\n' +
1735
- indent('try {', 1) + '\n' +
1736
- indent(`let ${varI} = ${funcCall};`, 2) + '\n' +
1737
- indent(`return ${varI};`, 2) + '\n' +
1738
- indent('} finally {', 2) + '\n' +
1739
- indent(funcEnd, 1) + '\n' +
1740
- indent('}', 1) + '\n' +
1741
- '}).call(this)'
1742
- if (func.asyncFn) {
1743
- code = `await (${code})`
1744
- }
1745
- code = `${sorePrefex}${code}${sorePostfix}`
1746
- }
1747
- }
1748
- // ...して
1749
- if (node.josi === 'して' || (node.josi === '' && !isExpression)) {
1750
- code = this.convLineno(node, false) + code
1751
- code += ';\n'
1752
- }
1753
- }
1754
-
1755
- if (res.i === 0 && this.performanceMonitor.systemFunction !== 0) {
1756
- code = '(function (key, type) {\n' +
1757
- 'const sf_start = performance.now() * 1000;\n' +
1758
- 'try {\n' +
1759
- 'return ' + code + ';\n' +
1760
- '} finally {\n' +
1761
- 'const sl_time = performance.now() * 1000 - sf_start;\n' +
1762
- 'if (!__self.__performance_monitor) {\n' +
1763
- '__self.__performance_monitor={};\n' +
1764
- '__self.__performance_monitor[key] = { called:1, totel_usec: sl_time, min_usec: sl_time, max_usec: sl_time, type: type };\n' +
1765
- '} else if (!__self.__performance_monitor[key]) {\n' +
1766
- '__self.__performance_monitor[key] = { called:1, totel_usec: sl_time, min_usec: sl_time, max_usec: sl_time, type: type };\n' +
1767
- '} else {\n' +
1768
- '__self.__performance_monitor[key].called++;\n' +
1769
- '__self.__performance_monitor[key].totel_usec+=sl_time;\n' +
1770
- 'if(__self.__performance_monitor[key].min_usec>sl_time){__self.__performance_monitor[key].min_usec=sl_time;}\n' +
1771
- 'if(__self.__performance_monitor[key].max_usec<sl_time){__self.__performance_monitor[key].max_usec=sl_time;}\n' +
1772
- `}}})('${funcName}_sys', 'system')\n`
1855
+ funcCall = this.wrapPerfMonitor(funcCall, this.getPerfMonitorKey(funcName, '_body'), 'sysbody',
1856
+ { isExpr: true, isAsync: !!func.asyncFn, startVar: 'sbf_start', timeVar: 'sbl_time' })
1773
1857
  }
1858
+ // パフォーマンスモニタ:システム関数 (呼び出しコードを含む) はコードの組み立て中に適用する
1859
+ const sysPerfKey = (res.i === 0 && this.performanceMonitor.systemFunction !== 0)
1860
+ ? this.getPerfMonitorKey(funcName, '_sys')
1861
+ : null
1774
1862
 
1775
- return code
1863
+ const parts: CallCodeParts = { funcDef, funcCall, funcBegin, funcEnd, isAsync: !!func.asyncFn, sysPerfKey }
1864
+ return (func.return_none)
1865
+ ? this.genVoidCallCode(node, parts)
1866
+ : this.genValueCallCode(node, isExpression, parts)
1776
1867
  }
1777
1868
 
1778
1869
  convCallValue(node: AstBlocks, isExpression: boolean): string {
@@ -1783,9 +1874,8 @@ export class NakoGen {
1783
1874
  if (isExpression) {
1784
1875
  return funcCall
1785
1876
  }
1786
- const sorePrefex = (this.speedMode.invalidSore === 0) ? '__self.__setSore(' : ''
1787
- const sorePostfix = (this.speedMode.invalidSore === 0) ? ')' : ''
1788
- return this.convLineno(node, false) + `${sorePrefex}${funcCall}${sorePostfix};\n`
1877
+ const [sorePrefix, sorePostfix] = this.getSoreWrap()
1878
+ return this.convLineno(node, false) + `${sorePrefix}${funcCall}${sorePostfix};\n`
1789
1879
  }
1790
1880
 
1791
1881
  convRenbun(node: AstOperator): string {
@@ -2272,6 +2362,7 @@ export function generateJS(com: NakoCompiler, ast: Ast, opt: NakoGenOptions): Na
2272
2362
 
2273
2363
  // ランダムな関数名を生成
2274
2364
  const funcID = String((new Date()).getTime()) + '_' + Math.floor(0xFFFFFFFF * Math.random()).toString()
2365
+ let runtimeResult = ''
2275
2366
  // テストの実行
2276
2367
  if (js && opt.isTest) {
2277
2368
  js += '\n__self._runTests(__tests);\n'
@@ -2279,6 +2370,8 @@ export function generateJS(com: NakoCompiler, ast: Ast, opt: NakoGenOptions): Na
2279
2370
  // async method
2280
2371
  if (gen.numAsyncFn > 0 || gen.debugOption.useDebug) {
2281
2372
  const asyncMain = '__eval_nako3async_' + funcID + '__'
2373
+ const asyncMainPromise = '__eval_nako3async_promise_' + funcID + '__'
2374
+ runtimeResult = `return ${asyncMainPromise}`
2282
2375
  js = `
2283
2376
  // ------------------------------------------------------------------
2284
2377
  // <nadesiko3::gen::async id="${funcID}" times="${gen.numAsyncFn}">
@@ -2291,12 +2384,11 @@ async function ${asyncMain}(__self) {
2291
2384
  } // end of ${asyncMain}
2292
2385
  // ------------------------------------------------------------------
2293
2386
  // call ${asyncMain}
2294
- (async () => {
2387
+ const ${asyncMainPromise} = (async () => {
2295
2388
  if (__self.__v0.get('__standalone')) {
2296
2389
  await ${asyncMain}(self);
2297
2390
  } else {
2298
- ${asyncMain}.call(self, self)
2299
- .then(() => { /* __async_ok__ */ })
2391
+ await ${asyncMain}.call(self, self)
2300
2392
  .catch(err => {
2301
2393
  if (err.message === '__終わる__') { return }
2302
2394
  __self.numFailures++
@@ -2337,14 +2429,32 @@ ${syncMain}(__self)
2337
2429
  // デバッグメッセージ
2338
2430
  let codeImportFiles = ''
2339
2431
  const importNames = []
2432
+ // プラグインとして登録せず、コピーだけが必要なファイル
2433
+ // (plugin_system_*.mjs は plugin_system.mjs 側でマージ済みのため二重登録しない) #2351
2434
+ const noRegisterFiles = [
2435
+ 'nako_errors.mjs',
2436
+ 'plugin_system_debug.mjs',
2437
+ 'plugin_system_math.mjs',
2438
+ 'plugin_system_string.mjs',
2439
+ 'plugin_system_array.mjs',
2440
+ 'plugin_system_datetime.mjs',
2441
+ 'plugin_system_url.mjs',
2442
+ 'plugin_system_types.mjs',
2443
+ 'plugin_system_json.mjs',
2444
+ 'plugin_system_regexp.mjs',
2445
+ 'plugin_system_dict.mjs',
2446
+ 'plugin_system_stdio.mjs',
2447
+ 'plugin_system_timer.mjs'
2448
+ ]
2340
2449
  for (const f of opt.importFiles) {
2341
- if (f === 'nako_errors.mjs') { continue }
2450
+ if (noRegisterFiles.includes(f)) { continue }
2342
2451
  const ff = 'nako3runtime_' + f.replace(/\.(js|mjs)$/, '').replace(/[^a-zA-Z0-9_]/g, '_')
2343
2452
  importNames.push(ff)
2344
2453
  codeImportFiles += `import ${ff} from './nako3runtime/${f}'\n`
2345
2454
  }
2346
2455
  // ---
2347
2456
  const initCode = gen.getPluginInitCode()
2457
+ // runtimeEnvはnew Functionの関数本体として実行するコードで、非同期時はトップレベルのreturnを含む。
2348
2458
  const runtimeEnvCode = `
2349
2459
  // <runtimeEnvCode>
2350
2460
  const self = this
@@ -2352,6 +2462,7 @@ ${opt.codeEnv}
2352
2462
  ${jsInit}
2353
2463
  ${initCode}
2354
2464
  ${js}
2465
+ ${runtimeResult}
2355
2466
  // </runtimeEnvCode>
2356
2467
  `
2357
2468
  com.getLogger().trace('--- generate::jsInit ---\n' + jsInit)