23
23
from tools .toolchain_profiler import ToolchainProfiler
24
24
25
25
import base64
26
+ import glob
27
+ import hashlib
26
28
import json
27
29
import logging
28
30
import os
@@ -500,8 +502,7 @@ def ensure_archive_index(archive_file):
500
502
run_process ([shared .LLVM_RANLIB , archive_file ])
501
503
502
504
503
- @ToolchainProfiler .profile_block ('JS symbol generation' )
504
- def get_all_js_syms ():
505
+ def generate_js_symbols ():
505
506
# Runs the js compiler to generate a list of all symbols available in the JS
506
507
# libraries. This must be done separately for each linker invokation since the
507
508
# list of symbols depends on what settings are used.
@@ -516,6 +517,59 @@ def get_all_js_syms():
516
517
if shared .is_c_symbol (name ):
517
518
name = shared .demangle_c_symbol_name (name )
518
519
library_syms .add (name )
520
+ return library_syms
521
+
522
+
523
+ @ToolchainProfiler .profile_block ('JS symbol generation' )
524
+ def get_all_js_syms ():
525
+ # Avoiding using the cache when generating struct info since
526
+ # this step is performed while the cache is locked.
527
+ if settings .BOOTSTRAPPING_STRUCT_INFO or config .FROZEN_CACHE :
528
+ return generate_js_symbols ()
529
+
530
+ # To avoid the cost of calling generate_js_symbols each time an executable is
531
+ # linked we cache symbol lists for the N most recently used configs.
532
+ # We define a cache hit as when the settings and `--js-library` contents are
533
+ # identical.
534
+ input_files = {}
535
+ # The JS library files are all absolute paths so can't conflict with this one.
536
+ input_files ['settings.json' ] = json .dumps (settings .dict (), sort_keys = True , indent = 2 )
537
+ for jslib in sorted (glob .glob (utils .path_from_root ('src' ) + '/library*.js' )):
538
+ input_files [jslib ] = read_file (jslib )
539
+ for jslib in settings .JS_LIBRARIES :
540
+ if not os .path .isabs (jslib ):
541
+ jslib = utils .path_from_root ('src' , jslib )
542
+ input_files [jslib ] = read_file (jslib )
543
+ input_data = []
544
+ for name , content in input_files .items ():
545
+ content_hash = hashlib .sha1 (content .encode ('utf-8' )).hexdigest ()
546
+ input_data .append (f'{ name } : { content_hash } ' )
547
+
548
+ input_data = '\n ' .join (input_data ) + '\n '
549
+ cache_filename = None
550
+ num_cache_entries = 20
551
+
552
+ with cache .lock ('js_symbol_lists' ):
553
+ oldest_timestamp = 0
554
+ for i in range (num_cache_entries ):
555
+ input_file = cache .get_path (f'js_symbol_list_{ i } .inputs' )
556
+ list_file = cache .get_path (f'js_symbol_list_{ i } .txt' )
557
+ if not os .path .exists (input_file ) or not os .path .exists (list_file ):
558
+ cache_filename = list_file
559
+ break
560
+ timestamp = os .path .getmtime (input_file )
561
+ if timestamp < oldest_timestamp or not oldest_timestamp :
562
+ oldest_timestamp = timestamp
563
+ cache_filename = list_file
564
+ if read_file (input_file ) == input_data :
565
+ # Cache hit, read the symbol list from the list_file
566
+ return read_file (list_file ).splitlines ()
567
+
568
+ # Cache miss. Generate a new symbol list and write to the the cache
569
+ library_syms = generate_js_symbols ()
570
+
571
+ write_file (cache_filename , '\n ' .join (library_syms ) + '\n ' )
572
+ write_file (shared .replace_suffix (cache_filename , '.inputs' ), input_data )
519
573
520
574
return library_syms
521
575
0 commit comments