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,58 @@ 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
+ input_files ['settings.json' ] = json .dumps (settings .dict (), sort_keys = True , indent = 2 )
536
+ for jslib in sorted (glob .glob (utils .path_from_root ('src' ) + '/library*.js' )):
537
+ input_files [jslib ] = read_file (jslib )
538
+ for jslib in settings .JS_LIBRARIES :
539
+ if os .path .abspath (jslib ):
540
+ jslib = utils .path_from_root ('src' , jslib )
541
+ input_files [jslib ] = read_file (jslib )
542
+ input_data = []
543
+ for name , content in input_files .items ():
544
+ content_hash = hashlib .sha1 (content .encode ('utf-8' )).hexdigest ()
545
+ input_data .append (f'{ name } : { content_hash } ' )
546
+
547
+ input_data = '\n ' .join (input_data ) + '\n '
548
+ cache_filename = None
549
+ num_cache_entries = 20
550
+
551
+ with cache .lock ('js_symbol_lists' ):
552
+ oldest_timestamp = 0
553
+ for i in range (num_cache_entries ):
554
+ input_file = cache .get_path (f'js_symbol_list_{ i } .inputs' )
555
+ list_file = cache .get_path (f'js_symbol_list_{ i } .txt' )
556
+ if not os .path .exists (input_file ) or not os .path .exists (list_file ):
557
+ cache_filename = list_file
558
+ break
559
+ timestamp = os .path .getmtime (input_file )
560
+ if timestamp < oldest_timestamp or not oldest_timestamp :
561
+ oldest_timestamp = timestamp
562
+ cache_filename = list_file
563
+ if read_file (input_file ) == input_data :
564
+ # Cache hit, read the symbol list from the list_file
565
+ return read_file (list_file ).splitlines ()
566
+
567
+ # Cache miss. Generate a new symbol list and write to the the cache
568
+ library_syms = generate_js_symbols ()
569
+
570
+ write_file (cache_filename , '\n ' .join (library_syms ) + '\n ' )
571
+ write_file (shared .replace_suffix (cache_filename , '.inputs' ), input_data )
519
572
520
573
return library_syms
521
574
0 commit comments