From 7919c10548ec0c4cf3a85388947c90c7800c0815 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Thu, 16 Nov 2023 10:34:22 +0900
Subject: [PATCH] v8,tools: expose necessary V8 defines

---
 common.gypi                     |  60 +++++++++++++---
 configure.py                    |   1 +
 tools/generate_config_gypi.py   | 118 ++++++++++++++++++++++++--------
 tools/v8_gypfiles/features.gypi |  37 +++++++---
 unofficial.gni                  |   9 +--
 5 files changed, 176 insertions(+), 49 deletions(-)

diff --git a/common.gypi b/common.gypi
index a74d6420af1997..b5de59ca996c60 100644
--- a/common.gypi
+++ b/common.gypi
@@ -75,8 +75,16 @@
 
     'v8_win64_unwinding_info': 1,
 
-    # TODO(refack): make v8-perfetto happen
+    # Variables controlling external defines exposed in public headers.
+    'v8_enable_conservative_stack_scanning%': 0,
+    'v8_enable_direct_local%': 0,
+    'v8_enable_map_packing%': 0,
+    'v8_enable_pointer_compression_shared_cage%': 0,
+    'v8_enable_sandbox%': 0,
+    'v8_enable_v8_checks%': 0,
+    'v8_enable_zone_compression%': 0,
     'v8_use_perfetto': 0,
+    'tsan%': 0,
 
     ##### end V8 defaults #####
 
@@ -134,7 +142,7 @@
             }],
           ],
         },
-        'defines': [ 'DEBUG', '_DEBUG', 'V8_ENABLE_CHECKS' ],
+        'defines': [ 'DEBUG', '_DEBUG' ],
         'cflags': [ '-g', '-O0' ],
         'conditions': [
           ['OS in "aix os400"', {
@@ -257,11 +265,8 @@
       }
     },
 
-    # Defines these mostly for node-gyp to pickup, and warn addon authors of
-    # imminent V8 deprecations, also to sync how dependencies are configured.
+    # Defines these mostly for node-gyp to pickup.
     'defines': [
-      'V8_DEPRECATION_WARNINGS',
-      'V8_IMMINENT_DEPRECATION_WARNINGS',
       '_GLIBCXX_USE_CXX11_ABI=1',
     ],
 
@@ -369,15 +374,50 @@
           }],
         ],
       }],
+      # The defines bellow must include all things from the external_v8_defines
+      # list in v8/BUILD.gn.
+      ['v8_enable_v8_checks == 1', {
+        'defines': ['V8_ENABLE_CHECKS'],
+      }],
       ['v8_enable_pointer_compression == 1', {
-        'defines': [
-          'V8_COMPRESS_POINTERS',
-          'V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE',
-        ],
+        'defines': ['V8_COMPRESS_POINTERS'],
+      }],
+      ['v8_enable_pointer_compression_shared_cage == 1', {
+        'defines': ['V8_COMPRESS_POINTERS_IN_SHARED_CAGE'],
+      }],
+      ['v8_enable_pointer_compression == 1 and v8_enable_pointer_compression_shared_cage != 1', {
+        'defines': ['V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE'],
       }],
       ['v8_enable_pointer_compression == 1 or v8_enable_31bit_smis_on_64bit_arch == 1', {
         'defines': ['V8_31BIT_SMIS_ON_64BIT_ARCH'],
       }],
