Revision: 48545
Author:   werdna
Date:     2009-03-18 23:28:35 +0000 (Wed, 18 Mar 2009)

Log Message:
-----------
New short-circuiting of expensive operations when a boolean op means that the 
result won't matter

Modified Paths:
--------------
    trunk/extensions/AbuseFilter/AbuseFilter.parser.php

Modified: trunk/extensions/AbuseFilter/AbuseFilter.parser.php
===================================================================
--- trunk/extensions/AbuseFilter/AbuseFilter.parser.php 2009-03-18 23:27:48 UTC 
(rev 48544)
+++ trunk/extensions/AbuseFilter/AbuseFilter.parser.php 2009-03-18 23:28:35 UTC 
(rev 48545)
@@ -288,7 +288,7 @@
 }
        
 class AbuseFilterParser {
-       var $mParams, $mVars, $mCode, $mTokens, $mPos, $mCur;
+       var $mParams, $mVars, $mCode, $mTokens, $mPos, $mCur, $mShortCircuit;
        
        // length,lcase,ccnorm,rmdoubles,specialratio,rmspecials,norm,count
        static $mFunctions = array(
@@ -430,36 +430,81 @@
                if( $this->mCur->type == AFPToken::TKeyword && 
$this->mCur->value == 'if' ) {
                        $this->move();
                        $this->doLevelBoolOps( $result );
+                       
                        if( !($this->mCur->type == AFPToken::TKeyword && 
$this->mCur->value == 'then') )
                                throw new AFPUserVisibleException( 
'expectednotfound', $this->mCur->pos, array('then') );
                        $this->move();
+
+                       
                        $r1 = new AFPData();
                        $r2 = new AFPData();
+                       
+                       $isTrue = $result->toBool();
+                       
+                       if ($isTrue) {
+                               $scOrig = $this->mShortCircuit;
+                               $this->mShortCircuit = true;
+                       }
                        $this->doLevelConditions( $r1 );
+                       if ($isTrue) {
+                               $this->mShortCircuit = $scOrig;
+                       }
+                       
                        if( !($this->mCur->type == AFPToken::TKeyword && 
$this->mCur->value == 'else') )
                                throw new AFPUserVisibleException( 
'expectednotfound', $this->mCur->pos, array('else') );
                        $this->move();
+                       
+                       if (!$isTrue) {
+                               $scOrig = $this->mShortCircuit;
+                               $this->mShortCircuit = true;
+                       }
                        $this->doLevelConditions( $r2 );
+                       if (!$isTrue) {
+                               $this->mShortCircuit = $scOrig;
+                       }
+                       
+                       if( !($this->mCur->type == AFPToken::TKeyword && 
$this->mCur->value == 'end') )
+                               throw new AFPUserVisibleException( 
'expectednotfound', $this->mCur->pos, array('end') );
+                       $this->move();
+                       
                        if( $result->toBool() ) {
                                $result = $r1;
                        } else {
                                $result = $r2;
                        }
-                       if( !($this->mCur->type == AFPToken::TKeyword && 
$this->mCur->value == 'end') )
-                               throw new AFPUserVisibleException( 
'expectednotfound', $this->mCur->pos, array('end') );
-                       $this->move();
+                       
                } else {
                        $this->doLevelBoolOps( $result );
                        if( $this->mCur->type == AFPToken::TOp && 
$this->mCur->value == '?' ) {
                                $this->move();
                                $r1 = new AFPData();
                                $r2 = new AFPData();
+                               
+                               $isTrue = $result->toBool();
+                               
+                               if ($isTrue) {
+                                       $scOrig = $this->mShortCircuit;
+                                       $this->mShortCircuit = true;
+                               }
                                $this->doLevelConditions( $r1 );
+                               if ($isTrue) {
+                                       $this->mShortCircuit = $scOrig;
+                               }
+                               
                                if( !($this->mCur->type == AFPToken::TOp && 
$this->mCur->value == ':') )
                                        throw new AFPUserVisibleException( 
'expectednotfound', $this->mCur->pos, array(':') );
                                $this->move();
+                               
+                               if (!$isTrue) {
+                                       $scOrig = $this->mShortCircuit;
+                                       $this->mShortCircuit = true;
+                               }
                                $this->doLevelConditions( $r2 );
-                               if( $result->toBool() ) {
+                               if (!$isTrue) {
+                                       $this->mShortCircuit = $scOrig;
+                               }
+                               
+                               if( $isTrue ) {
                                        $result = $r1;
                                } else {
                                        $result = $r2;
@@ -475,7 +520,31 @@
                        $op = $this->mCur->value;
                        $this->move();
                        $r2 = new AFPData();
+                       
+                       if ($op == '&' && !$result) {
+                               wfProfileIn( __METHOD__.'-shortcircuit' );
+                               $orig = $this->mShortCircuit;
+                               $this->mShortCircuit = true;
+                               $this->doLevelCompares( $r2 );
+                               $this->mShortCircuit = false;
+                               $result = new AFPData( AFPData::DBool, false );
+                               wfProfileOut( __METHOD__.'-shortcircuit' );
+                               return;
+                       }
+                       
+                       if ($op == '|' && $result) {
+                               wfProfileIn( __METHOD__.'-shortcircuit' );
+                               $orig = $this->mShortCircuit;
+                               $this->mShortCircuit = true;
+                               $this->doLevelCompares( $r2 );
+                               $this->mShortCircuit = false;
+                               $result = new AFPData( AFPData::DBool, true );
+                               wfProfileOut( __METHOD__.'-shortcircuit' );
+                               return;
+                       }
+                       
                        $this->doLevelCompares( $r2 );
+                       
                        wfProfileIn( __METHOD__ );
                        $result = AFPData::boolOp( $result, $r2, $op );
                        wfProfileOut( __METHOD__ );
@@ -561,6 +630,11 @@
                        $this->move();
                        $r2 = new AFPData();
                        $this->doLevelUnarys( $r2 );
+                       
+                       if ($this->mShortCircuit) {
+                               return; // The result doesn't matter.
+                       }
+                       
                        wfProfileIn( __METHOD__ );
                        wfProfileIn( __METHOD__."-$func" );
                        $result = AFPData::$func( $result, $r2 );
@@ -615,8 +689,14 @@
                        if( $this->mCur->type != AFPToken::TBrace || 
$this->mCur->value != ')' ) {
                                throw new AFPUserVisibleException( 
'expectednotfound', $this->mCur->pos, array(')') );
                        }
+                       $this->move();
+                       
                        wfProfileOut( __METHOD__."-loadargs" );
                        
+                       if ($this->mShortCircuit) {
+                               return; // The result doesn't matter.
+                       }
+                       
                        wfProfileIn( __METHOD__."-$func" );
                        
                        $funcHash = md5($func.serialize($args));
@@ -633,8 +713,6 @@
                        }
                        
                        wfProfileOut( __METHOD__."-$func" );
-                       
-                       $this->move();
                        wfProfileOut( __METHOD__ );
                } else {
                        $this->doLevelAtom( $result );
@@ -646,6 +724,8 @@
                $tok = $this->mCur->value;
                switch( $this->mCur->type ) {
                        case AFPToken::TID:
+                               if ($this->mShortCircuit)
+                                       break;
                                $var = strtolower($tok);
                                $result = $this->getVarValue( $var );
                                break;
@@ -686,15 +766,19 @@
        }
        
        protected function getVarValue( $var ) {
+               wfProfileIn( __METHOD__ );
                $var = strtolower($var);
                $builderValues = AbuseFilter::getBuilderValues();
                if ( ! array_key_exists( $var, $builderValues['vars'] ) ) {
                        // If the variable is invalid, throw an exception
+                       wfProfileOut( __METHOD__ );
                        throw new AFPUserVisibleException( 'unrecognisedvar',
                                                                                
                $this->mCur->pos,
                                                                                
                array( $var ) );
                } else {
-                       return $this->mVars->getVar( $var );
+                       $val = $this->mVars->getVar( $var );
+                       wfProfile( __METHOD__ );
+                       return $val;
                }
        }
        



_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to