mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	bpo-45152: refactor the dis module to make handling of hasconst opcodes more generic (GH-28258)
This commit is contained in:
		
							parent
							
								
									1afc7b3219
								
							
						
					
					
						commit
						40d2ac92f9
					
				
					 1 changed files with 35 additions and 19 deletions
				
			
		
							
								
								
									
										54
									
								
								Lib/dis.py
									
										
									
									
									
								
							
							
						
						
									
										54
									
								
								Lib/dis.py
									
										
									
									
									
								
							|  | @ -26,6 +26,7 @@ | |||
| MAKE_FUNCTION = opmap['MAKE_FUNCTION'] | ||||
| MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') | ||||
| 
 | ||||
| LOAD_CONST = opmap['LOAD_CONST'] | ||||
| 
 | ||||
| def _try_compile(source, name): | ||||
|     """Attempts to compile the given source, first as an expression and | ||||
|  | @ -317,19 +318,32 @@ def get_instructions(x, *, first_line=None): | |||
|                                    co.co_names, co.co_consts, | ||||
|                                    linestarts, line_offset, co_positions=co.co_positions()) | ||||
| 
 | ||||
| def _get_const_info(const_index, const_list): | ||||
| def _get_const_value(op, arg, co_consts): | ||||
|     """Helper to get the value of the const in a hasconst op. | ||||
| 
 | ||||
|        Returns the dereferenced constant if this is possible. | ||||
|        Otherwise (if it is a LOAD_CONST and co_consts is not | ||||
|        provided) returns the dis.UNKNOWN sentinel. | ||||
|     """ | ||||
|     assert op in hasconst | ||||
| 
 | ||||
|     argval = UNKNOWN | ||||
|     if op == LOAD_CONST: | ||||
|         if co_consts is not None: | ||||
|             argval = co_consts[arg] | ||||
|     return argval | ||||
| 
 | ||||
| def _get_const_info(op, arg, co_consts): | ||||
|     """Helper to get optional details about const references | ||||
| 
 | ||||
|        Returns the dereferenced constant and its repr if the constant | ||||
|        list is defined. | ||||
|        Returns the dereferenced constant and its repr if the value | ||||
|        can be calculated. | ||||
|        Otherwise returns the sentinel value dis.UNKNOWN for the value | ||||
|        and an empty string for its repr. | ||||
|     """ | ||||
|     if const_list is not None: | ||||
|         argval = const_list[const_index] | ||||
|         return argval, repr(argval) | ||||
|     else: | ||||
|         return UNKNOWN, '' | ||||
|     argval = _get_const_value(op, arg, co_consts) | ||||
|     argrepr = repr(argval) if argval is not UNKNOWN else '' | ||||
|     return argval, argrepr | ||||
| 
 | ||||
| def _get_name_info(name_index, get_name, **extrainfo): | ||||
|     """Helper to get optional details about named references | ||||
|  | @ -371,14 +385,14 @@ def parse_exception_table(code): | |||
|         return entries | ||||
| 
 | ||||
| def _get_instructions_bytes(code, varname_from_oparg=None, | ||||
|                             names=None, constants=None, | ||||
|                             names=None, co_consts=None, | ||||
|                             linestarts=None, line_offset=0, | ||||
|                             exception_entries=(), co_positions=None): | ||||
|     """Iterate over the instructions in a bytecode string. | ||||
| 
 | ||||
|     Generates a sequence of Instruction namedtuples giving the details of each | ||||
|     opcode.  Additional information about the code's runtime environment | ||||
|     (e.g. variable names, constants) can be specified using optional | ||||
|     (e.g. variable names, co_consts) can be specified using optional | ||||
|     arguments. | ||||
| 
 | ||||
|     """ | ||||
|  | @ -408,7 +422,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, | |||
|             #    raw name index for LOAD_GLOBAL, LOAD_CONST, etc. | ||||
|             argval = arg | ||||
|             if op in hasconst: | ||||
|                 argval, argrepr = _get_const_info(arg, constants) | ||||
|                 argval, argrepr = _get_const_info(op, arg, co_consts) | ||||
|             elif op in hasname: | ||||
|                 argval, argrepr = _get_name_info(arg, get_name) | ||||
|             elif op in hasjabs: | ||||
|  | @ -457,7 +471,7 @@ def _disassemble_recursive(co, *, file=None, depth=None): | |||
|                 _disassemble_recursive(x, file=file, depth=depth) | ||||
| 
 | ||||
| def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, | ||||
|                        names=None, constants=None, linestarts=None, | ||||
|                        names=None, co_consts=None, linestarts=None, | ||||
|                        *, file=None, line_offset=0, exception_entries=(), | ||||
|                        co_positions=None): | ||||
|     # Omit the line number column entirely if we have no line number info | ||||
|  | @ -476,7 +490,7 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, | |||
|     else: | ||||
|         offset_width = 4 | ||||
|     for instr in _get_instructions_bytes(code, varname_from_oparg, names, | ||||
|                                          constants, linestarts, | ||||
|                                          co_consts, linestarts, | ||||
|                                          line_offset=line_offset, exception_entries=exception_entries, | ||||
|                                          co_positions=co_positions): | ||||
|         new_source_line = (show_lineno and | ||||
|  | @ -557,11 +571,13 @@ def _find_imports(co): | |||
|     opargs = [(op, arg) for _, op, arg in _unpack_opargs(co.co_code) | ||||
|                   if op != EXTENDED_ARG] | ||||
|     for i, (op, oparg) in enumerate(opargs): | ||||
|         if (op == IMPORT_NAME and i >= 2 | ||||
|                 and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST): | ||||
|             level = consts[opargs[i-2][1]] | ||||
|             fromlist = consts[opargs[i-1][1]] | ||||
|             yield (names[oparg], level, fromlist) | ||||
|         if op == IMPORT_NAME and i >= 2: | ||||
|             from_op = opargs[i-1] | ||||
|             level_op = opargs[i-2] | ||||
|             if (from_op[0] in hasconst and level_op[0] in hasconst): | ||||
|                 level = _get_const_value(level_op[0], level_op[1], consts) | ||||
|                 fromlist = _get_const_value(from_op[0], from_op[1], consts) | ||||
|                 yield (names[oparg], level, fromlist) | ||||
| 
 | ||||
| def _find_store_names(co): | ||||
|     """Find names of variables which are written in the code | ||||
|  | @ -635,7 +651,7 @@ def dis(self): | |||
|         with io.StringIO() as output: | ||||
|             _disassemble_bytes(co.co_code, | ||||
|                                varname_from_oparg=co._varname_from_oparg, | ||||
|                                names=co.co_names, constants=co.co_consts, | ||||
|                                names=co.co_names, co_consts=co.co_consts, | ||||
|                                linestarts=self._linestarts, | ||||
|                                line_offset=self._line_offset, | ||||
|                                file=output, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Irit Katriel
						Irit Katriel