def map_add(state, arg): if sys.version_info >= (3, 8): # PEP 572 The MAP_ADD expects the value as the first element in the stack # and the key as the second element. new_value_type = Const.unwrap(state.stack.pop()) new_key_type = Const.unwrap(state.stack.pop()) else: new_key_type = Const.unwrap(state.stack.pop()) new_value_type = Const.unwrap(state.stack.pop()) state.stack[-arg] = Dict[Union[state.stack[-arg].key_type, new_key_type], Union[state.stack[-arg].value_type, new_value_type]]
def load_method(state, arg): """Like load_attr. Replaces TOS object with method and TOS.""" o = state.stack.pop() name = state.get_name(arg) if isinstance(o, Const): method = Const(getattr(o.value, name)) elif isinstance(o, typehints.AnyTypeConstraint): method = typehints.Any else: method = Const(BoundMethod(getattr(o, name), o)) state.stack.append(method)
def _getattr(o, name): if isinstance(o, Const) and hasattr(o.value, name): return Const(getattr(o.value, name)) elif (inspect.isclass(o) and isinstance(getattr(o, name, None), (types.MethodType, types.FunctionType))): # TODO(luke-zhu): Support other callable objects func = getattr(o, name) # Python 3 has no unbound methods return Const(BoundMethod(func, o)) elif isinstance(o, row_type.RowTypeConstraint): return o.get_type_for(name) else: return Any
def build_tuple(state, arg): if arg == 0: state.stack.append(Tuple[()]) else: state.stack[-arg:] = [ Tuple[[Const.unwrap(t) for t in state.stack[-arg:]]] ]
def make_function(state, arg): """Creates a function with the arguments at the top of the stack. """ # TODO(luke-zhu): Handle default argument types globals = state.f.__globals__ # Inherits globals from the current frame func_name = state.stack[-1].value func_code = state.stack[-2].value pop_count = 2 closure = None # arg contains flags, with corresponding stack values if positive. # https://docs.python.org/3.6/library/dis.html#opcode-MAKE_FUNCTION pop_count += bin(arg).count('1') if arg & 0x08: # Convert types in Tuple constraint to a tuple of CPython cells. # https://stackoverflow.com/a/44670295 closure = tuple((lambda _: lambda: _)(t).__closure__[0] for t in state.stack[-3].tuple_types) func = types.FunctionType(func_code, globals, name=func_name, closure=closure) assert pop_count <= len(state.stack) state.stack[-pop_count:] = [Const(func)]
def load_attr(state, arg): """Replaces the top of the stack, TOS, with getattr(TOS, co_names[arg]) """ o = state.stack.pop() name = state.get_name(arg) if isinstance(o, Const) and hasattr(o.value, name): state.stack.append(Const(getattr(o.value, name))) elif (inspect.isclass(o) and isinstance(getattr(o, name, None), (types.MethodType, types.FunctionType))): # TODO(luke-zhu): Support other callable objects if sys.version_info[0] == 2: func = getattr(o, name).__func__ else: func = getattr(o, name) # Python 3 has no unbound methods state.stack.append(Const(BoundMethod(func, o))) else: state.stack.append(Any)
def load_method(state, arg): """Like load_attr. Replaces TOS object with method and TOS.""" o = state.stack.pop() name = state.get_name(arg) if isinstance(o, Const): method = Const(getattr(o.value, name)) elif isinstance(o, typehints.AnyTypeConstraint): method = typehints.Any elif hasattr(o, name): attr = getattr(o, name) if isinstance(attr, _MethodDescriptorType): # Skip builtins since they don't disassemble. method = typehints.Any else: method = Const(BoundMethod(attr, o)) else: method = typehints.Any state.stack.append(method)
def build_const_key_map(state, arg): key_tuple = state.stack.pop() if isinstance(key_tuple, typehints.TupleHint.TupleConstraint): key_types = key_tuple.tuple_types elif isinstance(key_tuple, Const): key_types = [Const(v) for v in key_tuple.value] else: key_types = [Any] state.stack[-arg:] = [ Dict[reduce(union, key_types, Union[()]), reduce(union, state.stack[-arg:], Union[()])] ]
def unpack_sequence(state, arg): t = state.stack.pop() if isinstance(t, Const): try: unpacked = [Const(ti) for ti in t.value] if len(unpacked) != arg: unpacked = [Any] * arg except TypeError: unpacked = [Any] * arg elif (isinstance(t, typehints.TupleHint.TupleConstraint) and len(t.tuple_types) == arg): unpacked = list(t.tuple_types) else: unpacked = [element_type(t)] * arg state.stack += reversed(unpacked)
def binary_subscr(state, unused_arg): index = state.stack.pop() base = Const.unwrap(state.stack.pop()) if base in (str, unicode): out = base elif (isinstance(index, Const) and isinstance(index.value, int) and isinstance(base, typehints.IndexableTypeConstraint)): try: out = base._constraint_for_index(index.value) except IndexError: out = element_type(base) elif index == slice and isinstance(base, typehints.IndexableTypeConstraint): out = base else: out = element_type(base) state.stack.append(out)
def make_function(state, arg): """Creates a function with the arguments at the top of the stack. """ # TODO(luke-zhu): Handle default argument types globals = state.f.__globals__ # Inherits globals from the current frame if sys.version_info[0] == 2: func_code = state.stack[-1].value func = types.FunctionType(func_code, globals) # argc is the number of default parameters. Ignored here. pop_count = 1 + arg else: # Python 3.x func_name = state.stack[-1].value func_code = state.stack[-2].value pop_count = 2 closure = None if sys.version_info[:2] == (3, 5): # https://docs.python.org/3.5/library/dis.html#opcode-MAKE_FUNCTION num_default_pos_args = (arg & 0xff) num_default_kwonly_args = ((arg >> 8) & 0xff) num_annotations = ((arg >> 16) & 0x7fff) pop_count += (num_default_pos_args + 2 * num_default_kwonly_args + num_annotations + num_annotations > 0) elif sys.version_info >= (3, 6): # arg contains flags, with corresponding stack values if positive. # https://docs.python.org/3.6/library/dis.html#opcode-MAKE_FUNCTION pop_count += bin(arg).count('1') if arg & 0x08: # Convert types in Tuple constraint to a tuple of CPython cells. # https://stackoverflow.com/a/44670295 closure = tuple((lambda _: lambda: _)(t).__closure__[0] for t in state.stack[-3].tuple_types) func = types.FunctionType(func_code, globals, name=func_name, closure=closure) assert pop_count <= len(state.stack) state.stack[-pop_count:] = [Const(func)]
def map_add(state, arg): new_key_type = Const.unwrap(state.stack.pop()) new_value_type = Const.unwrap(state.stack.pop()) state.stack[-arg] = Dict[Union[state.stack[-arg].key_type, new_key_type], Union[state.stack[-arg].value_type, new_value_type]]
def list_append(state, arg): new_element_type = Const.unwrap(state.stack.pop()) state.stack[-arg] = List[Union[element_type(state.stack[-arg]), new_element_type]]
def unary(state, unused_arg): state.stack[-1] = Const.unwrap(state.stack[-1])
def set_add(state, arg): new_element_type = Const.unwrap(state.stack.pop()) state.stack[-arg] = Set[union(element_type(state.stack[-arg]), new_element_type)]