/** * Holds the rules to evaluate the operators of PHP. */ module php/strategy/const-prop/analysis/common/operators /** * The first block of strategies concerns assignment. * Depending on the RHS of the assignment, different things * need to be done. * Since assignment is part of the boilerplate-bookkeeping, the * strategies are more special. */ strategies /** * Assignment operators * http://www.php.net/manual/en/language.operators.assignment.php */ assign-operator-const-prop(main,get,set,valid-get,valid-put,valid-remove|) = ( Assign(Variable(main) ,main) ; assign-to-variable(get,set,valid-get,valid-put,valid-remove|) ) <+ ( Assign(ArrayAccess(main,main),main) ; assign-to-array(main,set,get,valid-put,valid-remove|) ) <+ ( Assign(StringAccess(main,main),main) ; assign-to-array(main,set,get,valid-put,valid-remove|) ) /** * Assignment to a variable is special if the RHS is an array. In that case * the assignment needs to copy the array. Since it is possible that a second * analysis is done, we need to copy the array again with the same new * array-id. * If there is no assignment we need to undefine the mapping to the variable, * it is not known anymore what the properties are. */ assign-to-variable(get,set,valid-get,valid-put,valid-remove|) = ?Assign(Variable(var), expr) ;(( where(if <has-php-array-value> expr then aid := <get-simple-array-value> expr ; newaid := <copy-php-array> aid ; value := newaid ; try( <copy-php-array(valid-get,valid-put|newaid)> aid ) else value := <get-php-simple-value> expr end ; <add-php-variable-rule> (Variable(var),value) ) ; add-php-simple-value(|value) ) <+ where( <remove-php-variable-rule> Variable(var) )) // when done we try the other analysis ;try( ((where( type := <get> expr ; <add-php-variable-rule(valid-put)> (Variable(var),type) ) ; set(|type)) <+ where(<remove-php-variable-rule(valid-remove)> Variable(var)) )) /** * Assigning to an array is special when the array is the globals-array. * Otherwise it is a simple generic assignment. */ assign-to-array(main,set,get,valid-put,valid-remove|) = ( ?Assign(ArrayAccess(var,Some(keyExpr)), expr) <+ ?Assign(StringAccess(var,keyExpr), expr) ) ; try( where( (( aid := <get-simple-array-value> var ; <is-globals-array> aid ; name := <get-simple-raw-string-value> keyExpr ; <explode-string; ?[<is-alpha> | _ ]> name ; <main> Assign(Variable(Simple(name)),expr) ) <+ <generic-assign-to-array(main,get,valid-put,valid-remove|)> (var,Some(Key(keyExpr)),expr) ) ) ; add-php-simple-value(|<get-php-simple-value> expr) ) ; try( set(|<get> expr) ) assign-to-array(main,set,get,valid-put,valid-remove|) = ?Assign(ArrayAccess(var,None()), expr) ; where(try( <generic-assign-to-array(main,get,valid-put,valid-remove|)> (var,None(),expr)) ; add-php-simple-value(|<get-php-simple-value> expr) ) ; try(set(|<get> expr)) /** * A reference assign assigns the RHS as a reference to the LHS. Both locations * will point to the same value-location. */ assign-operator-const-prop(main,get,set,valid-get,valid-put,valid-remove|) = ReferenceAssign(Variable(id),main) ; ?ReferenceAssign(Variable(var1),expr) ; where( !expr ; ( ?Variable(var2) <+( (?ArrayAccess(array,Some(keyexpr)) <+ (?StringAccess(array,keyexpr)) ) ; aid := <get-simple-array-value> array ; key := <get-simple-raw-value> keyexpr ; var2 := (aid,key) )) ; valueIdentifier := <get-php-value-identifier> var2 ; <add-php-variable-identifier-rule> (Variable(var1),valueIdentifier) ) ;try(add-php-simple-value(|<get-php-simple-value> Variable(var2))) ;try(set(|<get> Variable(var2))) assign-operator-const-prop(main,get,set,valid-get,valid-put,valid-remove|) = //Assignment to array without key ?ReferenceAssign(ArrayAccess(<main; ?var>,None()), <main ; ?expr>) ; add-php-simple-value(| <generic-reference-assign-to-array(main,get,valid-put,valid-remove|)> (var,None(),expr)) assign-operator-const-prop(main,get,set,valid-get,valid-put,valid-remove|) = ( ?ReferenceAssign(ArrayAccess(<main; ?var>,Some(<main; ?keyExpr>)), expr) <+ ?ReferenceAssign(StringAccess(<main; ?var>,<main; ?keyExpr>), expr) ) ; where( aid := <get-simple-array-value> var ; if <not(is-globals-array)> aid then value := <generic-reference-assign-to-array(main,get,valid-put,valid-remove|)> (var,Some(Key(keyExpr)),expr) else name := <get-simple-raw-string-value> keyExpr ; if <explode-string; ?[is-alpha | _ ]> name then value := <main> ReferenceAssign(Variable(Simple(name)),expr) else value := <generic-reference-assign-to-array(main,get,valid-put,valid-remove|)> (var,Some(Key(keyExpr)),expr) end end ) ; add-php-simple-value(|value) /** * Generic action for assigning a value to an array. This strategy works * on a three-tuple and delivers a value to be returned. * * @type (arrayvariable, key-expression, expression to assign) */ generic-assign-to-array(main,get,valid-put,valid-remove|) = ?(var,key,expr) ; aid := <get-simple-array-value> var ; value := <add-array-pairs-to-array(main,get,valid-put,valid-remove|aid)> Pair(key,Value(expr)) generic-reference-assign-to-array(main,get,valid-put,valid-remove|) = ?(var,key,expr) ; aid := <get-simple-array-value> var ; value := <add-array-pairs-to-array(main,get,valid-put,valid-remove|aid)> Pair(key,RefValue(expr)) /** * The following is const-prop for operators. * Since operators manipulate values, we expect every analysis * to implement their own handling, just like literals. */ strategies /** * Section for casting 'operators'. * @see http://www.php.net/manual/en/language.types.type-juggling.php */ operator-const-prop(main|) = IntCast(main) ; generic-cast-operator(get-simple-integer-value) operator-const-prop(main|) = FloatCast(main) ; generic-cast-operator(get-simple-float-value) operator-const-prop(main|) = BoolCast(main) ; generic-cast-operator(get-simple-boolean-value) operator-const-prop(main|) = StringCast(main) ; generic-cast-operator(get-simple-string-value) operator-const-prop(main|) = ArrayCast(main) ; generic-cast-operator(get-simple-array-value) operator-const-prop(main|) = NullCast(main) ; ?NullCast(expr) ; add-php-simple-value(|PHPNull()) generic-cast-operator(s) = ( ?IntCast(expr) <+ ?FloatCast(expr) <+ ?BoolCast(expr) <+ ?StringCast(expr) <+ ?ArrayCast(expr) ) ; where( value := <s> expr) ; add-php-simple-value(|value) strategies /** * Arithmetic Operators * see: http://www.php.net/manual/en/language.operators.arithmetic.php */ operator-const-prop(main|) = Negative(main) ; ?Negative(expr) ; where( value := <get-php-simple-value ; get-php-number-value> expr ; value' := <PHPInteger(<subt> (0,<id>)) <+ PHPFloat(<subt> (0,<id>))> ) ; add-php-simple-value(|value') operator-const-prop(main|) = Positive(main) ; ?Positive(expr) ; add-php-simple-value(|<get-php-simple-value; get-php-number-value> expr) operator-const-prop(main|) = Plus(main,main) ; generic-aritmetic-operator(add) operator-const-prop(main|) = Min(main,main) ; generic-aritmetic-operator(subt) operator-const-prop(main|) = Mul(main,main) ; generic-aritmetic-operator(mul) // Mod also results in false with a value of null operator-const-prop(main|) = Mod(main,main) ; ?Mod(x,y) ; where ( if <get-php-simple-value ; results-in-zero-value> y then value := PHPBoolean(False()) else value := <generic-aritmetic-operator(mod);get-php-simple-value> Mod(x,y) end ) ; add-php-simple-value(|value) /** * Exceptions add div: * - Dividing by zero results in false * - When an int is divided by an int the result is a int, * unless the value is not a fraction. */ operator-const-prop(main|) = Div(main,main) ; ?Div(x,y) ; where ( if <get-php-simple-value ; results-in-zero-value> y then value := PHPBoolean(False()) else tmp1 := <generic-aritmetic-operator(div) ; get-php-simple-value> Div(x,y) ; tmp2 := <generic-aritmetic-operator(div, get-php-float-value); get-php-simple-value> Div(x,y) ; if <eq> (<get-php-raw-value; real> tmp1, <get-php-raw-value> tmp2) then value := tmp1 //no difference between integer and real division, must be an int else value := tmp2 end end ) ; add-php-simple-value(|value) /** * Generic aritmetic operator, evaluates a pair of terms according * to a givens strategy. The strategy is applied to the number-values of the given * terms. The result is based on the outcome of the strategy. An outcome of type * real is wrapped in a PHPFloat and an integer is wrapped in PHPInteger. * * @param s The strategy to combine the two types * @param transform The strategy to be applied after getting the number value. * default: id * @type (term, term) -> PHPInteger OR PHPFloat */ generic-aritmetic-operator(s) = generic-aritmetic-operator(s, id) generic-aritmetic-operator(s, transform) = ( ?Plus(x,y) <+ ?Min(x,y) <+ ?Mul(x,y) <+ ?Mod(x,y) <+ ?Div(x,y) ) ; where( x' := <get-php-simple-value ; get-php-number-value ; transform ; get-php-raw-value> x ; y' := <get-php-simple-value ; get-php-number-value ; transform ; get-php-raw-value> y ; comp := <s> (x',y') ) ; if <is-real> comp then add-php-simple-value(|PHPFloat(comp)) else add-php-simple-value(|PHPInteger(comp)) end /** * Passes if the given php-simple-value will result in a * value of 0. * * @type PHPSimpleValue -> PHPSimpleValue */ results-in-zero-value = ?PHPNull() <+ ?PHPBoolean(False()) <+ ?PHPInteger(0) <+ ?PHPFloat(0.0) <+ (?PHPString(_) ; get-php-integer-value ; ?PHPInteger(0)) strategies /** * Comparison Operators * see: http://www.php.net/manual/en/language.operators.comparison.php */ operator-const-prop(main|) = IsEqual(main,main) ; ?IsEqual(x,y) ; where( if ((<has-php-string-value> x <+ <has-php-null-value> x); <has-php-string-value> y) then value := <generic-equal-operator(get-simple-raw-string-value)> (x, y) else if (<has-php-boolean-value> x <+ <has-php-null-value> x) then value := <generic-equal-operator(get-simple-raw-boolean-value)> (x, y) else value := <generic-equal-operator(get-simple-raw-integer-value)> (x, y) end end ) ; add-php-simple-value(|value) operator-const-prop(main|) = // just reverse result of IsEqual IsNotEqual(main,main) ; ?IsNotEqual(x,y) ; where( value := <main; get-php-simple-value> Not(IsEqual(x,y))) ; add-php-simple-value(|value) operator-const-prop(main|) = // If they have the same type then we check IsEqual IsIdentical(main,main) ; ?IsIdentical(x,y) ; where( if <have-same-php-value> (x,y) then value := <main; get-php-simple-value> IsEqual(x,y) else value := PHPBoolean(False()) end ) ; add-php-simple-value(|value) operator-const-prop(main|) = // just reverse result of IsIdentical IsNotIdentical(main,main) ; ?IsNotIdentical(x,y) ; where( value := <main; get-php-simple-value> Not(IsIdentical(x,y))) ; add-php-simple-value(|value) operator-const-prop(main|) = Greater(main,main) ; ?Greater(x,y) ; where ( if (<has-php-string-value> x <+ <has-php-string-value> y) then value := <generic-comparison-operator(get-simple-raw-string-value,string-case-gt)> (x, y) else value := <generic-comparison-operator(get-simple-raw-integer-value,gt)> (x, y) end ) ; add-php-simple-value(|value) operator-const-prop(main|) = // If equal otherwise if greater GreaterEqual(main,main) ; ?GreaterEqual(x,y) ; where( if <eq> (<main; get-php-simple-value> IsEqual(x,y), PHPBoolean(True())) then value := PHPBoolean(True()) else value := <main; get-php-simple-value> Greater(x,y) end ) ; add-php-simple-value(|value) operator-const-prop(main|) = // Not GreaterEqual Less(main,main) ; ?Less(x,y) ; add-php-simple-value(|<main; get-php-simple-value> Not(GreaterEqual(x,y))) operator-const-prop(main|) = // Not Greater LessEqual(main,main) ; ?LessEqual(x,y) ; add-php-simple-value(|<main; get-php-simple-value> Not(Greater(x,y))) //Utils for comparison operators strategies /** * Shortcut for generic-comparison-operator(id,equal) */ generic-equal-operator = generic-equal-operator(id) /** * Shortcut for generic-comparison-operator(s,equal) */ generic-equal-operator(s) = generic-comparison-operator(s,equal) /** * Generic comparison operator. Matches a tuple, * applies 's' to the terms and compares the terms * using the 'comp' strategy. Builds True() if comp succeeds, * False() otherwise. * * @param s Term -> Term * @param comp (x,y) -> _ * @type (term, term) -> PHPBoolean(Boolean) */ generic-comparison-operator(s,comp) = ?(x,y) ; x' := <s> x ; y' := <s> y ; if <comp> (x', y') then bool := PHPBoolean(True()) else bool := PHPBoolean(False()) end strategies /** * Logical operators * http://www.php.net/manual/en/language.operators.logical.php */ operator-const-prop(main|) = Not(main) ; ?Not(expr) ; where( value := <generic-equal-operator> (<get-simple-boolean-value> expr, PHPBoolean(False())) ) ; add-php-simple-value(|value) operator-const-prop(main|) = LAnd(main,main) ; generic-logical-op(eq,?True()|True(),False()) operator-const-prop(main|) = And(main,main) ; generic-logical-op(eq,?True()|True(),False()) operator-const-prop(main|) = LOr(main,main) ; generic-logical-op(eq,?False()|False(),True()) operator-const-prop(main|) = Or(main,main) ; generic-logical-op(eq,?False()|False(),True()) operator-const-prop(main|) = LXor(main,main) ; generic-logical-op(eq,id|False(),True()) generic-logical-op(s1,s2|val1,val2) = ( ?And(x,y) <+ ?Or(x,y) <+ ?LAnd(x,y) <+ ?LOr(x,y) <+ ?LXor(x,y) ) ; x' := <get-simple-raw-boolean-value> x ; y' := <get-simple-raw-boolean-value> y ; if (<s1> (x',y'); <s2> x' ) then add-php-simple-value(|PHPBoolean(val1)) else add-php-simple-value(|PHPBoolean(val2)) end strategies /** * String operators * http://www.php.net/manual/en/language.operators.string.php */ operator-const-prop(main|) = Concat(main,main) ; ?Concat(x,y) ; where( value := PHPString(<conc-strings> (<get-simple-raw-string-value> x ,<get-simple-raw-string-value> y ) ) ) ; add-php-simple-value(|value) strategies /** * Increment / Decrement operators * http://www.php.net/manual/en/language.operators.increment.php */ operator-const-prop(main|) = ( PostInc(main ; has-php-boolean-value) <+ PostDec(main ; has-php-boolean-value) <+ PreInc(main ; has-php-boolean-value) <+ PreDec(main ; has-php-boolean-value) ); ( ?PostInc(expr) <+ ?PostDec(expr) <+ ?PreInc(expr) <+ ?PreDec(expr) ) ; add-php-simple-value(|<get-php-simple-value> expr) /** * Section for null */ operator-const-prop(main|) = //decrement has no effect on null ( PostDec(main ; has-php-null-value) <+ PreDec(main ; has-php-null-value) ) ; add-php-simple-value(|PHPNull()) operator-const-prop(main|) = //decrement has effect on null ( PostInc(main ; has-php-null-value) <+ PreInc(main ; has-php-null-value) ) ; add-php-simple-value(|PHPInteger(1)) /** * Section for integers */ operator-const-prop(main|) = //result is old value PostInc(main ; has-php-integer-value) ; ?PostInc(cvar) ; where (cvar' := <get-simple-integer-value> cvar) ; where (<main> Assign(cvar,Plus(cvar,LNumber(Deci("1"))))) ; add-php-simple-value(|cvar') operator-const-prop(main|) = //result is new value PreInc(main ; has-php-integer-value) ; ?PreInc(cvar) ; where (<main; get-php-simple-value; ?cvar'> Assign(cvar,Plus(cvar,LNumber(Deci("1"))))) ; add-php-simple-value(|cvar') operator-const-prop(main|) = //result is old value PostDec(main ; has-php-integer-value) ; ?PostDec(cvar) ; where (cvar' := <get-simple-integer-value> cvar) ; where (<main> Assign(cvar,Min(cvar,LNumber(Deci("1"))))) ; add-php-simple-value(|cvar') operator-const-prop(main|) = //result is new value PreDec(main ; has-php-integer-value) ; ?PreDec(cvar) ; where (<main; get-php-simple-value; ?cvar'> Assign(cvar,Min(cvar,LNumber(Deci("1"))))) ; add-php-simple-value(|cvar') /** * Section for Strings */ operator-const-prop(main|) = //result is new value PreInc(main ; has-php-string-value) ; ?PreInc(cvar) ; where (value := <get-simple-raw-string-value ; ?str ; php-increment-string-value> cvar) ; where (<add-php-variable-rule> (cvar,PHPString(value))) ; add-php-simple-value(|PHPString(value)) operator-const-prop(main|) = //result is old value PostInc(main ; has-php-string-value) ; ?PostInc(cvar) ; where (value := <get-simple-raw-string-value ; ?str ; php-increment-string-value> cvar) ; where (<add-php-variable-rule> (cvar,PHPString(value))) ; add-php-simple-value(|PHPString(str)) operator-const-prop(main|) = //result is new value PreDec(main ; has-php-string-value) ; ?PreDec(cvar) ; where (value := <get-simple-raw-string-value ; ?str ; php-decrement-string-value> cvar) ; where (<add-php-variable-rule> (cvar,PHPString(value))) ; add-php-simple-value(|PHPString(value)) operator-const-prop(main|) = //result is old value PostDec(main ; has-php-string-value) ; ?PostDec(cvar) ; where (value := <get-simple-raw-string-value ; ?str ; php-decrement-string-value> cvar) ; where (<add-php-variable-rule> (cvar,PHPString(value))) ; add-php-simple-value(|PHPString(str)) rules /** * Decrementation of a string. A single-char-string can be decremented. * Other strings are ignored. */ php-decrement-string-value: str -> str' where [char]:= <explode-string> str ; char' := <php-decrement-char> char ; str' := <implode-string> char' php-decrement-string-value: str -> str where <not(explode-string; ?[x])> str /** * Make sure that we stay in the right range. */ php-decrement-char: char -> char' where((<lt> (char,123); <gt> (char,97)) // ascii-range for small letters <+ (<lt> (char,91) ; <gt> (char,65))) // ascii-range for big letters ; char' := <subt; ![<id>]> (char,1) php-decrement-char: 122 -> 122 php-decrement-char: 90 -> 90 /** * Incrementing a string. Increment latest charachter and maybe update * the one before that if the Z or z is passed. */ php-increment-string-value: str -> str' where chars := <explode-string> str ; char := <last> chars ; char' := <php-increment-char> char ; chars':= <php-combine-incremented-char-lists> (chars,char') ; str' := <implode-string> chars' php-increment-char: char -> char' where((<lt> (char,122); <gt> (char,96)) // ascii-range for small letters <+ (<lt> (char,90) ; <gt> (char,64))) // ascii-range for big letters ; char' := <add; ![<id>]> (char,1) php-increment-char: 122 -> [97,97] //Adding Z or z goes to AA and aa php-increment-char: 90 -> [65,65] /** * Combining char-lists that are incremented. Lot of special cases for short * lists. */ php-combine-incremented-char-lists: ([],chars) -> chars php-combine-incremented-char-lists: (chars,[]) -> chars php-combine-incremented-char-lists: (original,[x]) -> chars where <not(?[])> original ; chars := <at-last(![x])> original php-combine-incremented-char-lists: ([x],chars) -> chars php-combine-incremented-char-lists: (original,[ _ | [y] ]) -> chars where <not(?[])> original ; <not(?[x])> original ; original' := <at-last(![])> original //remove last element, add one ; original'' := <implode-string; php-increment-string-value; explode-string> original' ; chars := <conc> (original'',[y]) // Utils strategies /** * Case sensitive equivalent of string-gt */ string-case-gt = try((explode-string, explode-string)); strcmp; ?1 /** * Case sensitive equivalent of string-lt */ string-case-lt = try((explode-string, explode-string)); strcmp; ?-1