Ejemplo n.º 1
0
    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]
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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])
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
 def eval(self, context, params=None):
     # return the string with all characters in reverse order:
     return eval_arg(self.arg, context)[::-1]
Ejemplo n.º 11
0
    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 ''
Ejemplo n.º 12
0
    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"