Php Inspections (EA Extended): if-conditions optimization
-
Upload
vladimir-reznichenko -
Category
Engineering
-
view
168 -
download
2
Transcript of Php Inspections (EA Extended): if-conditions optimization
Php Inspections (EA Extended)*:If-statements optimization
* Intelligent Static Code Analyzer for JetBrains IDEs
Vladimir Reznichenko Karlsruhe, 20 October 2016
What is Static Code Analysis about?
Static code analysis is about finding defects in source code *:
● Code Style violations (formatting, naming conventions and etc.);● Implementation Defects (bugs, portability, performance and etc.);● Security Issues (CVEs, API usage and etc.);● Code Duplication;● Code Metrics (e.g. Complexity, UTs coverage and etc.);
* and bytecode (e.g. FindBugs)
Php Inspections (EA Extended): what’s covered?
Challenges we met
● If-statements analysis:○ Execution costs estimation;○ Interconnected conditions; if (is_array($a) && $a[0] > 0) ;○ Variadic constructions, booleans, identical sub-expressions detection and more;
● exceptions handling workflow analysis:○ Simulation of the workflow for running analysis;○ PhpDoc parsing: PHP is not supporting “throws” declarations;○ Nested catch and finally has implementation issues in older PHP versions;
● analysis performance:○ Concurrency (inspections are running in several independent threads);○ GC: memory optimization (VisualVM, data structures);○ Avoid low-performing analysis, stop as early as possible;
Challenges we met
● If-statements analysis:○ Execution costs estimation;○ Interconnected conditions;○ Variadic constructions, booleans, identical sub-expressions detection and more;
If-statements analysis: patterns
● Execution costs: if ($var->method($a) && $b > 0) ;● Identical operands: if ($a !== $a) ;● Ambiguous type checks: if ($a instanceof \Date || $a instanceof \DateInterface) ;● If ($a instanceof \DateInterface && null !== $a) ;● Variadic constructions: if (isset($a) && isset($b)) ; => if (isset($a, $b)) ;● Hardcoded booleans: if ($a > 0 || true) ;● Confusing conditions: if ($a > 0 || $a <= 0 && $a > $minValue) ;● Duplicated expressions in elseif and nested ifs:● If (is_array($a) || is_string($a)) {● If (is_array($a) && count($a) > 0) ;● }
Execution costs estimation: idea
The challenge has a name: “Shortest path problem” from Graphs theory (Discrete mathematics).
Applying the problem e.g. to “if ($var->method($a) && $a > 0) ;” we have 2 paths:
● $var->method($a) ○ Method lookup ;○ Calls stack: push and pop ;○ Complex operation, therefore high execution costs ;
● $a > 0○ Primitive operation, therefore low execution costs ;
Execution costs estimation: example
Let’s take more common case: if-else construct.
If (<conditions>) { <operation 1>; } else { <operation 2>; }
Formula for if-else construction cost estimation will be*:
C(<if-else>) = C(<conditions>) + max(C(<operation 1>), C(<operation 2>))
* The theory of parsing, translation, and compiling
Execution costs estimation: C() function
This is most important part: which weight to assign to different constructs?
For this you need know your compiler/interpreter internals and language capabilities.
Example weights specific for PHP:● Binary/Unary operations: 0 ; (primitive operations)● Array access: +1 (hash-maps based arrays implementation) ;● Method/function reference: +5 (call stack invocation) ;● Lambdas: +10 (no JIT compiler, dynamically allocated) ;● etc.
Code samples: hooking into IDEpublic class NotOptimalIfConditionsInspection extends BasePhpInspection {
@Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new BasePhpElementVisitor() { public void visitPhpIf(If ifStatement) { /* we are visiting a branch of AST-tree here, analyze it */ }
/* other visitors here */ }; }
}
Code samples: processing conditionsLinkedList<PsiElement> conditions = <extraction_method>(ifStatement.getCondition(), operationHolder);if (null != conditions ) { allConditions.addAll(conditions); /* invoke strategies */ conditions.clear();}
for (ElseIf objElseIf : ifStatement.getElseIfBranches()) { conditions = <extraction_method>(objElseIf.getCondition(), operationHolder); if (null != conditions) { allConditions.addAll(conditions); /* invoke strategies */ conditions.clear(); }}<nested_ifs_duplicates_strategy>(allConditions, ifStatement);
Code samples: execution costs estimation
How to calculate execution costs for expression “$a->getConfig()[‘waf’]”
if (expression instanceof ArrayAccessExpression) {
final ArrayAccessExpression arrayAccess = (ArrayAccessExpression) expression; final ArrayIndex arrayIndex = arrayAccess.getIndex();
int ownCosts = getExpressionCost(arrayAccess.getValue()); /* recursion: $a->getConfig() -> 5 */ if (null != arrayIndex) { ownCosts += getExpressionCost(arrayIndex.getValue()); /* recursion: ‘waf’ -> 0 */ }
return (1 + ownCosts); /* -> 6 */
}
Thank you