public function virtual($method, array $args, $index, $types)
    {
        //if no test
        if (static::$stop) {
            return $this;
        }

        $offset = 1;

        //if the trace came from Argument->test()
        if (isset($trace['class'], $trace['function'])
            && $trace['class'] == __CLASS__
            && $trace['function'] == 'test'
        ) {
            //go back one more
            $offset = 2;
        }

        $trace = debug_backtrace();
        $trace = $trace[$offset];

        $types       = func_get_args();
        $method      = array_shift($types);
        $args          = array_shift($types);
        $index       = array_shift($types) - 1;

        if ($index < 0) {
            $index = 0;
        }

        //if it's not set then it's good because the default value
        //set in the method will be it.
        if ($index >= count($args)) {
            return $this;
        }

        $argument = $args[$index];

        foreach ($types as $i => $type) {
            if ($this->isValid($type, $argument)) {
                return $this;
            }
        }

        if (strpos($method, '->') === false && isset($trace['class'])) {
            $method = $trace['class'].'->'.$method;
        }

        $type = $this->getDataType($argument);

        Exception::i()
            ->setMessage(self::INVALID_ARGUMENT)
            ->addVariable($index + 1)
            ->addVariable($method)
            ->addVariable(implode(' or ', $types))
            ->addVariable($type)
            ->setTypeLogic()
            ->setTraceOffset($offset)
            ->trigger();
    }