1425bb815Sopenharmony_ci#!/usr/bin/env python 2425bb815Sopenharmony_ci 3425bb815Sopenharmony_ci# Copyright JS Foundation and other contributors, http://js.foundation 4425bb815Sopenharmony_ci# 5425bb815Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 6425bb815Sopenharmony_ci# you may not use this file except in compliance with the License. 7425bb815Sopenharmony_ci# You may obtain a copy of the License at 8425bb815Sopenharmony_ci# 9425bb815Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 10425bb815Sopenharmony_ci# 11425bb815Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 12425bb815Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS 13425bb815Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14425bb815Sopenharmony_ci# See the License for the specific language governing permissions and 15425bb815Sopenharmony_ci# limitations under the License. 16425bb815Sopenharmony_ci 17425bb815Sopenharmony_cifrom __future__ import print_function 18425bb815Sopenharmony_ci 19425bb815Sopenharmony_citry: 20425bb815Sopenharmony_ci from configparser import ConfigParser 21425bb815Sopenharmony_ciexcept ImportError: 22425bb815Sopenharmony_ci from ConfigParser import ConfigParser 23425bb815Sopenharmony_ci 24425bb815Sopenharmony_ciimport argparse 25425bb815Sopenharmony_ciimport fileinput 26425bb815Sopenharmony_ciimport json 27425bb815Sopenharmony_ciimport os 28425bb815Sopenharmony_ciimport re 29425bb815Sopenharmony_ci 30425bb815Sopenharmony_cifrom settings import PROJECT_DIR 31425bb815Sopenharmony_ci 32425bb815Sopenharmony_ci 33425bb815Sopenharmony_ciMAGIC_STRINGS_INI = os.path.join(PROJECT_DIR, 'jerry-core', 'lit', 'lit-magic-strings.ini') 34425bb815Sopenharmony_ciMAGIC_STRINGS_INC_H = os.path.join(PROJECT_DIR, 'jerry-core', 'lit', 'lit-magic-strings.inc.h') 35425bb815Sopenharmony_ci 36425bb815Sopenharmony_ci 37425bb815Sopenharmony_cidef debug_dump(obj): 38425bb815Sopenharmony_ci def deepcopy(obj): 39425bb815Sopenharmony_ci if isinstance(obj, (list, tuple)): 40425bb815Sopenharmony_ci return [deepcopy(e) for e in obj] 41425bb815Sopenharmony_ci if isinstance(obj, set): 42425bb815Sopenharmony_ci return [repr(e) for e in obj] 43425bb815Sopenharmony_ci if isinstance(obj, dict): 44425bb815Sopenharmony_ci return {repr(k): deepcopy(e) for k, e in obj.items()} 45425bb815Sopenharmony_ci return obj 46425bb815Sopenharmony_ci return json.dumps(deepcopy(obj), indent=4) 47425bb815Sopenharmony_ci 48425bb815Sopenharmony_ci 49425bb815Sopenharmony_cidef read_magic_string_defs(debug=False): 50425bb815Sopenharmony_ci # Read the `jerry-core/lit/lit-magic-strings.ini` file and returns the magic 51425bb815Sopenharmony_ci # string definitions found therein in the form of 52425bb815Sopenharmony_ci # [LIT_MAGIC_STRINGS] 53425bb815Sopenharmony_ci # LIT_MAGIC_STRING_xxx = "vvv" 54425bb815Sopenharmony_ci # ... 55425bb815Sopenharmony_ci # as 56425bb815Sopenharmony_ci # [('LIT_MAGIC_STRING_xxx', 'vvv'), ...] 57425bb815Sopenharmony_ci # sorted by length and alpha. 58425bb815Sopenharmony_ci ini_parser = ConfigParser() 59425bb815Sopenharmony_ci ini_parser.optionxform = str # case sensitive options (magic string IDs) 60425bb815Sopenharmony_ci ini_parser.read(MAGIC_STRINGS_INI) 61425bb815Sopenharmony_ci 62425bb815Sopenharmony_ci defs = [(str_ref, json.loads(str_value) if str_value != '' else '') 63425bb815Sopenharmony_ci for str_ref, str_value in ini_parser.items('LIT_MAGIC_STRINGS')] 64425bb815Sopenharmony_ci defs = sorted(defs, key=lambda ref_value: (len(ref_value[1]), ref_value[1])) 65425bb815Sopenharmony_ci 66425bb815Sopenharmony_ci if debug: 67425bb815Sopenharmony_ci print('debug: magic string definitions: {dump}' 68425bb815Sopenharmony_ci .format(dump=debug_dump(defs))) 69425bb815Sopenharmony_ci 70425bb815Sopenharmony_ci return defs 71425bb815Sopenharmony_ci 72425bb815Sopenharmony_ci 73425bb815Sopenharmony_cidef extract_magic_string_refs(debug=False): 74425bb815Sopenharmony_ci results = {} 75425bb815Sopenharmony_ci 76425bb815Sopenharmony_ci def process_line(fname, lnum, line, guard_stack): 77425bb815Sopenharmony_ci # Build `results` dictionary as 78425bb815Sopenharmony_ci # results['LIT_MAGIC_STRING_xxx'][('!defined (CONFIG_DISABLE_yyy_BUILTIN)', ...)] 79425bb815Sopenharmony_ci # = [('zzz.c', 123), ...] 80425bb815Sopenharmony_ci # meaning that the given literal is referenced under the given guards at 81425bb815Sopenharmony_ci # the listed (file, line number) locations. 82425bb815Sopenharmony_ci for str_ref in re.findall('LIT_MAGIC_STRING_[a-zA-Z0-9_]+', line): 83425bb815Sopenharmony_ci if str_ref in ['LIT_MAGIC_STRING_DEF', 84425bb815Sopenharmony_ci 'LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE', 85425bb815Sopenharmony_ci 'LIT_MAGIC_STRING_LENGTH_LIMIT', 86425bb815Sopenharmony_ci 'LIT_MAGIC_STRING__COUNT']: 87425bb815Sopenharmony_ci continue 88425bb815Sopenharmony_ci 89425bb815Sopenharmony_ci guard_set = set() 90425bb815Sopenharmony_ci for guards in guard_stack: 91425bb815Sopenharmony_ci guard_set.update(guards) 92425bb815Sopenharmony_ci guard_tuple = tuple(sorted(guard_set)) 93425bb815Sopenharmony_ci 94425bb815Sopenharmony_ci if str_ref not in results: 95425bb815Sopenharmony_ci results[str_ref] = {} 96425bb815Sopenharmony_ci str_guards = results[str_ref] 97425bb815Sopenharmony_ci 98425bb815Sopenharmony_ci if guard_tuple not in str_guards: 99425bb815Sopenharmony_ci str_guards[guard_tuple] = [] 100425bb815Sopenharmony_ci file_list = str_guards[guard_tuple] 101425bb815Sopenharmony_ci 102425bb815Sopenharmony_ci file_list.append((fname, lnum)) 103425bb815Sopenharmony_ci 104425bb815Sopenharmony_ci def process_guard(guard): 105425bb815Sopenharmony_ci # Transform `#ifndef MACRO` to `#if !defined (MACRO)` and 106425bb815Sopenharmony_ci # `#ifdef MACRO` to `#if defined (MACRO)` to enable or-ing/and-ing the 107425bb815Sopenharmony_ci # conditions later on. 108425bb815Sopenharmony_ci if guard.startswith('ndef '): 109425bb815Sopenharmony_ci guard = guard.replace('ndef ', '!defined (', 1) + ')' 110425bb815Sopenharmony_ci elif guard.startswith('def '): 111425bb815Sopenharmony_ci guard = guard.replace('def ', 'defined (', 1) + ')' 112425bb815Sopenharmony_ci return guard 113425bb815Sopenharmony_ci 114425bb815Sopenharmony_ci def process_file(fname): 115425bb815Sopenharmony_ci # Builds `guard_stack` list for each line of a file as 116425bb815Sopenharmony_ci # [['!defined (CONFIG_DISABLE_yyy_BUILTIN)', ...], ...] 117425bb815Sopenharmony_ci # meaning that all the listed guards (conditionals) have to hold for the 118425bb815Sopenharmony_ci # line to be kept by the preprocessor. 119425bb815Sopenharmony_ci guard_stack = [] 120425bb815Sopenharmony_ci 121425bb815Sopenharmony_ci for line in fileinput.input(fname): 122425bb815Sopenharmony_ci if_match = re.match('^ *# *if(.*)', line) 123425bb815Sopenharmony_ci elif_match = re.match('^ *# *elif(.*)', line) 124425bb815Sopenharmony_ci else_match = re.match('^ *# *else', line) 125425bb815Sopenharmony_ci endif_match = re.match('^ *# *endif', line) 126425bb815Sopenharmony_ci if if_match is not None: 127425bb815Sopenharmony_ci guard_stack.append([process_guard(if_match.group(1))]) 128425bb815Sopenharmony_ci elif elif_match is not None: 129425bb815Sopenharmony_ci guards = guard_stack[-1] 130425bb815Sopenharmony_ci guards[-1] = '!(%s)' % guards[-1] 131425bb815Sopenharmony_ci guards.append(process_guard(elif_match.group(1))) 132425bb815Sopenharmony_ci elif else_match is not None: 133425bb815Sopenharmony_ci guards = guard_stack[-1] 134425bb815Sopenharmony_ci guards[-1] = '!(%s)' % guards[-1] 135425bb815Sopenharmony_ci elif endif_match is not None: 136425bb815Sopenharmony_ci guard_stack.pop() 137425bb815Sopenharmony_ci 138425bb815Sopenharmony_ci lnum = fileinput.filelineno() 139425bb815Sopenharmony_ci process_line(fname, lnum, line, guard_stack) 140425bb815Sopenharmony_ci 141425bb815Sopenharmony_ci if guard_stack: 142425bb815Sopenharmony_ci print('warning: {fname}: unbalanced preprocessor conditional ' 143425bb815Sopenharmony_ci 'directives (analysis finished with no closing `#endif` ' 144425bb815Sopenharmony_ci 'for {guard_stack})' 145425bb815Sopenharmony_ci .format(fname=fname, guard_stack=guard_stack)) 146425bb815Sopenharmony_ci 147425bb815Sopenharmony_ci for root, _, files in os.walk(os.path.join(PROJECT_DIR, 'jerry-core')): 148425bb815Sopenharmony_ci for fname in files: 149425bb815Sopenharmony_ci if (fname.endswith('.c') or fname.endswith('.h')) \ 150425bb815Sopenharmony_ci and fname != 'lit-magic-strings.inc.h': 151425bb815Sopenharmony_ci process_file(os.path.join(root, fname)) 152425bb815Sopenharmony_ci 153425bb815Sopenharmony_ci if debug: 154425bb815Sopenharmony_ci print('debug: magic string references: {dump}' 155425bb815Sopenharmony_ci .format(dump=debug_dump(results))) 156425bb815Sopenharmony_ci 157425bb815Sopenharmony_ci return results 158425bb815Sopenharmony_ci 159425bb815Sopenharmony_ci 160425bb815Sopenharmony_cidef calculate_magic_string_guards(defs, uses, debug=False): 161425bb815Sopenharmony_ci extended_defs = [] 162425bb815Sopenharmony_ci 163425bb815Sopenharmony_ci for str_ref, str_value in defs: 164425bb815Sopenharmony_ci if str_ref not in uses: 165425bb815Sopenharmony_ci print('warning: unused magic string {str_ref}' 166425bb815Sopenharmony_ci .format(str_ref=str_ref)) 167425bb815Sopenharmony_ci continue 168425bb815Sopenharmony_ci 169425bb815Sopenharmony_ci # Calculate the most compact guard, i.e., if a magic string is 170425bb815Sopenharmony_ci # referenced under various guards, keep the one that is more generic. 171425bb815Sopenharmony_ci # E.g., 172425bb815Sopenharmony_ci # guard1 = A and B and C and D and E and F 173425bb815Sopenharmony_ci # guard2 = A and B and C 174425bb815Sopenharmony_ci # then guard1 or guard2 == guard2. 175425bb815Sopenharmony_ci guards = [set(guard_tuple) for guard_tuple in uses[str_ref].keys()] 176425bb815Sopenharmony_ci for i, guard_i in enumerate(guards): 177425bb815Sopenharmony_ci if guard_i is None: 178425bb815Sopenharmony_ci continue 179425bb815Sopenharmony_ci for j, guard_j in enumerate(guards): 180425bb815Sopenharmony_ci if j == i or guard_j is None: 181425bb815Sopenharmony_ci continue 182425bb815Sopenharmony_ci if guard_i < guard_j: 183425bb815Sopenharmony_ci guards[j] = None 184425bb815Sopenharmony_ci guards = {tuple(sorted(guard)) for guard in guards if guard is not None} 185425bb815Sopenharmony_ci 186425bb815Sopenharmony_ci extended_defs.append((str_ref, str_value, guards)) 187425bb815Sopenharmony_ci 188425bb815Sopenharmony_ci if debug: 189425bb815Sopenharmony_ci print('debug: magic string definitions (with guards): {dump}' 190425bb815Sopenharmony_ci .format(dump=debug_dump(extended_defs))) 191425bb815Sopenharmony_ci 192425bb815Sopenharmony_ci return extended_defs 193425bb815Sopenharmony_ci 194425bb815Sopenharmony_ci 195425bb815Sopenharmony_cidef guards_to_str(guards): 196425bb815Sopenharmony_ci return ' \\\n|| '.join(' && '.join(g.strip() for g in sorted(guard)) 197425bb815Sopenharmony_ci for guard in sorted(guards)) 198425bb815Sopenharmony_ci 199425bb815Sopenharmony_ci 200425bb815Sopenharmony_cidef generate_header(gen_file): 201425bb815Sopenharmony_ci header = \ 202425bb815Sopenharmony_ci"""/* Copyright JS Foundation and other contributors, http://js.foundation 203425bb815Sopenharmony_ci * 204425bb815Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 205425bb815Sopenharmony_ci * you may not use this file except in compliance with the License. 206425bb815Sopenharmony_ci * You may obtain a copy of the License at 207425bb815Sopenharmony_ci * 208425bb815Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 209425bb815Sopenharmony_ci * 210425bb815Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 211425bb815Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS 212425bb815Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 213425bb815Sopenharmony_ci * See the License for the specific language governing permissions and 214425bb815Sopenharmony_ci * limitations under the License. 215425bb815Sopenharmony_ci */ 216425bb815Sopenharmony_ci 217425bb815Sopenharmony_ci/* This file is automatically generated by the %s script 218425bb815Sopenharmony_ci * from %s. Do not edit! */ 219425bb815Sopenharmony_ci""" % (os.path.basename(__file__), os.path.basename(MAGIC_STRINGS_INI)) 220425bb815Sopenharmony_ci print(header, file=gen_file) 221425bb815Sopenharmony_ci 222425bb815Sopenharmony_ci 223425bb815Sopenharmony_cidef generate_magic_string_defs(gen_file, defs): 224425bb815Sopenharmony_ci last_guards = set([()]) 225425bb815Sopenharmony_ci for str_ref, str_value, guards in defs: 226425bb815Sopenharmony_ci if last_guards != guards: 227425bb815Sopenharmony_ci if () not in last_guards: 228425bb815Sopenharmony_ci print('#endif', file=gen_file) 229425bb815Sopenharmony_ci if () not in guards: 230425bb815Sopenharmony_ci print('#if {guards}'.format(guards=guards_to_str(guards)), file=gen_file) 231425bb815Sopenharmony_ci 232425bb815Sopenharmony_ci print('LIT_MAGIC_STRING_DEF ({str_ref}, {str_value})' 233425bb815Sopenharmony_ci .format(str_ref=str_ref, str_value=json.dumps(str_value)), file=gen_file) 234425bb815Sopenharmony_ci 235425bb815Sopenharmony_ci last_guards = guards 236425bb815Sopenharmony_ci 237425bb815Sopenharmony_ci if () not in last_guards: 238425bb815Sopenharmony_ci print('#endif', file=gen_file) 239425bb815Sopenharmony_ci 240425bb815Sopenharmony_ci 241425bb815Sopenharmony_cidef generate_first_magic_strings(gen_file, defs): 242425bb815Sopenharmony_ci print(file=gen_file) # empty line separator 243425bb815Sopenharmony_ci 244425bb815Sopenharmony_ci max_size = len(defs[-1][1]) 245425bb815Sopenharmony_ci for size in range(max_size + 1): 246425bb815Sopenharmony_ci last_guards = set([()]) 247425bb815Sopenharmony_ci for str_ref, str_value, guards in defs: 248425bb815Sopenharmony_ci if len(str_value) >= size: 249425bb815Sopenharmony_ci if () not in guards and () in last_guards: 250425bb815Sopenharmony_ci print('#if {guards}'.format(guards=guards_to_str(guards)), file=gen_file) 251425bb815Sopenharmony_ci elif () not in guards and () not in last_guards: 252425bb815Sopenharmony_ci if guards == last_guards: 253425bb815Sopenharmony_ci continue 254425bb815Sopenharmony_ci print('#elif {guards}'.format(guards=guards_to_str(guards)), file=gen_file) 255425bb815Sopenharmony_ci elif () in guards and () not in last_guards: 256425bb815Sopenharmony_ci print('#else', file=gen_file) 257425bb815Sopenharmony_ci 258425bb815Sopenharmony_ci print('LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE ({size}, {str_ref})' 259425bb815Sopenharmony_ci .format(size=size, str_ref=str_ref), file=gen_file) 260425bb815Sopenharmony_ci 261425bb815Sopenharmony_ci if () in guards: 262425bb815Sopenharmony_ci break 263425bb815Sopenharmony_ci 264425bb815Sopenharmony_ci last_guards = guards 265425bb815Sopenharmony_ci 266425bb815Sopenharmony_ci if () not in last_guards: 267425bb815Sopenharmony_ci print('#endif', file=gen_file) 268425bb815Sopenharmony_ci 269425bb815Sopenharmony_ci 270425bb815Sopenharmony_cidef main(): 271425bb815Sopenharmony_ci parser = argparse.ArgumentParser(description='lit-magic-strings.inc.h generator') 272425bb815Sopenharmony_ci parser.add_argument('--debug', action='store_true', help='enable debug output') 273425bb815Sopenharmony_ci args = parser.parse_args() 274425bb815Sopenharmony_ci 275425bb815Sopenharmony_ci defs = read_magic_string_defs(debug=args.debug) 276425bb815Sopenharmony_ci uses = extract_magic_string_refs(debug=args.debug) 277425bb815Sopenharmony_ci 278425bb815Sopenharmony_ci extended_defs = calculate_magic_string_guards(defs, uses, debug=args.debug) 279425bb815Sopenharmony_ci 280425bb815Sopenharmony_ci with open(MAGIC_STRINGS_INC_H, 'w') as gen_file: 281425bb815Sopenharmony_ci generate_header(gen_file) 282425bb815Sopenharmony_ci generate_magic_string_defs(gen_file, extended_defs) 283425bb815Sopenharmony_ci generate_first_magic_strings(gen_file, extended_defs) 284425bb815Sopenharmony_ci 285425bb815Sopenharmony_ci 286425bb815Sopenharmony_ciif __name__ == '__main__': 287425bb815Sopenharmony_ci main() 288