def eval(self, context, params=None): # Evaluate the value of the function returing the array. array_val = eval_arg(self.array, context=context) # Evaluate the index to read. array_index = eval_arg(self.index, context=context) # Do we have a list to read from? if (not isinstance(array_val, list)): log.error("%r is not a list. Cannot perform array access." % array_val) return '' # Do we have a valid index? if (not isinstance(array_index, int)): log.error( "Index %r is not an integer. Cannot perform array access." % array_index) return '' if ((array_index >= len(array_val)) or (array_index < 0)): log.error( "Index %r is outside array bounds. Cannot perform array access." % array_index) return '' # Everything is valid. Return the array element. return array_val[array_index]
def eval(self, context, params=None): # Handle accessing document variables as a special case. tmp = self.__repr__().lower() if (tmp.startswith("activedocument.variables(")): return eval_arg(self.__repr__(), context) # TODO: Need to actually have some sort of object model. For now # just treat this as a variable access. tmp_lhs = eval_arg(self.lhs, context) tmp_rhs = None rhs = None if (len(self.rhs1) > 0): rhs = self.rhs1 else: rhs = self.rhs[len(self.rhs) - 1] # If the final element in the member expression is a function call, # the result should be the result of the function call. Otherwise treat # it as a fancy variable access. if (isinstance(rhs, Function_Call)): # Skip local functions that have a name collision with VBA built in functions. if (context.contains_user_defined(rhs.name)): for func in Function_Call.log_funcs: if (rhs.name.lower() == func.lower()): return str(self) # This is not a builtin. Evaluate it tmp_rhs = eval_arg(rhs, context) return tmp_rhs else: return eval_arg(self.__repr__(), context)
def eval(self, context, params=None): #print "EVAL: " + str(self) # Unary operator? if (self.lhs is None): # We have only a RHS. Evaluate it. rhs = None try: rhs = eval_arg(self.rhs, context) except: log.error("Boolxpr: Cannot eval " + self.__repr__() + ".") return '' # Evalue the unary expression. #print "RHS (1): " + str(rhs) #print self.op.lower() if (self.op.lower() == "not"): return (not rhs) else: log.error("BoolExpr: Unknown unary op " + str(self.op)) return '' # If we get here we always have a LHS. Evaluate that in the current context. #print "HERE: 1" lhs = self.lhs try: lhs = eval_arg(self.lhs, context) #print "HERE: 2" except AttributeError: pass # Do we have an operator or just a variable reference? #print "LHS: " + str(lhs) #print self.op if (self.op is None): # Variable reference. Return its value. return lhs # We have an operator. Get the value of the RHS. rhs = self.rhs try: rhs = eval_arg(self.rhs, context) #print "HERE: 3" except AttributeError as e: #print e pass #print "RHS (2): " + str(rhs) # Evaluate the expression. if ((self.op.lower() == "and") or (self.op.lower() == "andalso")): return lhs and rhs elif ((self.op.lower() == "or") or (self.op.lower() == "orelse")): return lhs or rhs elif (self.op.lower() == "eqv"): return (lhs == rhs) else: log.error("BoolExpr: Unknown operator %r" % self.op) return False
def eval(self, context, params=None): # TODO: Need to actually have some sort of object model. For now # just treat this as a variable access. tmp_lhs = eval_arg(self.lhs, context) tmp_rhs = eval_arg(self.rhs, context) # If the final element in the member expression is a function call, # the result should be the result of the function call. Otherwise treat # it as a fancy variable access. if (isinstance(self.rhs, Function_Call)): return tmp_rhs else: return eval_arg(str(tmp_lhs) + "." + str(tmp_rhs), context)
def eval(self, context, params=None): # This is implemented in the common vba_library._Chr handler class. import vba_library # pylint: disable=protected-access chr_handler = vba_library._Chr() param = eval_arg(self.arg, context) return chr_handler.eval(context, [param])
def eval(self, context, params=None): # return the environment variable name surrounded by % signs: # e.g. Environ("TEMP") => "%TEMP%" arg = eval_arg(self.arg, context=context) value = '%%%s%%' % arg if (log.getEffectiveLevel() == logging.DEBUG): log.debug('evaluating Environ(%s) => %r' % (arg, value)) return value
def eval(self, context, params=None): # We always have a LHS. Evaluate that in the current context. lhs = self.lhs try: lhs = eval_arg(self.lhs, context) except AttributeError: pass # Do we have an operator or just a variable reference? if (self.op is None): # Variable reference. Return its value. return lhs # We have an operator. Get the value of the RHS. rhs = self.rhs try: rhs = eval_arg(self.rhs, context) except AttributeError: pass # Evaluate the expression. if (self.op == "="): return lhs == rhs elif (self.op == ">"): return lhs > rhs elif (self.op == "<"): return lhs < rhs elif (self.op == ">="): return lhs >= rhs elif (self.op == "<="): return lhs <= rhs elif (self.op == "<>"): return lhs != rhs else: log.error("BoolExprItem: Unknown operator %r" % self.op) return False
def eval(self, context, params=None): # Are we just looking up a variable called 'asc'? if (self.arg is None): try: return context.get("asc") except KeyError: return "NULL" # Eval the argument. c = eval_arg(self.arg, context) # Don't modify the "**MATCH ANY**" special value. c_str = safe_str_convert(c).strip() if (c_str == "**MATCH ANY**"): return c # Looks like Asc(NULL) is NULL? if (c == "NULL"): return 0 # Calling Asc() on int? if (isinstance(c, int)): r = c else: # Got a string. # Should this match anything? if (c_str == "**MATCH ANY**"): r = "**MATCH ANY**" # This is an unmodified Asc() call. else: r = vb_str.get_ms_ascii_value(c_str) # Return the result. if (log.getEffectiveLevel() == logging.DEBUG): log.debug("Asc: return %r" % r) return r
def eval(self, context, params=None): # We always have a LHS. Evaluate that in the current context. lhs = self.lhs try: lhs = eval_arg(self.lhs, context) except AttributeError: pass # Do we have an operator or just a variable reference? if (self.op is None): # Variable reference. Return its value. return lhs # We have an operator. Get the value of the RHS. rhs = self.rhs try: rhs = eval_arg(self.rhs, context) except AttributeError: pass # Handle unitialized variables. Grrr. Base their conversion on # the type of the initialized expression. if (rhs == "NULL"): if (isinstance(lhs, str)): rhs = '' else: rhs = 0 context.set(self.rhs, rhs) log.debug("Set unitinitialized " + str(self.rhs) + " = " + str(rhs)) if (lhs == "NULL"): if (isinstance(rhs, str)): lhs = '' else: lhs = 0 context.set(self.lhs, lhs) log.debug("Set unitialized " + str(self.lhs) + " = " + str(lhs)) # Ugh. VBA autoconverts strings and ints. if (isinstance(lhs, str) and isinstance(rhs, int)): # Convert both to ints, if possible. try: lhs = int(lhs) except: pass if (isinstance(rhs, str) and isinstance(lhs, int)): # Convert both to ints, if possible. try: rhs = int(rhs) except: pass # Evaluate the expression. if ((self.op == "=") or (self.op.lower() == "is")): return lhs == rhs elif (self.op == ">"): return lhs > rhs elif (self.op == "<"): return lhs < rhs elif (self.op == ">="): return lhs >= rhs elif (self.op == "<="): return lhs <= rhs elif (self.op == "<>"): return lhs != rhs elif (self.op.lower() == "like"): # TODO: Actually convert VBA regexes to Python regexes. try: return (re.match(rhs, lhs) is not None) except Exception as e: log.error("BoolExprItem: 'Like' re match failed. " + str(e)) return False else: log.error("BoolExprItem: Unknown operator %r" % self.op) return False
def eval(self, context, params=None): # return the string with all characters in reverse order: return eval_arg(self.arg, context)[::-1]
def eval(self, context, params=None): # create a new context for this execution: caller_context = context # Looks like local variables from the calling context can be accessed in the called # function, so keep those. #context = Context(context=caller_context, _locals=context.locals) # TODO: Local variable inheritence needs to be investigated more... context = Context(context=caller_context) context.in_procedure = True # We are entering the function so reset whether we executed a goto. context.goto_executed = False # Save the name of the current function so we can handle exit function calls. context.curr_func_name = safe_str_convert(self.name) # Set the information about labeled code blocks in the called # context. This will be used when emulating GOTOs. context.tagged_blocks = self.tagged_blocks # Compute the argument values. call_info = {} call_info["FUNCTION_NAME -->"] = (self.name, None) # add function name in locals if the function takes 0 arguments. This is # needed since otherwise it is not possible to differentiate a function call # from a reference to the function return value in the function body. if (len(self.params) == 0): call_info[self.name] = ('NULL', None) # Set the default parameter values. for param in self.params: init_val = None if (param.init_val is not None): init_val = eval_arg(param.init_val, context=context) call_info[param.name] = (init_val, None) # Array accesses of calls to functions that return an array are parsed as # function calls with the array indices given as function call arguments. Note # that this parsing problem only occurs for 0 argument functions (foo(12)). # Array accesses of functions with parameters look like 'bar(1,2,3)(12)', so they # parse properly. # # Check for the 0 parameter function array access case here. array_indices = None if ((self.params is not None) and (params is not None) and (len(self.params) == 0) and (len(params) > 0)): array_indices = params # Set given parameter values. self.byref_params = {} defined_param_pos = -1 for defined_param in self.params: # Get the given parameter at this position, if we have one. defined_param_pos += 1 param_value = "NULL" param_name = defined_param.name if ((params is not None) and (defined_param_pos < len(params))): param_value = params[defined_param_pos] # Handle empty string parameters. if (((param_value == 0) or (param_value == "NULL")) and (defined_param.my_type == "String")): param_value = "" # Coerce parameters to String if needed. if (defined_param.my_type == "String"): param_value = utils.safe_str_convert(param_value) # Add the parameter value to the local function context. if (log.getEffectiveLevel() == logging.DEBUG): log.debug('Function %s: setting param %s = %r' % (self.name, param_name, param_value)) # Handle params with default values. if ((param_name not in call_info) or (call_info[param_name] == ('', None)) or (param_value != "")): call_info[param_name] = (param_value, defined_param.my_type) # Is this a ByRef parameter? if (defined_param.mechanism == "ByRef"): # Save it so we can pull out the updated value in the Call statement. self.byref_params[(param_name, defined_param_pos)] = None # Do we have an obvious recursive loop? Detect this by looking for the current call # with the exact same arguments appearing in the call stack. # TODO: This needs more work and testing. if (context.call_stack.count(call_info) > 0): log.warn("Recursive infinite loop detected. Aborting call " + safe_str_convert(call_info)) #print self #print call_info #print context.call_stack #sys.exit(0) return "NULL" # Add the current call to the call stack. context.call_stack.append(call_info) # Assign all const variables first. do_const_assignments(self.statements, context) # Set the parameter values in the current context. for param_name in call_info: param_val, param_type = call_info[param_name] context.set(param_name, param_val, var_type=param_type, force_local=True) # Variable updates can go in the local scope. old_global_scope = context.global_scope context.global_scope = False # Emulate the function. if (log.getEffectiveLevel() == logging.DEBUG): log.debug('evaluating Function %s(%s)' % (self.name, params)) # TODO self.call_params context.clear_error() for s in self.statements: if (log.getEffectiveLevel() == logging.DEBUG): log.debug('Function %s eval statement: %s' % (self.name, s)) if (isinstance(s, VBA_Object)): s.eval(context=context) # Have we exited from the function with 'Exit Function'? if (context.exit_func): break # Was there an error that will make us jump to an error handler? if (context.must_handle_error()): break context.clear_error() # If the just run statement was a loop, the GOTO was run in # the loop and we have finished the loop, so run the statements # after the loop. if (is_loop_statement(s)): context.goto_executed = False s.exited_with_goto = False # Did we just run a GOTO? If so we should not run the # statements after the GOTO. if (context.goto_executed or s.exited_with_goto): if (log.getEffectiveLevel() == logging.DEBUG): log.debug("GOTO executed. Go to next loop iteration.") break # Reset variable update scoping. context.global_scope = old_global_scope # Run the error handler if we have one and we broke out of the statement # loop with an error. context.handle_error(params) # Handle trailing if's with no end if. if (self.bogus_if is not None): self.bogus_if.eval(context=context) # Done with call. Pop this call off the call stack. del context.call_stack[-1] # We are leaving the function so reset whether we executed a goto. context.goto_executed = False # Bubble up any unhandled errors to the caller. caller_context.got_error = context.got_error # Same with whether we did any wildcard value tests. caller_context.tested_wildcard = context.tested_wildcard # TODO: get result from context.locals context.exit_func = False try: # Save the values of the ByRef parameters. for byref_param in self.byref_params: if (context.contains(byref_param[0].lower())): self.byref_params[byref_param] = context.get( byref_param[0].lower()) # Get the return value. return_value = context.get(self.name, local_only=True) if ((return_value is None) or (isinstance(return_value, Function))): return_value = '' if (log.getEffectiveLevel() == logging.DEBUG): log.debug('Function %s: return value = %r' % (self.name, return_value)) # Convert the return value to a String if needed. if ((self.return_type == "String") and (not isinstance(return_value, str))): return_value = vba_conversion.coerce_to_str(return_value) # Handle array accesses of the results of 0 parameter functions if needed. if (array_indices is not None): # Does the function actually return an array? if (isinstance(return_value, list)): # Are the array indices valid? all_int = True for i in array_indices: if (not isinstance(i, int)): all_int = False break if (all_int): # Perform the array access. for i in array_indices: return_value = return_value[i] # Invalid array indices. else: log.warn("Array indices " + safe_str_convert(array_indices) + " are invalid. " + \ "Not doing array access of function return value.") # Function does not return array. else: log.warn( safe_str_convert(self) + " does not return an array. Not doing array access.") # Copy all the global variables from the function context to the caller # context so global updates are tracked. for global_var in context.globals.keys(): caller_context.globals[global_var] = context.globals[ global_var] # Try to identify string decode functions. We are looking # for functions that are called multiple times and always # return a string value. context.track_possible_decoded_str(self.name, return_value) # Done. Return the function result. if (log.getEffectiveLevel() == logging.DEBUG): log.debug("Returning from func " + safe_str_convert(self)) return return_value except KeyError: # No return value explicitly set. It looks like VBA uses an empty string as # these funcion values. return ''
def eval(self, context, params=None): # create a new context for this execution: caller_context = context context = Context(context=caller_context) context.in_procedure = True # Save the name of the current function so we can handle exit function calls. context.curr_func_name = safe_str_convert(self.name) # We are entering the function so reset whether we executed a goto. context.goto_executed = False # Set the information about labeled code blocks in the called # context. This will be used when emulating GOTOs. context.tagged_blocks = self.tagged_blocks # Compute the argument values. call_info = {} call_info["FUNCTION_NAME -->"] = self.name # Set the default parameter values. for param in self.params: init_val = None if (param.init_val is not None): init_val = eval_arg(param.init_val, context=context) call_info[param.name] = init_val # Set given parameter values. self.byref_params = {} if ((params is not None) and (len(params) == len(self.params))): # TODO: handle named parameters # pylint: disable=consider-using-enumerate for i in range(len(params)): # Set the parameter value. param_name = self.params[i].name param_value = params[i] # Handle empty string parameters. if ((param_value == 0) and (self.params[i].my_type == "String")): param_value = "" # Coerce parameters to String if needed. if ((self.params[i].my_type == "String") and (not self.params[i].is_array)): param_value = safe_str_convert(param_value) # Add the parameter value to the local function context. if (log.getEffectiveLevel() == logging.DEBUG): log.debug('Function %s: setting param %s = %r' % (self.name, param_name, param_value)) call_info[param_name] = param_value # Is this a ByRef parameter? if (self.params[i].mechanism == "ByRef"): # Save it so we can pull out the updated value in the Call statement. self.byref_params[(param_name, i)] = None # Do we have an obvious recursive loop? Detect this by looking for the current call # with the exact same arguments appearing in the call stack. # TODO: This needs more work and testing. if (context.call_stack.count(call_info) > 0): log.warn("Recursive infinite loop detected. Aborting call " + safe_str_convert(call_info)) #print self #print call_info #print context.call_stack #sys.exit(0) return "NULL" # Add the current call to the call stack. context.call_stack.append(call_info) # Assign all const variables first. do_const_assignments(self.statements, context) # Set the parameter values in the current context. for param_name in call_info: context.set(param_name, call_info[param_name], force_local=True) # Variable updates can go in the local scope. old_global_scope = context.global_scope context.global_scope = False # Emulate the function. if (log.getEffectiveLevel() == logging.DEBUG): log.debug('evaluating Sub %s(%s)' % (self.name, params)) log.info('evaluating Sub %s' % self.name) # TODO self.call_params context.clear_error() for s in self.statements: # Emulate the current statement. if (log.getEffectiveLevel() == logging.DEBUG): log.debug('Sub %s eval statement: %s' % (self.name, s)) if (isinstance(s, VBA_Object)): s.eval(context=context) # Was there an error that will make us jump to an error handler? #if (context.have_error()): if (context.must_handle_error()): break context.clear_error() # Did we just run a GOTO? If so we should not run the # statements after the GOTO. if (context.goto_executed or (hasattr(s, "exited_with_goto") and s.exited_with_goto)): if (log.getEffectiveLevel() == logging.DEBUG): log.debug( "GOTO executed. Control flow handled by GOTO, so skip rest of procedure statements." ) break # Reset variable update scoping. context.global_scope = old_global_scope # Run the error handler if we have one and we broke out of the statement # loop with an error. context.handle_error(params) # Handle trailing if's with no end if. if (self.bogus_if is not None): if (isinstance(self.bogus_if, VBA_Object)): self.bogus_if.eval(context=context) elif (isinstance(self.bogus_if, list)): for cmd in self.bogus_if: cmd.eval(context=context) # Save the values of the ByRef parameters. for byref_param in self.byref_params: self.byref_params[byref_param] = context.get( byref_param[0].lower()) # Done with call. Pop this call off the call stack. del context.call_stack[-1] # We are leaving the function so reset whether we executed a goto. context.goto_executed = False context.exit_func = False # Bubble up any unhandled errors to the caller. caller_context.got_error = context.got_error # Same with whether we did any wildcard value tests. caller_context.tested_wildcard = context.tested_wildcard # Handle subs with no return values. try: context.get(self.name) except KeyError: # No return value explicitly set. It looks like VBA uses an empty string as # these funcion values. context.set(self.name, '') if (log.getEffectiveLevel() == logging.DEBUG): log.debug("Returning from sub " + safe_str_convert(self)) return "NULL"