# Source code for binaryninja.function

    # Copyright (c) 2015-2017 Vector 35 LLC
    #
    # Permission is hereby granted, free of charge, to any person obtaining a copy
    # of this software and associated documentation files (the "Software"), to
    # deal in the Software without restriction, including without limitation the
    # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    # sell copies of the Software, and to permit persons to whom the Software is
    # furnished to do so, subject to the following conditions:
    #
    # The above copyright notice and this permission notice shall be included in
    # all copies or substantial portions of the Software.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    # IN THE SOFTWARE.

    from __future__ import absolute_import
    import threading
    import traceback
    import ctypes

    # Binary Ninja components
    import binaryninja
    from binaryninja import _binaryninjacore as core
    from binaryninja import associateddatastore  # Required in the main scope due to being an argument for _FunctionAssociatedDataStore
    from binaryninja import highlight
    from binaryninja import log
    from binaryninja import types
    from binaryninja.enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType,
    	HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend,
    	DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, VariableSourceType,
    	FunctionAnalysisSkipOverride)

    # 2-3 compatibility
    from binaryninja import range

    class LookupTableEntry(object):
    	def __init__(self, from_values, to_value):
    		self.from_values = from_values
    		self.to_value = to_value

    	def __repr__(self):
    		return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value)

    class RegisterValue(object):
    	def __init__(self, arch = None, value = None, confidence = types.max_confidence):
    		self.is_constant = False
    		if value is None:
    			self.type = RegisterValueType.UndeterminedValue
    		else:
    			self.type = RegisterValueType(value.state)
    			if value.state == RegisterValueType.EntryValue:
    				self.arch = arch
    				if arch is not None:
    					self.reg = arch.get_reg_name(value.value)
    				else:
    					self.reg = value.value
    			elif (value.state == RegisterValueType.ConstantValue) or (value.state == RegisterValueType.ConstantPointerValue):
    				self.value = value.value
    				self.is_constant = True
    			elif value.state == RegisterValueType.StackFrameOffset:
    				self.offset = value.value
    			elif value.state == RegisterValueType.ImportedAddressValue:
    				self.value = value.value
    		self.confidence = confidence

    	def __repr__(self):
    		if self.type == RegisterValueType.EntryValue:
    			return "<entry %s>" % self.reg
    		if self.type == RegisterValueType.ConstantValue:
    			return "<const %#x>" % self.value
    		if self.type == RegisterValueType.ConstantPointerValue:
    			return "<const ptr %#x>" % self.value
    		if self.type == RegisterValueType.StackFrameOffset:
    			return "<stack frame offset %#x>" % self.offset
    		if self.type == RegisterValueType.ReturnAddressValue:
    			return "<return address>"
    		if self.type == RegisterValueType.ImportedAddressValue:
    			return "<imported address from entry %#x>" % self.value
    		return "<undetermined>"

    	def _to_api_object(self):
    		result = core.BNRegisterValue()
    		result.state = self.type
    		result.value = 0
    		if self.type == RegisterValueType.EntryValue:
    			if self.arch is not None:
    				result.value = self.arch.get_reg_index(self.reg)
    			else:
    				result.value = self.reg
    		elif (self.type == RegisterValueType.ConstantValue) or (self.type == RegisterValueType.ConstantPointerValue):
    			result.value = self.value
    		elif self.type == RegisterValueType.StackFrameOffset:
    			result.value = self.offset
    		elif self.type == RegisterValueType.ImportedAddressValue:
    			result.value = self.value
    		return result

    	@classmethod
    	def undetermined(self):
    		return RegisterValue()

    	@classmethod
    	def entry_value(self, arch, reg):
    		result = RegisterValue()
    		result.type = RegisterValueType.EntryValue
    		result.arch = arch
    		result.reg = reg
    		return result

    	@classmethod
    	def constant(self, value):
    		result = RegisterValue()
    		result.type = RegisterValueType.ConstantValue
    		result.value = value
    		result.is_constant = True
    		return result

    	@classmethod
    	def constant_ptr(self, value):
    		result = RegisterValue()
    		result.type = RegisterValueType.ConstantPointerValue
    		result.value = value
    		result.is_constant = True
    		return result

    	@classmethod
    	def stack_frame_offset(self, offset):
    		result = RegisterValue()
    		result.type = RegisterValueType.StackFrameOffset
    		result.offset = offset
    		return result

    	@classmethod
    	def imported_address(self, value):
    		result = RegisterValue()
    		result.type = RegisterValueType.ImportedAddressValue
    		result.value = value
    		return result

    	@classmethod
    	def return_address(self):
    		result = RegisterValue()
    		result.type = RegisterValueType.ReturnAddressValue
    		return result

    class ValueRange(object):
    	def __init__(self, start, end, step):
    		self.start = start
    		self.end = end
    		self.step = step

    	def __repr__(self):
    		if self.step == 1:
    			return "<range: %#x to %#x>" % (self.start, self.end)
    		return "<range: %#x to %#x, step %#x>" % (self.start, self.end, self.step)

    class PossibleValueSet(object):
    	def __init__(self, arch, value):
    		self.type = RegisterValueType(value.state)
    		if value.state == RegisterValueType.EntryValue:
    			self.reg = arch.get_reg_name(value.value)
    		elif value.state == RegisterValueType.ConstantValue:
    			self.value = value.value
    		elif value.state == RegisterValueType.ConstantPointerValue:
    			self.value = value.value
    		elif value.state == RegisterValueType.StackFrameOffset:
    			self.offset = value.value
    		elif value.state == RegisterValueType.SignedRangeValue:
    			self.offset = value.value
    			self.ranges = []
    			for i in range(0, value.count):
    				start = value.ranges[i].start
    				end = value.ranges[i].end
    				step = value.ranges[i].step
    				if start & (1 << 63):
    					start |= ~((1 << 63) - 1)
    				if end & (1 << 63):
    					end |= ~((1 << 63) - 1)
    				self.ranges.append(ValueRange(start, end, step))
    		elif value.state == RegisterValueType.UnsignedRangeValue:
    			self.offset = value.value
    			self.ranges = []
    			for i in range(0, value.count):
    				start = value.ranges[i].start
    				end = value.ranges[i].end
    				step = value.ranges[i].step
    				self.ranges.append(ValueRange(start, end, step))
    		elif value.state == RegisterValueType.LookupTableValue:
    			self.table = []
    			self.mapping = {}
    			for i in range(0, value.count):
    				from_list = []
    				for j in range(0, value.table[i].fromCount):
    					from_list.append(value.table[i].fromValues[j])
    					self.mapping[value.table[i].fromValues[j]] = value.table[i].toValue
    				self.table.append(LookupTableEntry(from_list, value.table[i].toValue))
    		elif (value.state == RegisterValueType.InSetOfValues) or (value.state == RegisterValueType.NotInSetOfValues):
    			self.values = set()
    			for i in range(0, value.count):
    				self.values.add(value.valueSet[i])

    	def __repr__(self):
    		if self.type == RegisterValueType.EntryValue:
    			return "<entry %s>" % self.reg
    		if self.type == RegisterValueType.ConstantValue:
    			return "<const %#x>" % self.value
    		if self.type == RegisterValueType.ConstantPointerValue:
    			return "<const ptr %#x>" % self.value
    		if self.type == RegisterValueType.StackFrameOffset:
    			return "<stack frame offset %#x>" % self.offset
    		if self.type == RegisterValueType.SignedRangeValue:
    			return "<signed ranges: %s>" % repr(self.ranges)
    		if self.type == RegisterValueType.UnsignedRangeValue:
    			return "<unsigned ranges: %s>" % repr(self.ranges)
    		if self.type == RegisterValueType.LookupTableValue:
    			return "<table: %s>" % ', '.join([repr(i) for i in self.table])
    		if self.type == RegisterValueType.InSetOfValues:
    			return "<in set(%s)>" % '[{}]'.format(', '.join(hex(i) for i in self.values))
    		if self.type == RegisterValueType.NotInSetOfValues:
    			return "<not in set(%s)>" % '[{}]'.format(', '.join(hex(i) for i in self.values))
    		if self.type == RegisterValueType.ReturnAddressValue:
    			return "<return address>"
    		return "<undetermined>"

    class StackVariableReference(object):
    	def __init__(self, src_operand, t, name, var, ref_ofs, size):
    		self.source_operand = src_operand
    		self.type = t
    		self.name = name
    		self.var = var
    		self.referenced_offset = ref_ofs
    		self.size = size
    		if self.source_operand == 0xffffffff:
    			self.source_operand = None

    	def __repr__(self):
    		if self.source_operand is None:
    			if self.referenced_offset != self.var.storage:
    				return "<ref to %s%+#x>" % (self.name, self.referenced_offset - self.var.storage)
    			return "<ref to %s>" % self.name
    		if self.referenced_offset != self.var.storage:
    			return "<operand %d ref to %s%+#x>" % (self.source_operand, self.name, self.var.storage)
    		return "<operand %d ref to %s>" % (self.source_operand, self.name)

    class Variable(object):
    	def __init__(self, func, source_type, index, storage, name = None, var_type = None):
    		self.function = func
    		self.source_type = VariableSourceType(source_type)
    		self.index = index
    		self.storage = storage

    		var = core.BNVariable()
    		var.type = source_type
    		var.index = index
    		var.storage = storage
    		self.identifier = core.BNToVariableIdentifier(var)

    		if func is not None:
    			if name is None:
    				name = core.BNGetVariableName(func.handle, var)
    			if var_type is None:
    				var_type_conf = core.BNGetVariableType(func.handle, var)
    				if var_type_conf.type:
    					var_type = types.Type(var_type_conf.type, platform = func.platform, confidence = var_type_conf.confidence)
    				else:
    					var_type = None

    		self.name = name
    		self.type = var_type

    	@classmethod
    	def from_identifier(self, func, identifier, name = None, var_type = None):
    		var = core.BNFromVariableIdentifier(identifier)
    		return Variable(func, VariableSourceType(var.type), var.index, var.storage, name, var_type)

    	def __repr__(self):
    		if self.type is None:
    			return "<var %s>" % self.name
    		return "<var %s %s%s>" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name())

    	def __str__(self):
    		return self.name

    	def __eq__(self, other):
    		return (self.identifier, self.function) == (other.identifier, other.function)

    	def __hash__(self):
    		return hash((self.identifier, self.function))

    class ConstantReference(object):
    	def __init__(self, val, size, ptr, intermediate):
    		self.value = val
    		self.size = size
    		self.pointer = ptr
    		self.intermediate = intermediate

    	def __repr__(self):
    		if self.pointer:
    			return "<constant pointer %#x>" % self.value
    		if self.size == 0:
    			return "<constant %#x>" % self.value
    		return "<constant %#x size %d>" % (self.value, self.size)

    class IndirectBranchInfo(object):
    	def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined):
    		self.source_arch = source_arch
    		self.source_addr = source_addr
    		self.dest_arch = dest_arch
    		self.dest_addr = dest_addr
    		self.auto_defined = auto_defined

    	def __repr__(self):
    		return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr)

    class ParameterVariables(object):
    	def __init__(self, var_list, confidence = types.max_confidence):
    		self.vars = var_list
    		self.confidence = confidence

    	def __repr__(self):
    		return repr(self.vars)

    	def __iter__(self):
    		for var in self.vars:
    			yield var

    	def __getitem__(self, idx):
    		return self.vars[idx]

    	def __len__(self):
    		return len(self.vars)

    	def with_confidence(self, confidence):
    		return ParameterVariables(list(self.vars), confidence = confidence)

    class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore):
    	_defaults = {}

    class Function(object):
    	_associated_data = {}

    	def __init__(self, view, handle):
    		self._view = view
    		self.handle = core.handle_of_type(handle, core.BNFunction)
    		self._advanced_analysis_requests = 0
    		self._arch = None
    		self._platform = None

    	def __del__(self):
    		if self._advanced_analysis_requests > 0:
    			core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests)
    		core.BNFreeFunction(self.handle)

    	def __eq__(self, value):
    		if not isinstance(value, Function):
    			return False
    		return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)

    	def __ne__(self, value):
    		if not isinstance(value, Function):
    			return True
    		return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)

    	def __hash__(self):
    		return hash((self.start, self.arch.name, self.platform.name))

    	@classmethod
    	def _unregister(cls, func):
    		handle = ctypes.cast(func, ctypes.c_void_p)
    		if handle.value in cls._associated_data:
    			del cls._associated_data[handle.value]

    	@classmethod
    	def set_default_session_data(cls, name, value):
    		_FunctionAssociatedDataStore.set_default(name, value)

    	@property
    	def name(self):
    		"""Symbol name for the function"""
    		return self.symbol.name

    	@name.setter
    	def name(self, value):
    		if value is None:
    			if self.symbol is not None:
    				self.view.undefine_user_symbol(self.symbol)
    		else:
    			symbol = types.Symbol(SymbolType.FunctionSymbol, self.start, value)
    			self.view.define_user_symbol(symbol)

    	@property
    	def view(self):
    		"""Function view (read-only)"""
    		return self._view

    	@property
    	def arch(self):
    		"""Function architecture (read-only)"""
    		if self._arch:
    			return self._arch
    		else:
    			arch = core.BNGetFunctionArchitecture(self.handle)
    			if arch is None:
    				return None
    			self._arch = binaryninja.architecture.CoreArchitecture._from_cache(arch)
    			return self._arch

    	@property
    	def platform(self):
    		"""Function platform (read-only)"""
    		if self._platform:
    			return self._platform
    		else:
    			plat = core.BNGetFunctionPlatform(self.handle)
    			if plat is None:
    				return None
    			self._platform = binaryninja.platform.Platform(None, handle = plat)
    			return self._platform

    	@property
    	def start(self):
    		"""Function start (read-only)"""
    		return core.BNGetFunctionStart(self.handle)

    	@property
    	def symbol(self):
    		"""Function symbol(read-only)"""
    		sym = core.BNGetFunctionSymbol(self.handle)
    		if sym is None:
    			return None
    		return types.Symbol(None, None, None, handle = sym)

    	@property
    	def auto(self):
    		"""Whether function was automatically discovered (read-only)"""
    		return core.BNWasFunctionAutomaticallyDiscovered(self.handle)

    	@property
    	def can_return(self):
    		"""Whether function can return"""
    		result = core.BNCanFunctionReturn(self.handle)
    		return types.BoolWithConfidence(result.value, confidence = result.confidence)

    	@can_return.setter
    	def can_return(self, value):
    		bc = core.BNBoolWithConfidence()
    		bc.value = bool(value)
    		if hasattr(value, 'confidence'):
    			bc.confidence = value.confidence
    		else:
    			bc.confidence = types.max_confidence
    		core.BNSetUserFunctionCanReturn(self.handle, bc)

    	@property
    	def explicitly_defined_type(self):
    		"""Whether function has explicitly defined types (read-only)"""
    		return core.BNFunctionHasExplicitlyDefinedType(self.handle)

    	@property
    	def needs_update(self):
    		"""Whether the function has analysis that needs to be updated (read-only)"""
    		return core.BNIsFunctionUpdateNeeded(self.handle)

    	@property
    	def basic_blocks(self):
    		"""List of basic blocks (read-only)"""
    		count = ctypes.c_ulonglong()
    		blocks = core.BNGetFunctionBasicBlockList(self.handle, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(binaryninja.basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i])))
    		core.BNFreeBasicBlockList(blocks, count.value)
    		return result

    	@property
    	def comments(self):
    		"""Dict of comments (read-only)"""
    		count = ctypes.c_ulonglong()
    		addrs = core.BNGetCommentedAddresses(self.handle, count)
    		result = {}
    		for i in range(0, count.value):
    			result[addrs[i]] = self.get_comment_at(addrs[i])
    		core.BNFreeAddressList(addrs)
    		return result

    	@property
    	def low_level_il(self):
    		"""returns LowLevelILFunction used to represent Function low level IL (read-only)"""
    		return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self)

    	@property
    	def lifted_il(self):
    		"""returns LowLevelILFunction used to represent lifted IL (read-only)"""
    		return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self)

    	@property
    	def medium_level_il(self):
    		"""Function medium level IL (read-only)"""
    		return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self)

    	@property
    	def function_type(self):
    		"""Function type object"""
    		return types.Type(core.BNGetFunctionType(self.handle), platform = self.platform)

    	@function_type.setter
    	def function_type(self, value):
    		self.set_user_type(value)

    	@property
    	def stack_layout(self):
    		"""List of function stack variables (read-only)"""
    		count = ctypes.c_ulonglong()
    		v = core.BNGetStackLayout(self.handle, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name,
    				types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence)))
    		result.sort(key = lambda x: x.identifier)
    		core.BNFreeVariableNameAndTypeList(v, count.value)
    		return result

    	@property
    	def vars(self):
    		"""List of function variables (read-only)"""
    		count = ctypes.c_ulonglong()
    		v = core.BNGetFunctionVariables(self.handle, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name,
    				types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence)))
    		result.sort(key = lambda x: x.identifier)
    		core.BNFreeVariableNameAndTypeList(v, count.value)
    		return result

    	@property
    	def indirect_branches(self):
    		"""List of indirect branches (read-only)"""
    		count = ctypes.c_ulonglong()
    		branches = core.BNGetIndirectBranches(self.handle, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(IndirectBranchInfo(binaryninja.architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, binaryninja.architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined))
    		core.BNFreeIndirectBranchList(branches)
    		return result

    	@property
    	def session_data(self):
    		"""Dictionary object where plugins can store arbitrary data associated with the function"""
    		handle = ctypes.cast(self.handle, ctypes.c_void_p)
    		if handle.value not in Function._associated_data:
    			obj = _FunctionAssociatedDataStore()
    			Function._associated_data[handle.value] = obj
    			return obj
    		else:
    			return Function._associated_data[handle.value]

    	@property
    	def analysis_performance_info(self):
    		count = ctypes.c_ulonglong()
    		info = core.BNGetFunctionAnalysisPerformanceInfo(self.handle, count)
    		result = {}
    		for i in range(0, count.value):
    			result[info[i].name] = info[i].seconds
    		core.BNFreeAnalysisPerformanceInfo(info, count.value)
    		return result

    	@property
    	def type_tokens(self):
    		"""Text tokens for this function's prototype"""
    		return self.get_type_tokens()[0].tokens

    	@property
    	def return_type(self):
    		"""Return type of the function"""
    		result = core.BNGetFunctionReturnType(self.handle)
    		if not result.type:
    			return None
    		return types.Type(result.type, platform = self.platform, confidence = result.confidence)

    	@return_type.setter
    	def return_type(self, value):
    		type_conf = core.BNTypeWithConfidence()
    		if value is None:
    			type_conf.type = None
    			type_conf.confidence = 0
    		else:
    			type_conf.type = value.handle
    			type_conf.confidence = value.confidence
    		core.BNSetUserFunctionReturnType(self.handle, type_conf)

    	@property
    	def return_regs(self):
    		"""Registers that are used for the return value"""
    		result = core.BNGetFunctionReturnRegisters(self.handle)
    		reg_set = []
    		for i in range(0, result.count):
    			reg_set.append(self.arch.get_reg_name(result.regs[i]))
    		regs = types.RegisterSet(reg_set, confidence = result.confidence)
    		core.BNFreeRegisterSet(result)
    		return regs

    	@return_regs.setter
    	def return_regs(self, value):
    		regs = core.BNRegisterSetWithConfidence()
    		regs.regs = (ctypes.c_uint * len(value))()
    		regs.count = len(value)
    		for i in range(0, len(value)):
    			regs.regs[i] = self.arch.get_reg_index(value[i])
    		if hasattr(value, 'confidence'):
    			regs.confidence = value.confidence
    		else:
    			regs.confidence = types.max_confidence
    		core.BNSetUserFunctionReturnRegisters(self.handle, regs)

    	@property
    	def calling_convention(self):
    		"""Calling convention used by the function"""
    		result = core.BNGetFunctionCallingConvention(self.handle)
    		if not result.convention:
    			return None
    		return binaryninja.callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence)

    	@calling_convention.setter
    	def calling_convention(self, value):
    		conv_conf = core.BNCallingConventionWithConfidence()
    		if value is None:
    			conv_conf.convention = None
    			conv_conf.confidence = 0
    		else:
    			conv_conf.convention = value.handle
    			conv_conf.confidence = value.confidence
    		core.BNSetUserFunctionCallingConvention(self.handle, conv_conf)

    	@property
    	def parameter_vars(self):
    		"""List of variables for the incoming function parameters"""
    		result = core.BNGetFunctionParameterVariables(self.handle)
    		var_list = []
    		for i in range(0, result.count):
    			var_list.append(Variable(self, result.vars[i].type, result.vars[i].index, result.vars[i].storage))
    		confidence = result.confidence
    		core.BNFreeParameterVariables(result)
    		return ParameterVariables(var_list, confidence = confidence)

    	@parameter_vars.setter
    	def parameter_vars(self, value):
    		if value is None:
    			var_list = []
    		else:
    			var_list = list(value)
    		var_conf = core.BNParameterVariablesWithConfidence()
    		var_conf.vars = (core.BNVariable * len(var_list))()
    		var_conf.count = len(var_list)
    		for i in range(0, len(var_list)):
    			var_conf.vars[i].type = var_list[i].source_type
    			var_conf.vars[i].index = var_list[i].index
    			var_conf.vars[i].storage = var_list[i].storage
    		if value is None:
    			var_conf.confidence = 0
    		elif hasattr(value, 'confidence'):
    			var_conf.confidence = value.confidence
    		else:
    			var_conf.confidence = types.max_confidence
    		core.BNSetUserFunctionParameterVariables(self.handle, var_conf)

    	@property
    	def has_variable_arguments(self):
    		"""Whether the function takes a variable number of arguments"""
    		result = core.BNFunctionHasVariableArguments(self.handle)
    		return types.BoolWithConfidence(result.value, confidence = result.confidence)

    	@has_variable_arguments.setter
    	def has_variable_arguments(self, value):
    		bc = core.BNBoolWithConfidence()
    		bc.value = bool(value)
    		if hasattr(value, 'confidence'):
    			bc.confidence = value.confidence
    		else:
    			bc.confidence = types.max_confidence
    		core.BNSetUserFunctionHasVariableArguments(self.handle, bc)

    	@property
    	def stack_adjustment(self):
    		"""Number of bytes removed from the stack after return"""
    		result = core.BNGetFunctionStackAdjustment(self.handle)
    		return types.SizeWithConfidence(result.value, confidence = result.confidence)

    	@stack_adjustment.setter
    	def stack_adjustment(self, value):
    		sc = core.BNSizeWithConfidence()
    		sc.value = int(value)
    		if hasattr(value, 'confidence'):
    			sc.confidence = value.confidence
    		else:
    			sc.confidence = types.max_confidence
    		core.BNSetUserFunctionStackAdjustment(self.handle, sc)

    	@property
    	def reg_stack_adjustments(self):
    		"""Number of entries removed from each register stack after return"""
    		count = ctypes.c_ulonglong()
    		adjust = core.BNGetFunctionRegisterStackAdjustments(self.handle, count)
    		result = {}
    		for i in range(0, count.value):
    			name = self.arch.get_reg_stack_name(adjust[i].regStack)
    			value = types.RegisterStackAdjustmentWithConfidence(adjust[i].adjustment,
    				confidence = adjust[i].confidence)
    			result[name] = value
    		core.BNFreeRegisterStackAdjustments(adjust)
    		return result

    	@reg_stack_adjustments.setter
    	def reg_stack_adjustments(self, value):
    		adjust = (core.BNRegisterStackAdjustment * len(value))()
    		i = 0
    		for reg_stack in value.keys():
    			adjust[i].regStack = self.arch.get_reg_stack_index(reg_stack)
    			if isinstance(value[reg_stack], types.RegisterStackAdjustmentWithConfidence):
    				adjust[i].adjustment = value[reg_stack].value
    				adjust[i].confidence = value[reg_stack].confidence
    			else:
    				adjust[i].adjustment = value[reg_stack]
    				adjust[i].confidence = types.max_confidence
    			i += 1
    		core.BNSetUserFunctionRegisterStackAdjustments(self.handle, adjust, len(value))

    	@property
    	def clobbered_regs(self):
    		"""Registers that are modified by this function"""
    		result = core.BNGetFunctionClobberedRegisters(self.handle)
    		reg_set = []
    		for i in range(0, result.count):
    			reg_set.append(self.arch.get_reg_name(result.regs[i]))
    		regs = types.RegisterSet(reg_set, confidence = result.confidence)
    		core.BNFreeRegisterSet(result)
    		return regs

    	@clobbered_regs.setter
    	def clobbered_regs(self, value):
    		regs = core.BNRegisterSetWithConfidence()
    		regs.regs = (ctypes.c_uint * len(value))()
    		regs.count = len(value)
    		for i in range(0, len(value)):
    			regs.regs[i] = self.arch.get_reg_index(value[i])
    		if hasattr(value, 'confidence'):
    			regs.confidence = value.confidence
    		else:
    			regs.confidence = types.max_confidence
    		core.BNSetUserFunctionClobberedRegisters(self.handle, regs)

    	@property
    	def global_pointer_value(self):
    		"""Discovered value of the global pointer register, if the function uses one (read-only)"""
    		result = core.BNGetFunctionGlobalPointerValue(self.handle)
    		return RegisterValue(self.arch, result.value, confidence = result.confidence)

    	@property
    	def comment(self):
    		"""Gets the comment for the current function"""
    		return core.BNGetFunctionComment(self.handle)

    	@comment.setter
    	def comment(self, comment):
    		"""Sets a comment for the current function"""
    		return core.BNSetFunctionComment(self.handle, comment)

    	@property
    	def llil_basic_blocks(self):
    		"""A generator of all LowLevelILBasicBlock objects in the current function"""
    		for block in self.low_level_il:
    			yield block

    	@property
    	def mlil_basic_blocks(self):
    		"""A generator of all MediumLevelILBasicBlock objects in the current function"""
    		for block in self.medium_level_il:
    			yield block

    	@property
    	def instructions(self):
    		"""A generator of instruction tokens and their start addresses for the current function"""
    		for block in self.basic_blocks:
    			start = block.start
    			for i in block:
    				yield (i[0], start)
    				start += i[1]

    	@property
    	def llil_instructions(self):
    		"""A generator of llil instructions of the current function"""
    		for block in self.llil_basic_blocks:
    			for i in block:
    				yield i

    	@property
    	def mlil_instructions(self):
    		"""A generator of mlil instructions of the current function"""
    		for block in self.mlil_basic_blocks:
    			for i in block:
    				yield i

    	@property
    	def too_large(self):
    		"""Whether the function is too large to automatically perform analysis (read-only)"""
    		return core.BNIsFunctionTooLarge(self.handle)

    	@property
    	def analysis_skipped(self):
    		"""Whether automatic analysis was skipped for this function"""
    		return core.BNIsFunctionAnalysisSkipped(self.handle)

    	@property
    	def analysis_skip_reason(self):
    		"""Function analysis skip reason"""
    		return AnalysisSkipReason(core.BNGetAnalysisSkipReason(self.handle))

    	@analysis_skipped.setter
    	def analysis_skipped(self, skip):
    		if skip:
    			core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.AlwaysSkipFunctionAnalysis)
    		else:
    			core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.NeverSkipFunctionAnalysis)

    	@property
    	def analysis_skip_override(self):
    		"""Override for skipping of automatic analysis"""
    		return FunctionAnalysisSkipOverride(core.BNGetFunctionAnalysisSkipOverride(self.handle))

    	@analysis_skip_override.setter
    	def analysis_skip_override(self, override):
    		core.BNSetFunctionAnalysisSkipOverride(self.handle, override)

    	def __iter__(self):
    		count = ctypes.c_ulonglong()
    		blocks = core.BNGetFunctionBasicBlockList(self.handle, count)
    		try:
    			for i in range(0, count.value):
    				yield binaryninja.basicblock.BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))
    		finally:
    			core.BNFreeBasicBlockList(blocks, count.value)

    	def __setattr__(self, name, value):
    		try:
    			object.__setattr__(self, name, value)
    		except AttributeError:
    			raise AttributeError("attribute '%s' is read only" % name)

    	def __repr__(self):
    		arch = self.arch
    		if arch:
    			return "<func: %s@%#x>" % (arch.name, self.start)
    		else:
    			return "<func: %#x>" % self.start

    	def mark_recent_use(self):
    		core.BNMarkFunctionAsRecentlyUsed(self.handle)

    	def get_comment_at(self, addr):
    		return core.BNGetCommentForAddress(self.handle, addr)

    	def set_comment(self, addr, comment):
    		"""Deprecated use set_comment_at instead"""
    		core.BNSetCommentForAddress(self.handle, addr, comment)

    	def set_comment_at(self, addr, comment):
    		"""
    		``set_comment_at`` sets a comment for the current function at the address specified

    		:param addr int: virtual address within the current function to apply the comment to
    		:param comment str: string comment to apply
    		:rtype: None
    		:Example:

    			>>> current_function.set_comment_at(here, "hi")

    		"""
    		core.BNSetCommentForAddress(self.handle, addr, comment)

    	def get_low_level_il_at(self, addr, arch=None):
    		"""
    		``get_low_level_il_at`` gets the LowLevelILInstruction corresponding to the given virtual address

    		:param int addr: virtual address of the function to be queried
    		:param Architecture arch: (optional) Architecture for the given function
    		:rtype: LowLevelILInstruction
    		:Example:

    			>>> func = bv.functions[0]
    			>>> func.get_low_level_il_at(func.start)
    			<il: push(rbp)>
    		"""
    		if arch is None:
    			arch = self.arch

    		idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr)

    		if idx == len(self.low_level_il):
    			return None

    		return self.low_level_il[idx]

    	def get_low_level_il_exits_at(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(exits[i])
    		core.BNFreeILInstructionList(exits)
    		return result

    	def get_reg_value_at(self, addr, reg, arch=None):
    		"""
    		``get_reg_value_at`` gets the value the provided string register address corresponding to the given virtual address

    		:param int addr: virtual address of the instruction to query
    		:param str reg: string value of native register to query
    		:param Architecture arch: (optional) Architecture for the given function
    		:rtype: binaryninja.function.RegisterValue
    		:Example:

    			>>> func.get_reg_value_at(0x400dbe, 'rdi')
    			<const 0x2>
    		"""
    		if arch is None:
    			arch = self.arch
    		reg = arch.get_reg_index(reg)
    		value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg)
    		result = RegisterValue(arch, value)
    		return result

    	def get_reg_value_after(self, addr, reg, arch=None):
    		"""
    		``get_reg_value_after`` gets the value instruction address corresponding to the given virtual address

    		:param int addr: virtual address of the instruction to query
    		:param str reg: string value of native register to query
    		:param Architecture arch: (optional) Architecture for the given function
    		:rtype: binaryninja.function.RegisterValue
    		:Example:

    			>>> func.get_reg_value_after(0x400dbe, 'rdi')
    			<undetermined>
    		"""
    		if arch is None:
    			arch = self.arch
    		reg = arch.get_reg_index(reg)
    		value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg)
    		result = RegisterValue(arch, value)
    		return result

    	def get_stack_contents_at(self, addr, offset, size, arch=None):
    		"""
    		``get_stack_contents_at`` returns the RegisterValue for the item on the stack in the current function at the
    		given virtual address ``addr``, stack offset ``offset`` and size of ``size``. Optionally specifying the architecture.

    		:param int addr: virtual address of the instruction to query
    		:param int offset: stack offset base of stack
    		:param int size: size of memory to query
    		:param Architecture arch: (optional) Architecture for the given function
    		:rtype: binaryninja.function.RegisterValue

    		.. note:: Stack base is zero on entry into the function unless the architecture places the return address on the
    		stack as in (x86/x86_64) where the stack base will start at address_size

    		:Example:

    			>>> func.get_stack_contents_at(0x400fad, -16, 4)
    			<range: 0x8 to 0xffffffff>
    		"""
    		if arch is None:
    			arch = self.arch
    		value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size)
    		result = RegisterValue(arch, value)
    		return result

    	def get_stack_contents_after(self, addr, offset, size, arch=None):
    		if arch is None:
    			arch = self.arch
    		value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size)
    		result = RegisterValue(arch, value)
    		return result

    	def get_parameter_at(self, addr, func_type, i, arch=None):
    		if arch is None:
    			arch = self.arch
    		if func_type is not None:
    			func_type = func_type.handle
    		value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i)
    		result = RegisterValue(arch, value)
    		return result

    	def get_parameter_at_low_level_il_instruction(self, instr, func_type, i):
    		if func_type is not None:
    			func_type = func_type.handle
    		value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i)
    		result = RegisterValue(self.arch, value)
    		return result

    	def get_regs_read_by(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(arch.get_reg_name(regs[i]))
    		core.BNFreeRegisterList(regs)
    		return result

    	def get_regs_written_by(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(arch.get_reg_name(regs[i]))
    		core.BNFreeRegisterList(regs)
    		return result

    	def get_stack_vars_referenced_by(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			var_type = types.Type(core.BNNewTypeReference(refs[i].type), platform = self.platform, confidence = refs[i].typeConfidence)
    			result.append(StackVariableReference(refs[i].sourceOperand, var_type,
    				refs[i].name, Variable.from_identifier(self, refs[i].varIdentifier, refs[i].name, var_type),
    				refs[i].referencedOffset, refs[i].size))
    		core.BNFreeStackVariableReferenceList(refs, count.value)
    		return result

    	def get_constants_referenced_by(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(ConstantReference(refs[i].value, refs[i].size, refs[i].pointer, refs[i].intermediate))
    		core.BNFreeConstantReferenceList(refs)
    		return result

    	def get_lifted_il_at(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch

    		idx = core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr)

    		if idx == len(self.lifted_il):
    			return None

    		return self.lifted_il[idx]

    	def get_lifted_il_flag_uses_for_definition(self, i, flag):
    		flag = self.arch.get_flag_index(flag)
    		count = ctypes.c_ulonglong()
    		instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(instrs[i])
    		core.BNFreeILInstructionList(instrs)
    		return result

    	def get_lifted_il_flag_definitions_for_use(self, i, flag):
    		flag = self.arch.get_flag_index(flag)
    		count = ctypes.c_ulonglong()
    		instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(instrs[i])
    		core.BNFreeILInstructionList(instrs)
    		return result

    	def get_flags_read_by_lifted_il_instruction(self, i):
    		count = ctypes.c_ulonglong()
    		flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(self.arch._flags_by_index[flags[i]])
    		core.BNFreeRegisterList(flags)
    		return result

    	def get_flags_written_by_lifted_il_instruction(self, i):
    		count = ctypes.c_ulonglong()
    		flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(self.arch._flags_by_index[flags[i]])
    		core.BNFreeRegisterList(flags)
    		return result

    	def create_graph(self):
    		return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle))

    	def apply_imported_types(self, sym):
    		core.BNApplyImportedTypes(self.handle, sym.handle)

    	def apply_auto_discovered_type(self, func_type):
    		core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle)

    	def set_auto_indirect_branches(self, source, branches, source_arch=None):
    		if source_arch is None:
    			source_arch = self.arch
    		branch_list = (core.BNArchitectureAndAddress * len(branches))()
    		for i in range(len(branches)):
    			branch_list[i].arch = branches[i][0].handle
    			branch_list[i].address = branches[i][1]
    		core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches))

    	def set_user_indirect_branches(self, source, branches, source_arch=None):
    		if source_arch is None:
    			source_arch = self.arch
    		branch_list = (core.BNArchitectureAndAddress * len(branches))()
    		for i in range(len(branches)):
    			branch_list[i].arch = branches[i][0].handle
    			branch_list[i].address = branches[i][1]
    		core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches))

    	def get_indirect_branches_at(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(IndirectBranchInfo(binaryninja.architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, binaryninja.architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined))
    		core.BNFreeIndirectBranchList(branches)
    		return result

    	def get_block_annotations(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong(0)
    		lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count)
    		result = []
    		for i in range(0, count.value):
    			tokens = []
    			for j in range(0, lines[i].count):
    				token_type = InstructionTextTokenType(lines[i].tokens[j].type)
    				text = lines[i].tokens[j].text
    				if not isinstance(text, str):
    					text = text.encode("charmap")
    				value = lines[i].tokens[j].value
    				size = lines[i].tokens[j].size
    				operand = lines[i].tokens[j].operand
    				context = lines[i].tokens[j].context
    				confidence = lines[i].tokens[j].confidence
    				address = lines[i].tokens[j].address
    				tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
    			result.append(tokens)
    		core.BNFreeInstructionTextLines(lines, count.value)
    		return result

    	def set_auto_type(self, value):
    		core.BNSetFunctionAutoType(self.handle, value.handle)

    	def set_user_type(self, value):
    		core.BNSetFunctionUserType(self.handle, value.handle)

    	def set_auto_return_type(self, value):
    		type_conf = core.BNTypeWithConfidence()
    		if value is None:
    			type_conf.type = None
    			type_conf.confidence = 0
    		else:
    			type_conf.type = value.handle
    			type_conf.confidence = value.confidence
    		core.BNSetAutoFunctionReturnType(self.handle, type_conf)

    	def set_auto_return_regs(self, value):
    		regs = core.BNRegisterSetWithConfidence()
    		regs.regs = (ctypes.c_uint * len(value))()
    		regs.count = len(value)
    		for i in range(0, len(value)):
    			regs.regs[i] = self.arch.get_reg_index(value[i])
    		if hasattr(value, 'confidence'):
    			regs.confidence = value.confidence
    		else:
    			regs.confidence = types.max_confidence
    		core.BNSetAutoFunctionReturnRegisters(self.handle, regs)

    	def set_auto_calling_convention(self, value):
    		conv_conf = core.BNCallingConventionWithConfidence()
    		if value is None:
    			conv_conf.convention = None
    			conv_conf.confidence = 0
    		else:
    			conv_conf.convention = value.handle
    			conv_conf.confidence = value.confidence
    		core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf)

    	def set_auto_parameter_vars(self, value):
    		if value is None:
    			var_list = []
    		else:
    			var_list = list(value)
    		var_conf = core.BNParameterVariablesWithConfidence()
    		var_conf.vars = (core.BNVariable * len(var_list))()
    		var_conf.count = len(var_list)
    		for i in range(0, len(var_list)):
    			var_conf.vars[i].type = var_list[i].source_type
    			var_conf.vars[i].index = var_list[i].index
    			var_conf.vars[i].storage = var_list[i].storage
    		if value is None:
    			var_conf.confidence = 0
    		elif hasattr(value, 'confidence'):
    			var_conf.confidence = value.confidence
    		else:
    			var_conf.confidence = types.max_confidence
    		core.BNSetAutoFunctionParameterVariables(self.handle, var_conf)

    	def set_auto_has_variable_arguments(self, value):
    		bc = core.BNBoolWithConfidence()
    		bc.value = bool(value)
    		if hasattr(value, 'confidence'):
    			bc.confidence = value.confidence
    		else:
    			bc.confidence = types.max_confidence
    		core.BNSetAutoFunctionHasVariableArguments(self.handle, bc)

    	def set_auto_can_return(self, value):
    		bc = core.BNBoolWithConfidence()
    		bc.value = bool(value)
    		if hasattr(value, 'confidence'):
    			bc.confidence = value.confidence
    		else:
    			bc.confidence = types.max_confidence
    		core.BNSetAutoFunctionCanReturn(self.handle, bc)

    	def set_auto_stack_adjustment(self, value):
    		sc = core.BNSizeWithConfidence()
    		sc.value = int(value)
    		if hasattr(value, 'confidence'):
    			sc.confidence = value.confidence
    		else:
    			sc.confidence = types.max_confidence
    		core.BNSetAutoFunctionStackAdjustment(self.handle, sc)

    	def set_auto_reg_stack_adjustments(self, value):
    		adjust = (core.BNRegisterStackAdjustment * len(value))()
    		i = 0
    		for reg_stack in value.keys():
    			adjust[i].regStack = self.arch.get_reg_stack_index(reg_stack)
    			if isinstance(value[reg_stack], types.RegisterStackAdjustmentWithConfidence):
    				adjust[i].adjustment = value[reg_stack].value
    				adjust[i].confidence = value[reg_stack].confidence
    			else:
    				adjust[i].adjustment = value[reg_stack]
    				adjust[i].confidence = types.max_confidence
    			i += 1
    		core.BNSetAutoFunctionRegisterStackAdjustments(self.handle, adjust, len(value))

    	def set_auto_clobbered_regs(self, value):
    		regs = core.BNRegisterSetWithConfidence()
    		regs.regs = (ctypes.c_uint * len(value))()
    		regs.count = len(value)
    		for i in range(0, len(value)):
    			regs.regs[i] = self.arch.get_reg_index(value[i])
    		if hasattr(value, 'confidence'):
    			regs.confidence = value.confidence
    		else:
    			regs.confidence = types.max_confidence
    		core.BNSetAutoFunctionClobberedRegisters(self.handle, regs)

    	def get_int_display_type(self, instr_addr, value, operand, arch=None):
    		if arch is None:
    			arch = self.arch
    		return IntegerDisplayType(core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand))

    	def set_int_display_type(self, instr_addr, value, operand, display_type, arch=None):
    		"""

    		:param int instr_addr:
    		:param int value:
    		:param int operand:
    		:param enums.IntegerDisplayType display_type:
    		:param Architecture arch: (optional)
    		"""
    		if arch is None:
    			arch = self.arch
    		if isinstance(display_type, str):
    			display_type = IntegerDisplayType[display_type]
    		core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type)

    	def reanalyze(self):
    		"""
    		``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish.

    		:rtype: None
    		"""
    		core.BNReanalyzeFunction(self.handle)

    	def request_advanced_analysis_data(self):
    		core.BNRequestAdvancedFunctionAnalysisData(self.handle)
    		self._advanced_analysis_requests += 1

    	def release_advanced_analysis_data(self):
    		core.BNReleaseAdvancedFunctionAnalysisData(self.handle)
    		self._advanced_analysis_requests -= 1

    	def get_basic_block_at(self, addr, arch=None):
    		"""
    		``get_basic_block_at`` returns the BasicBlock of the optionally specified Architecture ``arch`` at the given
    		address ``addr``.

    		:param int addr: Address of the BasicBlock to retrieve.
    		:param Architecture arch: (optional) Architecture of the basic block if different from the Function's self.arch
    		:Example:
    			>>> current_function.get_basic_block_at(current_function.start)
    			<block: x86_64@0x100000f30-0x100000f50>
    		"""
    		if arch is None:
    			arch = self.arch
    		block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr)
    		if not block:
    			return None
    		return binaryninja.basicblock.BasicBlock(self._view, handle = block)

    	def get_instr_highlight(self, addr, arch=None):
    		"""
    		:Example:
    			>>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0))
    			>>> current_function.get_instr_highlight(here)
    			<color: #ff00ff>
    		"""
    		if arch is None:
    			arch = self.arch
    		color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr)
    		if color.style == HighlightColorStyle.StandardHighlightColor:
    			return highlight.HighlightColor(color = color.color, alpha = color.alpha)
    		elif color.style == HighlightColorStyle.MixedHighlightColor:
    			return highlight.HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha)
    		elif color.style == HighlightColorStyle.CustomHighlightColor:
    			return highlight.HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha)
    		return highlight.HighlightColor(color = HighlightStandardColor.NoHighlightColor)

    	def set_auto_instr_highlight(self, addr, color, arch=None):
    		"""
    		``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color

    		..warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.

    		:param int addr: virtual address of the instruction to be highlighted
    		:param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting
    		:param Architecture arch: (optional) Architecture of the instruction if different from self.arch
    		"""
    		if arch is None:
    			arch = self.arch
    		if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor):
    			raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor")
    		if isinstance(color, HighlightStandardColor):
    			color = highlight.HighlightColor(color = color)
    		core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct())

    	def set_user_instr_highlight(self, addr, color, arch=None):
    		"""
    		``set_user_instr_highlight`` highlights the instruction at the specified address with the supplied color

    		:param int addr: virtual address of the instruction to be highlighted
    		:param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting
    		:param Architecture arch: (optional) Architecture of the instruction if different from self.arch
    		:Example:

    			>>> current_function.set_user_instr_highlight(here, HighlightStandardColor.BlueHighlightColor)
    			>>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0))
    		"""
    		if arch is None:
    			arch = self.arch
    		if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor):
    			raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor")
    		if isinstance(color, HighlightStandardColor):
    			color = highlight.HighlightColor(color)
    		core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct())

    	def create_auto_stack_var(self, offset, var_type, name):
    		tc = core.BNTypeWithConfidence()
    		tc.type = var_type.handle
    		tc.confidence = var_type.confidence
    		core.BNCreateAutoStackVariable(self.handle, offset, tc, name)

    	def create_user_stack_var(self, offset, var_type, name):
    		tc = core.BNTypeWithConfidence()
    		tc.type = var_type.handle
    		tc.confidence = var_type.confidence
    		core.BNCreateUserStackVariable(self.handle, offset, tc, name)

    	def delete_auto_stack_var(self, offset):
    		core.BNDeleteAutoStackVariable(self.handle, offset)

    	def delete_user_stack_var(self, offset):
    		core.BNDeleteUserStackVariable(self.handle, offset)

    	def create_auto_var(self, var, var_type, name, ignore_disjoint_uses = False):
    		var_data = core.BNVariable()
    		var_data.type = var.source_type
    		var_data.index = var.index
    		var_data.storage = var.storage
    		tc = core.BNTypeWithConfidence()
    		tc.type = var_type.handle
    		tc.confidence = var_type.confidence
    		core.BNCreateAutoVariable(self.handle, var_data, tc, name, ignore_disjoint_uses)

    	def create_user_var(self, var, var_type, name, ignore_disjoint_uses = False):
    		var_data = core.BNVariable()
    		var_data.type = var.source_type
    		var_data.index = var.index
    		var_data.storage = var.storage
    		tc = core.BNTypeWithConfidence()
    		tc.type = var_type.handle
    		tc.confidence = var_type.confidence
    		core.BNCreateUserVariable(self.handle, var_data, tc, name, ignore_disjoint_uses)

    	def delete_auto_var(self, var):
    		var_data = core.BNVariable()
    		var_data.type = var.source_type
    		var_data.index = var.index
    		var_data.storage = var.storage
    		core.BNDeleteAutoVariable(self.handle, var_data)

    	def delete_user_var(self, var):
    		var_data = core.BNVariable()
    		var_data.type = var.source_type
    		var_data.index = var.index
    		var_data.storage = var.storage
    		core.BNDeleteUserVariable(self.handle, var_data)

    	def get_stack_var_at_frame_offset(self, offset, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		found_var = core.BNVariableNameAndType()
    		if not core.BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, found_var):
    			return None
    		result = Variable(self, found_var.var.type, found_var.var.index, found_var.var.storage,
    			found_var.name, types.Type(handle = core.BNNewTypeReference(found_var.type), platform = self.platform,
    			confidence = found_var.typeConfidence))
    		core.BNFreeVariableNameAndType(found_var)
    		return result

    	def get_type_tokens(self, settings=None):
    		if settings is not None:
    			settings = settings.handle
    		count = ctypes.c_ulonglong()
    		lines = core.BNGetFunctionTypeTokens(self.handle, settings, count)
    		result = []
    		for i in range(0, count.value):
    			addr = lines[i].addr
    			tokens = []
    			for j in range(0, lines[i].count):
    				token_type = InstructionTextTokenType(lines[i].tokens[j].type)
    				text = lines[i].tokens[j].text
    				value = lines[i].tokens[j].value
    				size = lines[i].tokens[j].size
    				operand = lines[i].tokens[j].operand
    				context = lines[i].tokens[j].context
    				confidence = lines[i].tokens[j].confidence
    				address = lines[i].tokens[j].address
    				tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
    			result.append(DisassemblyTextLine(addr, tokens))
    		core.BNFreeDisassemblyTextLines(lines, count.value)
    		return result

    	def get_reg_value_at_exit(self, reg):
    		result = core.BNGetFunctionRegisterValueAtExit(self.handle, self.arch.get_reg_index(reg))
    		return RegisterValue(self.arch, result.value, confidence = result.confidence)

    	def set_auto_call_stack_adjustment(self, addr, adjust, arch=None):
    		if arch is None:
    			arch = self.arch
    		if not isinstance(adjust, types.SizeWithConfidence):
    			adjust = types.SizeWithConfidence(adjust)
    		core.BNSetAutoCallStackAdjustment(self.handle, arch.handle, addr, adjust.value, adjust.confidence)

    	def set_auto_call_reg_stack_adjustment(self, addr, adjust, arch=None):
    		if arch is None:
    			arch = self.arch
    		adjust_buf = (core.BNRegisterStackAdjustment * len(adjust))()
    		i = 0
    		for reg_stack in adjust.keys():
    			adjust_buf[i].regStack = arch.get_reg_stack_index(reg_stack)
    			value = adjust[reg_stack]
    			if not isinstance(value, types.RegisterStackAdjustmentWithConfidence):
    				value = types.RegisterStackAdjustmentWithConfidence(value)
    			adjust_buf[i].adjustment = value.value
    			adjust_buf[i].confidence = value.confidence
    			i += 1
    		core.BNSetAutoCallRegisterStackAdjustment(self.handle, arch.handle, addr, adjust_buf, len(adjust))

    	def set_auto_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, adjust, arch=None):
    		if arch is None:
    			arch = self.arch
    		reg_stack = arch.get_reg_stack_index(reg_stack)
    		if not isinstance(adjust, types.RegisterStackAdjustmentWithConfidence):
    			adjust = types.RegisterStackAdjustmentWithConfidence(adjust)
    		core.BNSetAutoCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack,
    			adjust.value, adjust.confidence)

    	def set_call_stack_adjustment(self, addr, adjust, arch=None):
    		if arch is None:
    			arch = self.arch
    		if not isinstance(adjust, types.SizeWithConfidence):
    			adjust = types.SizeWithConfidence(adjust)
    		core.BNSetUserCallStackAdjustment(self.handle, arch.handle, addr, adjust.value, adjust.confidence)

    	def set_call_reg_stack_adjustment(self, addr, adjust, arch=None):
    		if arch is None:
    			arch = self.arch
    		adjust_buf = (core.BNRegisterStackAdjustment * len(adjust))()
    		i = 0
    		for reg_stack in adjust.keys():
    			adjust_buf[i].regStack = arch.get_reg_stack_index(reg_stack)
    			value = adjust[reg_stack]
    			if not isinstance(value, types.RegisterStackAdjustmentWithConfidence):
    				value = types.RegisterStackAdjustmentWithConfidence(value)
    			adjust_buf[i].adjustment = value.value
    			adjust_buf[i].confidence = value.confidence
    			i += 1
    		core.BNSetUserCallRegisterStackAdjustment(self.handle, arch.handle, addr, adjust_buf, len(adjust))

    	def set_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, adjust, arch=None):
    		if arch is None:
    			arch = self.arch
    		reg_stack = arch.get_reg_stack_index(reg_stack)
    		if not isinstance(adjust, types.RegisterStackAdjustmentWithConfidence):
    			adjust = types.RegisterStackAdjustmentWithConfidence(adjust)
    		core.BNSetUserCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack,
    			adjust.value, adjust.confidence)

    	def get_call_stack_adjustment(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		result = core.BNGetCallStackAdjustment(self.handle, arch.handle, addr)
    		return types.SizeWithConfidence(result.value, confidence = result.confidence)

    	def get_call_reg_stack_adjustment(self, addr, arch=None):
    		if arch is None:
    			arch = self.arch
    		count = ctypes.c_ulonglong()
    		adjust = core.BNGetCallRegisterStackAdjustment(self.handle, arch.handle, addr, count)
    		result = {}
    		for i in range(0, count.value):
    			result[arch.get_reg_stack_name(adjust[i].regStack)] = types.RegisterStackAdjustmentWithConfidence(
    				adjust[i].adjustment, confidence = adjust[i].confidence)
    		core.BNFreeRegisterStackAdjustments(adjust)
    		return result

    	def get_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, arch=None):
    		if arch is None:
    			arch = self.arch
    		reg_stack = arch.get_reg_stack_index(reg_stack)
    		adjust = core.BNGetCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack)
    		result = types.RegisterStackAdjustmentWithConfidence(adjust.adjustment, confidence = adjust.confidence)
    		return result

    class AdvancedFunctionAnalysisDataRequestor(object):
    	def __init__(self, func = None):
    		self._function = func
    		if self._function is not None:
    			self._function.request_advanced_analysis_data()

    	def __del__(self):
    		if self._function is not None:
    			self._function.release_advanced_analysis_data()

    	@property
    	def function(self):
    		return self._function

    	@function.setter
    	def function(self, func):
    		if self._function is not None:
    			self._function.release_advanced_analysis_data()
    		self._function = func
    		if self._function is not None:
    			self._function.request_advanced_analysis_data()

    	def close(self):
    		if self._function is not None:
    			self._function.release_advanced_analysis_data()
    		self._function = None

    class DisassemblyTextLine(object):
    	def __init__(self, addr, tokens, il_instr = None):
    		self.address = addr
    		self.tokens = tokens
    		self.il_instruction = il_instr

    	def __str__(self):
    		result = ""
    		for token in self.tokens:
    			result += token.text
    		return result

    	def __repr__(self):
    		return "<%#x: %s>" % (self.address, str(self))

    class FunctionGraphEdge(object):
    	def __init__(self, branch_type, source, target, points, back_edge):
    		self.type = BranchType(branch_type)
    		self.source = source
    		self.target = target
    		self.points = points
    		self.back_edge = back_edge

    	def __repr__(self):
    		return "<%s: %s>" % (self.type.name, repr(self.target))

    class FunctionGraphBlock(object):
    	def __init__(self, handle, graph):
    		self.handle = handle
    		self.graph = graph

    	def __del__(self):
    		core.BNFreeFunctionGraphBlock(self.handle)

    	def __eq__(self, value):
    		if not isinstance(value, FunctionGraphBlock):
    			return False
    		return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)

    	def __ne__(self, value):
    		if not isinstance(value, FunctionGraphBlock):
    			return True
    		return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)

    	@property
    	def basic_block(self):
    		"""Basic block associated with this part of the function graph (read-only)"""
    		block = core.BNGetFunctionGraphBasicBlock(self.handle)
    		func_handle = core.BNGetBasicBlockFunction(block)
    		if func_handle is None:
    			core.BNFreeBasicBlock(block)
    			return None

    		view = binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func_handle))
    		func = Function(view, func_handle)

    		if core.BNIsLowLevelILBasicBlock(block):
    			block = binaryninja.lowlevelil.LowLevelILBasicBlock(view, block,
    				lowlevelil.LowLevelILFunction(func.arch, core.BNGetBasicBlockLowLevelILFunction(block), func))
    		elif core.BNIsMediumLevelILBasicBlock(block):
    			block = binaryninja.mediumlevelil.MediumLevelILBasicBlock(view, block,
    				mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func))
    		else:
    			block = binaryninja.basicblock.BasicBlock(view, block)
    		return block

    	@property
    	def arch(self):
    		"""Function graph block architecture (read-only)"""
    		arch = core.BNGetFunctionGraphBlockArchitecture(self.handle)
    		if arch is None:
    			return None
    		return binaryninja.architecture.CoreArchitecture._from_cache(arch)

    	@property
    	def start(self):
    		"""Function graph block start (read-only)"""
    		return core.BNGetFunctionGraphBlockStart(self.handle)

    	@property
    	def end(self):
    		"""Function graph block end (read-only)"""
    		return core.BNGetFunctionGraphBlockEnd(self.handle)

    	@property
    	def x(self):
    		"""Function graph block X (read-only)"""
    		return core.BNGetFunctionGraphBlockX(self.handle)

    	@property
    	def y(self):
    		"""Function graph block Y (read-only)"""
    		return core.BNGetFunctionGraphBlockY(self.handle)

    	@property
    	def width(self):
    		"""Function graph block width (read-only)"""
    		return core.BNGetFunctionGraphBlockWidth(self.handle)

    	@property
    	def height(self):
    		"""Function graph block height (read-only)"""
    		return core.BNGetFunctionGraphBlockHeight(self.handle)

    	@property
    	def lines(self):
    		"""Function graph block list of lines (read-only)"""
    		count = ctypes.c_ulonglong()
    		lines = core.BNGetFunctionGraphBlockLines(self.handle, count)
    		block = self.basic_block
    		result = []
    		for i in range(0, count.value):
    			addr = lines[i].addr
    			if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(block, 'il_function'):
    				il_instr = block.il_function[lines[i].instrIndex]
    			else:
    				il_instr = None
    			tokens = []
    			for j in range(0, lines[i].count):
    				token_type = InstructionTextTokenType(lines[i].tokens[j].type)
    				text = lines[i].tokens[j].text
    				value = lines[i].tokens[j].value
    				size = lines[i].tokens[j].size
    				operand = lines[i].tokens[j].operand
    				context = lines[i].tokens[j].context
    				confidence = lines[i].tokens[j].confidence
    				address = lines[i].tokens[j].address
    				tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
    			result.append(DisassemblyTextLine(addr, tokens, il_instr))
    		core.BNFreeDisassemblyTextLines(lines, count.value)
    		return result

    	@property
    	def outgoing_edges(self):
    		"""Function graph block list of outgoing edges (read-only)"""
    		count = ctypes.c_ulonglong()
    		edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count)
    		result = []
    		for i in range(0, count.value):
    			branch_type = BranchType(edges[i].type)
    			target = edges[i].target
    			if target:
    				func = core.BNGetBasicBlockFunction(target)
    				if func is None:
    					core.BNFreeBasicBlock(target)
    					target = None
    				else:
    					target = binaryninja.basicblock.BasicBlock(binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func)),
    						core.BNNewBasicBlockReference(target))
    					core.BNFreeFunction(func)
    			points = []
    			for j in range(0, edges[i].pointCount):
    				points.append((edges[i].points[j].x, edges[i].points[j].y))
    			result.append(FunctionGraphEdge(branch_type, self, target, points, edges[i].backEdge))
    		core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value)
    		return result

    	def __setattr__(self, name, value):
    		try:
    			object.__setattr__(self, name, value)
    		except AttributeError:
    			raise AttributeError("attribute '%s' is read only" % name)

    	def __repr__(self):
    		arch = self.arch
    		if arch:
    			return "<graph block: %s@%#x-%#x>" % (arch.name, self.start, self.end)
    		else:
    			return "<graph block: %#x-%#x>" % (self.start, self.end)

    	def __iter__(self):
    		count = ctypes.c_ulonglong()
    		lines = core.BNGetFunctionGraphBlockLines(self.handle, count)
    		block = self.basic_block
    		try:
    			for i in range(0, count.value):
    				addr = lines[i].addr
    				if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(block, 'il_function'):
    					il_instr = block.il_function[lines[i].instrIndex]
    				else:
    					il_instr = None
    				tokens = []
    				for j in range(0, lines[i].count):
    					token_type = InstructionTextTokenType(lines[i].tokens[j].type)
    					text = lines[i].tokens[j].text
    					value = lines[i].tokens[j].value
    					size = lines[i].tokens[j].size
    					operand = lines[i].tokens[j].operand
    					context = lines[i].tokens[j].context
    					confidence = lines[i].tokens[j].confidence
    					address = lines[i].tokens[j].address
    					tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
    				yield DisassemblyTextLine(addr, tokens, il_instr)
    		finally:
    			core.BNFreeDisassemblyTextLines(lines, count.value)

    class DisassemblySettings(object):
    	def __init__(self, handle = None):
    		if handle is None:
    			self.handle = core.BNCreateDisassemblySettings()
    		else:
    			self.handle = handle

    	def __del__(self):
    		core.BNFreeDisassemblySettings(self.handle)

    	@property
    	def width(self):
    		return core.BNGetDisassemblyWidth(self.handle)

    	@width.setter
    	def width(self, value):
    		core.BNSetDisassemblyWidth(self.handle, value)

    	@property
    	def max_symbol_width(self):
    		return core.BNGetDisassemblyMaximumSymbolWidth(self.handle)

    	@max_symbol_width.setter
    	def max_symbol_width(self, value):
    		core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value)

    	def is_option_set(self, option):
    		if isinstance(option, str):
    			option = DisassemblyOption[option]
    		return core.BNIsDisassemblySettingsOptionSet(self.handle, option)

    	def set_option(self, option, state = True):
    		if isinstance(option, str):
    			option = DisassemblyOption[option]
    		core.BNSetDisassemblySettingsOption(self.handle, option, state)

    _pending_function_graph_completion_events = {}
    class FunctionGraph(object):
    	def __init__(self, view, handle):
    		self.view = view
    		self.handle = handle
    		self._on_complete = None
    		self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete)

    	def __del__(self):
    		self.abort()
    		core.BNFreeFunctionGraph(self.handle)

    	def __eq__(self, value):
    		if not isinstance(value, FunctionGraph):
    			return False
    		return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)

    	def __ne__(self, value):
    		if not isinstance(value, FunctionGraph):
    			return True
    		return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)

    	@property
    	def function(self):
    		"""Function for a function graph (read-only)"""
    		func = core.BNGetFunctionForFunctionGraph(self.handle)
    		if func is None:
    			return None
    		return Function(self.view, func)

    	@property
    	def complete(self):
    		"""Whether function graph layout is complete (read-only)"""
    		return core.BNIsFunctionGraphLayoutComplete(self.handle)

    	@property
    	def type(self):
    		"""Function graph type (read-only)"""
    		return FunctionGraphType(core.BNGetFunctionGraphType(self.handle))

    	@property
    	def blocks(self):
    		"""List of basic blocks in function (read-only)"""
    		count = ctypes.c_ulonglong()
    		blocks = core.BNGetFunctionGraphBlocks(self.handle, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self))
    		core.BNFreeFunctionGraphBlockList(blocks, count.value)
    		return result

    	@property
    	def has_blocks(self):
    		"""Whether the function graph has at least one block (read-only)"""
    		return core.BNFunctionGraphHasBlocks(self.handle)

    	@property
    	def width(self):
    		"""Function graph width (read-only)"""
    		return core.BNGetFunctionGraphWidth(self.handle)

    	@property
    	def height(self):
    		"""Function graph height (read-only)"""
    		return core.BNGetFunctionGraphHeight(self.handle)

    	@property
    	def horizontal_block_margin(self):
    		return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle)

    	@horizontal_block_margin.setter
    	def horizontal_block_margin(self, value):
    		core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin)

    	@property
    	def vertical_block_margin(self):
    		return core.BNGetVerticalFunctionGraphBlockMargin(self.handle)

    	@vertical_block_margin.setter
    	def vertical_block_margin(self, value):
    		core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value)

    	@property
    	def settings(self):
    		return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle))

    	@property
    	def is_il(self):
    		return core.BNIsILFunctionGraph(self.handle)

    	@property
    	def is_low_level_il(self):
    		return core.BNIsLowLevelILFunctionGraph(self.handle)

    	@property
    	def is_medium_level_il(self):
    		return core.BNIsMediumLevelILFunctionGraph(self.handle)

    	@property
    	def il_function(self):
    		if self.is_low_level_il:
    			il_func = core.BNGetFunctionGraphLowLevelILFunction(self.handle)
    			if not il_func:
    				return None
    			return binaryninja.lowlevelil.LowLevelILFunction(self.function.arch, il_func, self.function)
    		if self.is_medium_level_il:
    			il_func = core.BNGetFunctionGraphMediumLevelILFunction(self.handle)
    			if not il_func:
    				return None
    			return binaryninja.mediumlevelil.MediumLevelILFunction(self.function.arch, il_func, self.function)
    		return None

    	def __setattr__(self, name, value):
    		try:
    			object.__setattr__(self, name, value)
    		except AttributeError:
    			raise AttributeError("attribute '%s' is read only" % name)

    	def __repr__(self):
    		return "<graph of %s>" % repr(self.function)

    	def __iter__(self):
    		count = ctypes.c_ulonglong()
    		blocks = core.BNGetFunctionGraphBlocks(self.handle, count)
    		try:
    			for i in range(0, count.value):
    				yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self)
    		finally:
    			core.BNFreeFunctionGraphBlockList(blocks, count.value)

    	def _complete(self, ctxt):
    		try:
    			if self._on_complete is not None:
    				self._on_complete()
    				global _pending_function_graph_completion_events
    				if id(self) in _pending_function_graph_completion_events:
    					del _pending_function_graph_completion_events[id(self)]
    		except:
    			log.log_error(traceback.format_exc())

    	def layout(self, graph_type = FunctionGraphType.NormalFunctionGraph):
    		if isinstance(graph_type, str):
    			graph_type = FunctionGraphType[graph_type]
    		core.BNStartFunctionGraphLayout(self.handle, graph_type)

    	def _wait_complete(self):
    		self._wait_cond.acquire()
    		self._wait_cond.notify()
    		self._wait_cond.release()

    	def layout_and_wait(self, graph_type=FunctionGraphType.NormalFunctionGraph):
    		self._wait_cond = threading.Condition()
    		self.on_complete(self._wait_complete)
    		self.layout(graph_type)

    		self._wait_cond.acquire()
    		while not self.complete:
    			self._wait_cond.wait()
    		self._wait_cond.release()

    	def on_complete(self, callback):
    		global _pending_function_graph_completion_events
    		_pending_function_graph_completion_events[id(self)] = self
    		self._on_complete = callback
    		core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb)

    	def abort(self):
    		core.BNAbortFunctionGraph(self.handle)
    		global _pending_function_graph_completion_events
    		if id(self) in _pending_function_graph_completion_events:
    			del _pending_function_graph_completion_events[id(self)]

    	def get_blocks_in_region(self, left, top, right, bottom):
    		count = ctypes.c_ulonglong()
    		blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count)
    		result = []
    		for i in range(0, count.value):
    			result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self))
    		core.BNFreeFunctionGraphBlockList(blocks, count.value)
    		return result

    	def is_option_set(self, option):
    		if isinstance(option, str):
    			option = DisassemblyOption[option]
    		return core.BNIsFunctionGraphOptionSet(self.handle, option)

    	def set_option(self, option, state = True):
    		if isinstance(option, str):
    			option = DisassemblyOption[option]
    		core.BNSetFunctionGraphOption(self.handle, option, state)

    class RegisterInfo(object):
    	def __init__(self, full_width_reg, size, offset=0, extend=ImplicitRegisterExtend.NoExtend, index=None):
    		self.full_width_reg = full_width_reg
    		self.offset = offset
    		self.size = size
    		self.extend = extend
    		self.index = index

    	def __repr__(self):
    		if self.extend == ImplicitRegisterExtend.ZeroExtendToFullWidth:
    			extend = ", zero extend"
    		elif self.extend == ImplicitRegisterExtend.SignExtendToFullWidth:
    			extend = ", sign extend"
    		else:
    			extend = ""
    		return "<reg: size %d, offset %d in %s%s>" % (self.size, self.offset, self.full_width_reg, extend)

    class RegisterStackInfo(object):
    	def __init__(self, storage_regs, top_relative_regs, stack_top_reg, index=None):
    		self.storage_regs = storage_regs
    		self.top_relative_regs = top_relative_regs
    		self.stack_top_reg = stack_top_reg
    		self.index = index

    	def __repr__(self):
    		return "<reg stack: %d regs, stack top in %s>" % (len(self.storage_regs), self.stack_top_reg)

    class IntrinsicInput(object):
    	def __init__(self, type_obj, name=""):
    		self.name = name
    		self.type = type_obj

    	def __repr__(self):
    		if len(self.name) == 0:
    			return "<input: %s>" % str(self.type)
    		return "<input: %s %s>" % (str(self.type), self.name)

    class IntrinsicInfo(object):
    	def __init__(self, inputs, outputs, index=None):
    		self.inputs = inputs
    		self.outputs = outputs
    		self.index = index

    	def __repr__(self):
    		return "<intrinsic: %s -> %s>" % (repr(self.inputs), repr(self.outputs))

    class InstructionBranch(object):
    	def __init__(self, branch_type, target = 0, arch = None):
    		self.type = branch_type
    		self.target = target
    		self.arch = arch

    	def __repr__(self):
    		branch_type = self.type
    		if self.arch is not None:
    			return "<%s: %s@%#x>" % (branch_type.name, self.arch.name, self.target)
    		return "<%s: %#x>" % (branch_type, self.target)

    class InstructionInfo(object):
    	def __init__(self):
    		self.length = 0
    		self.arch_transition_by_target_addr = False
    		self.branch_delay = False
    		self.branches = []

    	def add_branch(self, branch_type, target = 0, arch = None):
    		self.branches.append(InstructionBranch(branch_type, target, arch))

    	def __repr__(self):
    		branch_delay = ""
    		if self.branch_delay:
    			branch_delay = ", delay slot"
    		return "<instr: %d bytes%s, %s>" % (self.length, branch_delay, repr(self.branches))

    class InstructionTextToken(object):
    	"""
    	``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views.

    		========================== ============================================
    		InstructionTextTokenType   Description
    		========================== ============================================
    		TextToken                  Text that doesn't fit into the other tokens
    		InstructionToken           The instruction mnemonic
    		OperandSeparatorToken      The comma or whatever else separates tokens
    		RegisterToken              Registers
    		IntegerToken               Integers
    		PossibleAddressToken       Integers that are likely addresses
    		BeginMemoryOperandToken    The start of memory operand
    		EndMemoryOperandToken      The end of a memory operand
    		FloatingPointToken         Floating point number
    		AnnotationToken            **For internal use only**
    		CodeRelativeAddressToken   **For internal use only**
    		StackVariableTypeToken     **For internal use only**
    		DataVariableTypeToken      **For internal use only**
    		FunctionReturnTypeToken    **For internal use only**
    		FunctionAttributeToken     **For internal use only**
    		ArgumentTypeToken          **For internal use only**
    		ArgumentNameToken          **For internal use only**
    		HexDumpByteValueToken      **For internal use only**
    		HexDumpSkippedByteToken    **For internal use only**
    		HexDumpInvalidByteToken    **For internal use only**
    		HexDumpTextToken           **For internal use only**
    		OpcodeToken                **For internal use only**
    		StringToken                **For internal use only**
    		CharacterConstantToken     **For internal use only**
    		CodeSymbolToken            **For internal use only**
    		DataSymbolToken            **For internal use only**
    		StackVariableToken         **For internal use only**
    		ImportToken                **For internal use only**
    		AddressDisplayToken        **For internal use only**
    		========================== ============================================

    	"""
    	def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff,
    		context = InstructionTextTokenContext.NoTokenContext, address = 0, confidence = types.max_confidence):
    		self.type = InstructionTextTokenType(token_type)
    		self.text = text
    		self.value = value
    		self.size = size
    		self.operand = operand
    		self.context = InstructionTextTokenContext(context)
    		self.confidence = confidence
    		self.address = address

    	def __str__(self):
    		return self.text

    	def __repr__(self):
    		return repr(self.text)