+      ['v8_enable_zone_compression == 1', {
+        'defines': ['V8_COMPRESS_ZONES',],
+      }],
+      ['v8_enable_sandbox == 1', {
+        'defines': ['V8_ENABLE_SANDBOX',],
+      }],
+      ['v8_deprecation_warnings == 1', {
+        'defines': ['V8_DEPRECATION_WARNINGS',],
+      }],
+      ['v8_imminent_deprecation_warnings == 1', {
+        'defines': ['V8_IMMINENT_DEPRECATION_WARNINGS',],
+      }],
+      ['v8_use_perfetto == 1', {
+        'defines': ['V8_USE_PERFETTO',],
+      }],
+      ['v8_enable_map_packing == 1', {
+        'defines': ['V8_MAP_PACKING',],
+      }],
+      ['tsan == 1', {
+        'defines': ['V8_IS_TSAN',],
+      }],
+      ['v8_enable_conservative_stack_scanning == 1', {
+        'defines': ['V8_ENABLE_CONSERVATIVE_STACK_SCANNING',],
+      }],
+      ['v8_enable_direct_local == 1', {
+        'defines': ['V8_ENABLE_DIRECT_LOCAL',],
+      }],
       ['OS == "win"', {
         'defines': [
           'WIN32',
diff --git a/configure.py b/configure.py
index ff08312b680146..84b016cd853080 100755
--- a/configure.py
+++ b/configure.py
@@ -1505,6 +1505,7 @@ def configure_v8(o):
   o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0
   o['variables']['v8_enable_shared_ro_heap'] = 0 if options.enable_pointer_compression or options.disable_shared_ro_heap else 1
   o['variables']['v8_enable_extensible_ro_snapshot'] = 0
+  o['variables']['v8_enable_v8_checks'] = 1 if options.debug else 0
   o['variables']['v8_trace_maps'] = 1 if options.trace_maps else 0
   o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform)
   o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8)
diff --git a/tools/generate_config_gypi.py b/tools/generate_config_gypi.py
index 26cc5f04201fec..948a740529ec34 100755
--- a/tools/generate_config_gypi.py
+++ b/tools/generate_config_gypi.py
@@ -7,69 +7,133 @@
 # This script reads the configurations of GN and outputs a config.gypi file that
 # will be used to populate process.config.variables.
 
+import argparse
 import re
 import os
 import subprocess
 import sys
 
-root_dir = os.path.dirname(os.path.dirname(__file__))
-sys.path.append(os.path.join(root_dir, 'node', 'tools'))
-import getmoduleversion
+sys.path.append(os.path.dirname(__file__))
 import getnapibuildversion
 
+# The defines bellow must include all things from the external_v8_defines list
+# in v8/BUILD.gn.
+# TODO(zcbenz): Import from v8_features.json once this change gets into Node:
+# https://chromium-review.googlesource.com/c/v8/v8/+/5040612
+V8_FEATURE_DEFINES = {
+  'v8_enable_v8_checks': 'V8_ENABLE_CHECKS',
+  'v8_enable_pointer_compression': 'V8_COMPRESS_POINTERS',
+  'v8_enable_pointer_compression_shared_cage': 'V8_COMPRESS_POINTERS_IN_SHARED_CAGE',
+  'v8_enable_31bit_smis_on_64bit_arch': 'V8_31BIT_SMIS_ON_64BIT_ARCH',
+  'v8_enable_zone_compression': 'V8_COMPRESS_ZONES',
+  'v8_enable_sandbox': 'V8_ENABLE_SANDBOX',
+  'v8_deprecation_warnings': 'V8_DEPRECATION_WARNINGS',
+  'v8_imminent_deprecation_warnings': 'V8_IMMINENT_DEPRECATION_WARNINGS',
+  'v8_use_perfetto': 'V8_USE_PERFETTO',
+  'v8_enable_map_packing': 'V8_MAP_PACKING',
+  'tsan': 'V8_IS_TSAN',
+  'v8_enable_conservative_stack_scanning': 'V8_ENABLE_CONSERVATIVE_STACK_SCANNING',
+  'v8_enable_direct_local': 'V8_ENABLE_DIRECT_LOCAL',
+}
+
+# Regex used for parsing results of "gn args".
 GN_RE = re.compile(r'(\w+)\s+=\s+(.*?)$', re.MULTILINE)
 
+if sys.platform == 'win32':
+  GN = 'gn.exe'
+else:
+  GN = 'gn'
+
+def bool_to_number(v):
+  return 1 if v else 0
+
 def bool_string_to_number(v):
-  return 1 if v == 'true' else 0
+  return bool_to_number(v == 'true')
+
+def get_gn_config(out_dir):
+  # Read args from GN configurations.
+  gn_args = subprocess.check_output(
+      [GN, 'args', '--list', '--short', '-C', out_dir])
+  config = dict(re.findall(GN_RE, gn_args.decode()))
+  # Get napi_build_version from Node, which is not part of GN args.
+  config['napi_build_version'] = getnapibuildversion.get_napi_version()
+  return config
+
+def get_v8_config(out_dir, node_gn_path):
+  # For args that have default values in V8's GN configurations, we can not rely
+  # on the values printed by "gn args", because most of them would be empty
+  # strings, and the actual value would depend on the logics in v8/BUILD.gn.
+  # So we print out the defines and deduce the feature from them instead.
+  node_defines = subprocess.check_output(
+      [GN, 'desc', '-C', out_dir, node_gn_path + ":libnode", 'defines']).decode().split('\n')
+  v8_config = {}
+  for feature, define in V8_FEATURE_DEFINES.items():
+    v8_config[feature] = bool_to_number(define in node_defines)
+  return v8_config
 
-def translate_config(config):
-  return {
+def translate_config(out_dir, config, v8_config):
+  config_gypi = {
     'target_defaults': {
       'default_configuration':
           'Debug' if config['is_debug'] == 'true' else 'Release',
     },
     'variables': {
       'asan': bool_string_to_number(config['is_asan']),
+      'enable_lto': config['use_thin_lto'],
+      'is_debug': bool_string_to_number(config['is_debug']),
       'llvm_version': 13,
       'napi_build_version': config['napi_build_version'],
       'node_builtin_shareable_builtins':
           eval(config['node_builtin_shareable_builtins']),
       'node_module_version': int(config['node_module_version']),
-      'node_shared': bool_string_to_number(config['is_component_build']),
       'node_use_openssl': config['node_use_openssl'],
       'node_use_node_code_cache': config['node_use_node_code_cache'],
       'node_use_node_snapshot': config['node_use_node_snapshot'],
-      'v8_enable_31bit_smis_on_64bit_arch':
-          bool_string_to_number(config['v8_enable_31bit_smis_on_64bit_arch']),
-      'v8_enable_pointer_compression':
-          bool_string_to_number(config['v8_enable_pointer_compression']),
       'v8_enable_i18n_support':
           bool_string_to_number(config['v8_enable_i18n_support']),
       'v8_enable_inspector':  # this is actually a node misnomer
           bool_string_to_number(config['node_enable_inspector']),
       'shlib_suffix': 'dylib' if sys.platform == 'darwin' else 'so',
+      'tsan': bool_string_to_number(config['is_tsan']),
+      # TODO(zcbenz): Shared components are not supported in GN config yet.
+      'node_shared': 'false',
+      'node_shared_brotli': 'false',
+      'node_shared_cares': 'false',
+      'node_shared_http_parser': 'false',
+      'node_shared_libuv': 'false',
+      'node_shared_nghttp2': 'false',
+      'node_shared_nghttp3': 'false',
+      'node_shared_ngtcp2': 'false',
+      'node_shared_openssl': 'false',
+      'node_shared_zlib': 'false',
     }
   }
+  config_gypi['variables'].update(v8_config)
+  return config_gypi
 
-def main(gn_out_dir, output_file, depfile):
-  # Get GN config and parse into a dictionary.
-  if sys.platform == 'win32':
-    gn = 'gn.exe'
-  else:
-    gn = 'gn'
-  gnconfig = subprocess.check_output(
-                 [gn, 'args', '--list', '--short', '-C', gn_out_dir])
-  config = dict(re.findall(GN_RE, gnconfig.decode('utf-8')))
-  config['node_module_version'] = getmoduleversion.get_version()
-  config['napi_build_version'] = getnapibuildversion.get_napi_version()
+def main():
+  parser = argparse.ArgumentParser(
+      description='Generate config.gypi file from GN configurations')
+  parser.add_argument('target', help='path to generated config.gypi file')
+  parser.add_argument('--out-dir', help='path to the output directory',
+                      default='out/Release')
+  parser.add_argument('--node-gn-path', help='path of the node target in GN',
+                      default='//node')
+  parser.add_argument('--dep-file', help='path to an optional dep file',
+                      default=None)
+  args, unknown_args = parser.parse_known_args()
+
+  config = get_gn_config(args.out_dir)
+  v8_config = get_v8_config(args.out_dir, args.node_gn_path)
 
   # Write output.
-  with open(output_file, 'w') as f:
-    f.write(repr(translate_config(config)))
+  with open(args.target, 'w') as f:
+    f.write(repr(translate_config(args.out_dir, config, v8_config)))
 
   # Write depfile. Force regenerating config.gypi when GN configs change.
-  with open(depfile, 'w') as f:
-    f.write('%s: %s '%(output_file, 'build.ninja'))
+  if args.dep_file:
+    with open(args.dep_file, 'w') as f:
+      f.write('%s: %s '%(args.target, 'build.ninja'))
 
 if __name__ == '__main__':
-  main(sys.argv[1], sys.argv[2], sys.argv[3])
+  main()
diff --git a/tools/v8_gypfiles/features.gypi b/tools/v8_gypfiles/features.gypi
index b5738243363182..be1dfe407a290d 100644
--- a/tools/v8_gypfiles/features.gypi
+++ b/tools/v8_gypfiles/features.gypi
@@ -155,6 +155,7 @@
 
     # Enable pointer compression (sets -dV8_COMPRESS_POINTERS).
     'v8_enable_pointer_compression%': 0,
+    'v8_enable_pointer_compression_shared_cage%': 0,
     'v8_enable_31bit_smis_on_64bit_arch%': 0,
 
     # Sets -dV8_SHORT_BUILTIN_CALLS
@@ -197,6 +198,15 @@
     # currently implemented.
     'v8_use_perfetto%': 0,
 
+    # Enable map packing & unpacking (sets -dV8_MAP_PACKING).
+    'v8_enable_map_packing%': 0,
+
+    # Scan the call stack conservatively during garbage collection.
+    'v8_enable_conservative_stack_scanning%': 0,
+
+    # Use direct pointers in local handles.
+    'v8_enable_direct_local%': 0,
+
     # Controls the threshold for on-heap/off-heap Typed Arrays.
     'v8_typed_array_max_size_in_heap%': 64,
 
@@ -345,10 +355,13 @@
         'defines': ['ENABLE_VTUNE_JIT_INTERFACE',],
       }],
       ['v8_enable_pointer_compression==1', {
-        'defines': [
-          'V8_COMPRESS_POINTERS',
-          'V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE',
-        ],
+        'defines': ['V8_COMPRESS_POINTERS'],
+      }],
+      ['v8_enable_pointer_compression_shared_cage==1', {
+        'defines': ['V8_COMPRESS_POINTERS_IN_SHARED_CAGE'],
+      }],
+      ['v8_enable_pointer_compression==1 and v8_enable_pointer_compression_shared_cage==0', {
+        'defines': ['V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE'],
       }],
       ['v8_enable_pointer_compression==1 or v8_enable_31bit_smis_on_64bit_arch==1', {
         'defines': ['V8_31BIT_SMIS_ON_64BIT_ARCH',],
@@ -392,13 +405,9 @@
       }],
       ['v8_deprecation_warnings==1', {
         'defines': ['V8_DEPRECATION_WARNINGS',],
-      },{
-        'defines!': ['V8_DEPRECATION_WARNINGS',],
       }],
       ['v8_imminent_deprecation_warnings==1', {
         'defines': ['V8_IMMINENT_DEPRECATION_WARNINGS',],
-      },{
-        'defines!': ['V8_IMMINENT_DEPRECATION_WARNINGS',],
       }],
       ['v8_enable_i18n_support==1', {
         'defines': ['V8_INTL_SUPPORT',],
@@ -443,9 +452,21 @@
       ['v8_use_perfetto==1', {
         'defines': ['V8_USE_PERFETTO',],
       }],
+      ['v8_enable_map_packing==1', {
+        'defines': ['V8_MAP_PACKING',],
+      }],
       ['v8_win64_unwinding_info==1', {
         'defines': ['V8_WIN64_UNWINDING_INFO',],
       }],
+      ['tsan==1', {
+        'defines': ['V8_IS_TSAN',],
+      }],
+      ['v8_enable_conservative_stack_scanning==1', {
+        'defines': ['V8_ENABLE_CONSERVATIVE_STACK_SCANNING',],
+      }],
+      ['v8_enable_direct_local==1', {
+        'defines': ['V8_ENABLE_DIRECT_LOCAL',],
+      }],
       ['v8_enable_regexp_interpreter_threaded_dispatch==1', {
         'defines': ['V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH',],
       }],
diff --git a/unofficial.gni b/unofficial.gni
index 29685d4ade559f..6524969c374f6f 100644
--- a/unofficial.gni
+++ b/unofficial.gni
@@ -256,10 +256,11 @@ template("node_gn_build") {
     script = "tools/generate_config_gypi.py"
     outputs = [ "$target_gen_dir/config.gypi" ]
     depfile = "$target_gen_dir/$target_name.d"
-    script_args = [ "$root_build_dir" ]
-    script_args += outputs
-    script_args += [ depfile ]
-    args = rebase_path(script_args, root_build_dir)
+    args = rebase_path(outputs, root_build_dir) + [
+      "--out-dir", rebase_path(root_build_dir, root_build_dir),
+      "--dep-file", rebase_path(depfile, root_build_dir),
+      "--node-gn-path", node_path,
+    ]
   }
 
   executable("node_js2c") {