/** * Will generate the code needed to enforce made invariant assertions * * @param \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $assertionLists List of assertion lists * * @return string */ protected function generateFunctionCode(TypedListList $assertionLists) { $code = 'protected function ' . ReservedKeywords::CLASS_INVARIANT . '(' . ReservedKeywords::INVARIANT_CALLER_VARIABLE . ', ' . ReservedKeywords::ERROR_LINE_VARIABLE . ') { ' . ReservedKeywords::CONTRACT_CONTEXT . ' = \\AppserverIo\\Doppelgaenger\\ContractContext::open(); if (' . ReservedKeywords::CONTRACT_CONTEXT . ') { ' . ReservedKeywords::FAILURE_VARIABLE . ' = array(); ' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ' = array();'; $conditionCounter = 0; $invariantIterator = $assertionLists->getIterator(); for ($i = 0; $i < $invariantIterator->count(); $i++) { // Create the inner loop for the different assertions if ($invariantIterator->current()->count() !== 0) { $assertionIterator = $invariantIterator->current()->getIterator(); // collect all assertion code for assertions of this instance for ($j = 0; $j < $assertionIterator->count(); $j++) { // Code to catch failed assertions $code .= $assertionIterator->current()->toCode(); $assertionIterator->next(); $conditionCounter++; } // generate the check for assertions results if ($conditionCounter > 0) { $code .= 'if (!empty(' . ReservedKeywords::FAILURE_VARIABLE . ') || !empty(' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ')) { ' . Placeholders::ENFORCEMENT . 'invariant' . Placeholders::PLACEHOLDER_CLOSE . ' }'; } } // increment the outer loop $invariantIterator->next(); } $code .= '} \\AppserverIo\\Doppelgaenger\\ContractContext::close(); }'; return $code; }
/** * Will generate the code needed to enforce made postcondition assertions * * @param \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $assertionLists List of assertion lists * @param string $functionName The name of the function for which we create the enforcement code * * @return string */ protected function generateCode(TypedListList $assertionLists, $functionName) { // We only use contracting if we're not inside another contract already $code = '/* BEGIN OF POSTCONDITION ENFORCEMENT */ if (' . ReservedKeywords::CONTRACT_CONTEXT . ') { ' . ReservedKeywords::FAILURE_VARIABLE . ' = array(); ' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ' = array(); '; // We need a counter to check how much conditions we got $conditionCounter = 0; $listIterator = $assertionLists->getIterator(); for ($i = 0; $i < $listIterator->count(); $i++) { // Create the inner loop for the different assertions $assertionIterator = $listIterator->current()->getIterator(); // Only act if we got actual entries if ($assertionIterator->count() === 0) { // increment the outer loop $listIterator->next(); continue; } // collect all assertion code for assertions of this instance for ($j = 0; $j < $assertionIterator->count(); $j++) { // Code to catch failed assertions $code .= $assertionIterator->current()->toCode(); $assertionIterator->next(); $conditionCounter++; } // generate the check for assertions results if ($conditionCounter > 0) { $code .= ' if (!empty(' . ReservedKeywords::FAILURE_VARIABLE . ') || !empty(' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ')) { ' . Placeholders::ENFORCEMENT . $functionName . 'postcondition' . Placeholders::PLACEHOLDER_CLOSE . ' } '; } // increment the outer loop $listIterator->next(); } // Closing bracket for contract depth check $code .= '} ' . '/* END OF POSTCONDITION ENFORCEMENT */'; // Did we get anything at all? If not only give back a comment. if ($conditionCounter === 0) { $code = '/* No postconditions for this function/method */'; } return $code; }
/** * Retrieves class attributes from token array. * * This method will search for any attributes a class might have. Just pass the token array of the class. * Work is done using token definitions and common sense in regards to PHP syntax. * To retrieve the different properties of an attribute it relies on getAttributeProperties(). * We need the list of invariants to mark attributes wo are under surveillance. * * @param array $tokens Array of tokens for this class * @param TypedListList $invariants List of invariants so we can compare the attributes to * * @return AttributeDefinitionList */ public function getAttributes(array $tokens, TypedListList $invariants = null) { // Check the tokens $attributes = new AttributeDefinitionList(); for ($i = 0; $i < count($tokens); $i++) { // If we got a variable we will check if there is any function definition above it. // If not, we got an attribute, if so we will check if there is an even number of closing and opening // brackets above it, which would mean we are not in the function. if (is_array($tokens[$i]) && $tokens[$i][0] === T_VARIABLE) { for ($j = $i - 1; $j >= 0; $j--) { if (is_array($tokens[$j]) && $tokens[$j][0] === T_FUNCTION) { // Initialize our counter and also the check if we even started counting $bracketCounter = 0; $usedCounter = false; // We got something, lets count the brackets between it and our variable's position for ($k = $j + 1; $k < $i; $k++) { if ($tokens[$k] === '{' || $tokens[$k][0] === T_CURLY_OPEN) { $usedCounter = true; $bracketCounter++; } elseif ($tokens[$k] === '}') { $usedCounter = true; $bracketCounter--; } } // If we got an even number of brackets (the counter is 0 and got used), we got an attribute if ($bracketCounter === 0 && $usedCounter === true) { $attributes->set($tokens[$i][1], $this->getAttributeProperties($tokens, $i)); } break; } elseif (is_array($tokens[$j]) && $tokens[$j][0] === $this->getToken()) { // If we reach the class definition without passing a function we definitely got an attribute $attributes->set($tokens[$i][1], $this->getAttributeProperties($tokens, $i)); break; } } } } // If we got invariants we will check if our attributes are used in invariants if ($invariants !== null) { // Lets iterate over all the attributes and check them against the invariants we got $listIterator = $invariants->getIterator(); $listCount = $listIterator->count(); $attributeIterator = $attributes->getIterator(); $attributeCount = $attributeIterator->count(); for ($i = 0; $i < $attributeCount; $i++) { // Do we have any of these attributes in our invariants? $listIterator = $invariants->getIterator(); for ($j = 0; $j < $listCount; $j++) { // Did we get anything useful? if ($listIterator->current() === null) { continue; } /** @var \AppserverIo\Doppelgaenger\Interfaces\TypedListInterface|\Iterator $invariantIterator */ $invariantIterator = $listIterator->current()->getIterator(); $invariantCount = $invariantIterator->count(); for ($k = 0; $k < $invariantCount; $k++) { $attributePosition = strpos($invariantIterator->current()->getString(), '$this->' . ltrim($attributeIterator->current()->getName(), '$')); if ($attributePosition !== false) { // Tell them we were mentioned and persist it $attributeIterator->current()->setInInvariant(true); } $invariantIterator->next(); } $listIterator->next(); } $attributeIterator->next(); } } return $attributes; }
/** * Will generate the code needed to enforce made precondition assertions * * @param \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $assertionLists List of assertion lists * @param string $functionName The name of the function for which we create the enforcement code * * @return string */ protected function generateCode(TypedListList $assertionLists, $functionName) { // We only use contracting if we're not inside another contract already $code = '/* BEGIN OF PRECONDITION ENFORCEMENT */ if (' . ReservedKeywords::CONTRACT_CONTEXT . ') { ' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' = false; ' . ReservedKeywords::FAILURE_VARIABLE . ' = array(); ' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ' = array(); '; // We need a counter to check how much conditions we got $conditionCounter = 0; $listIterator = $assertionLists->getIterator(); for ($i = 0; $i < $listIterator->count(); $i++) { // Create the inner loop for the different assertions $assertionIterator = $listIterator->current()->getIterator(); // Only act if we got actual entries if ($assertionIterator->count() === 0) { // increment the outer loop $listIterator->next(); continue; } // create a wrap around assuring that inherited conditions get or-combined $code .= ' if (' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' === false) { '; // iterate through the conditions for this certain instance for ($j = 0; $j < $assertionIterator->count(); $j++) { $conditionCounter++; // Code to catch failed assertions $code .= $assertionIterator->current()->toCode(); $assertionIterator->next(); } // close the or-combined wrap $code .= ' if (empty(' . ReservedKeywords::FAILURE_VARIABLE . ') && empty(' . ReservedKeywords::UNWRAPPED_FAILURE_VARIABLE . ')) { ' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' = true; } } '; // increment the outer loop $listIterator->next(); } // Preconditions need or-ed conditions so we make sure only one condition list gets checked $code .= 'if (' . ReservedKeywords::PASSED_ASSERTION_FLAG . ' === false){ ' . Placeholders::ENFORCEMENT . $functionName . 'precondition' . Placeholders::PLACEHOLDER_CLOSE . ' } } /* END OF PRECONDITION ENFORCEMENT */'; // If there were no assertions we will just return a comment if ($conditionCounter === 0) { return '/* No preconditions for this function/method */'; } return $code; }