Changeset 89be1c68 for src


Ignore:
Timestamp:
Jul 17, 2017, 2:35:52 PM (7 years ago)
Author:
Rob Schluntz <rschlunt@…>
Branches:
ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, resolv-new, with_gc
Children:
7ebaa56
Parents:
b46e3bd
Message:

Remove Cost constructors, use only named members

This change makes it easier to read code involving costs, since in almost every case, only a single part of the cost tuple is relevant. Furthermore, this change makes it much simpler to add another dimension to the cost tuple, since only Cost.h needs to be updated, rather than every location using the cost constructor.

Location:
src/ResolvExpr
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • src/ResolvExpr/Alternative.cc

    rb46e3bd r89be1c68  
    2828                : cost( cost ), cvtCost( cvtCost ), expr( expr ), env( env ) {}
    2929
    30         Alternative::Alternative( const Alternative &other ) {
    31                 initialize( other, *this );
     30        Alternative::Alternative( const Alternative &other ) : cost( other.cost ), cvtCost( other.cvtCost ), expr( maybeClone( other.expr ) ), env( other.env ) {
    3231        }
    3332
    3433        Alternative &Alternative::operator=( const Alternative &other ) {
    3534                if ( &other == this ) return *this;
    36                 initialize( other, *this );
     35                delete expr;
     36                cost = other.cost;
     37                cvtCost = other.cvtCost;
     38                expr = maybeClone( other.expr );
     39                env = other.env;
    3740                return *this;
    3841        }
     
    5154                other.expr = nullptr;
    5255                return *this;
    53         }
    54 
    55         void Alternative::initialize( const Alternative &src, Alternative &dest ) {
    56                 dest.cost = src.cost;
    57                 dest.cvtCost = src.cvtCost;
    58                 dest.expr = maybeClone( src.expr );
    59                 dest.env = src.env;
    6056        }
    6157
  • src/ResolvExpr/Alternative.h

    rb46e3bd r89be1c68  
    3636                ~Alternative();
    3737
    38                 void initialize( const Alternative &src, Alternative &dest );
    39 
    4038                void print( std::ostream &os, int indent = 0 ) const;
    4139
  • src/ResolvExpr/AlternativeFinder.cc

    rb46e3bd r89be1c68  
    6767
    6868        Cost sumCost( const AltList &in ) {
    69                 Cost total;
     69                Cost total = Cost::zero;
    7070                for ( AltList::const_iterator i = in.begin(); i != in.end(); ++i ) {
    7171                        total += i->cost;
     
    218218                if ( StructInstType *structInst = dynamic_cast< StructInstType* >( expr->get_result() ) ) {
    219219                        NameExpr nameExpr( "" );
    220                         addAggMembers( structInst, expr, alt.cost+Cost( 0, 0, 1 ), alt.env, &nameExpr );
     220                        addAggMembers( structInst, expr, alt.cost+Cost::safe, alt.env, &nameExpr );
    221221                } else if ( UnionInstType *unionInst = dynamic_cast< UnionInstType* >( expr->get_result() ) ) {
    222222                        NameExpr nameExpr( "" );
    223                         addAggMembers( unionInst, expr, alt.cost+Cost( 0, 0, 1 ), alt.env, &nameExpr );
     223                        addAggMembers( unionInst, expr, alt.cost+Cost::safe, alt.env, &nameExpr );
    224224                } // if
    225225        }
     
    277277                FunctionType *function = safe_dynamic_cast< FunctionType* >( pointer->get_base() );
    278278
    279                 Cost convCost( 0, 0, 0 );
     279                Cost convCost = Cost::zero;
    280280                std::list< DeclarationWithType* >& formals = function->get_parameters();
    281281                std::list< DeclarationWithType* >::iterator formal = formals.begin();
     
    290290                                actualType->print( std::cerr, 8 );
    291291                        )
    292                         Cost actualCost;
     292                        Cost actualCost = Cost::zero;
    293293                        if ( formal == formals.end() ) {
    294294                                if ( function->get_isVarArgs() ) {
    295                                         convCost += Cost( 1, 0, 0 );
     295                                        convCost.incUnsafe();
    296296                                        continue;
    297297                                } else {
     
    319319                        convCost += newCost;
    320320                        actualCost += newCost;
    321                         if ( actualCost != Cost( 0, 0, 0 ) ) {
     321                        if ( actualCost != Cost::zero ) {
    322322                                Type *newType = formalType->clone();
    323323                                alt.env.apply( newType );
    324324                                *actualExpr = new CastExpr( *actualExpr, newType );
    325325                        }
    326                         convCost += Cost( 0, polyCost( formalType, alt.env, indexer ) + polyCost( actualType, alt.env, indexer ), 0 );
     326                        convCost.incPoly( polyCost( formalType, alt.env, indexer ) + polyCost( actualType, alt.env, indexer ) );
    327327                        ++formal; // can't be in for-loop update because of the continue
    328328                }
     
    346346                        }
    347347                        convCost += newCost;
    348                         convCost += Cost( 0, polyCost( assert->second.formalType, alt.env, indexer ) + polyCost( assert->second.actualType, alt.env, indexer ), 0 );
     348                        convCost.incPoly( polyCost( assert->second.formalType, alt.env, indexer ) + polyCost( assert->second.actualType, alt.env, indexer ) );
    349349                }
    350350
     
    456456                        // match flattened actuals with formal parameters - actuals will be grouped to match
    457457                        // with formals as appropriate
    458                         Cost cost;
     458                        Cost cost = Cost::zero;
    459459                        std::list< Expression * > newExprs;
    460460                        ObjectDecl * obj = safe_dynamic_cast< ObjectDecl * >( formal );
     
    668668                                UntypedExpr *vexpr = untypedExpr->clone();
    669669                                vexpr->set_result( pt.clone() );
    670                                 alternatives.push_back( Alternative( vexpr, env, Cost()) );
     670                                alternatives.push_back( Alternative( vexpr, env, Cost::zero) );
    671671                                return;
    672672                        }
     
    869869                        if ( thisCost != Cost::infinity ) {
    870870                                // count one safe conversion for each value that is thrown away
    871                                 thisCost += Cost( 0, 0, discardedValues );
     871                                thisCost.incSafe( discardedValues );
    872872
    873873                                candidates.push_back( Alternative( restructureCast( i->expr->clone(), toType ), i->env, i->cost, thisCost ) );
     
    916916                for ( std::list< DeclarationWithType* >::iterator i = declList.begin(); i != declList.end(); ++i ) {
    917917                        VariableExpr newExpr( *i, nameExpr->get_argName() );
    918                         alternatives.push_back( Alternative( newExpr.clone(), env, Cost() ) );
     918                        alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
    919919                        PRINT(
    920920                                std::cerr << "decl is ";
     
    10601060                        for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
    10611061                                VariableExpr newExpr( *i );
    1062                                 alternatives.push_back( Alternative( newExpr.clone(), env, Cost() ) );
     1062                                alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
    10631063                                renameTypes( alternatives.back().expr );
    10641064                        } // for
     
    12331233                                if ( thisCost != Cost::infinity ) {
    12341234                                        // count one safe conversion for each value that is thrown away
    1235                                         thisCost += Cost( 0, 0, discardedValues );
     1235                                        thisCost.incSafe( discardedValues );
    12361236                                        candidates.push_back( Alternative( new InitExpr( restructureCast( alt.expr->clone(), toType ), initAlt.designation->clone() ), newEnv, alt.cost, thisCost ) );
    12371237                                }
  • src/ResolvExpr/CastCost.cc

    rb46e3bd r89be1c68  
    4646                                assert( type );
    4747                                if ( type->get_base() ) {
    48                                         return castCost( src, type->get_base(), indexer, env ) + Cost( 0, 0, 1 );
     48                                        return castCost( src, type->get_base(), indexer, env ) + Cost::safe;
    4949                                } // if
    5050                        } // if
    5151                } // if
    5252                if ( typesCompatibleIgnoreQualifiers( src, dest, indexer, env ) ) {
    53                         return Cost( 0, 0, 0 );
     53                        return Cost::zero;
    5454                } else if ( dynamic_cast< VoidType* >( dest ) ) {
    55                         return Cost( 0, 0, 1 );
     55                        return Cost::safe;
    5656                } else if ( ReferenceType * refType = dynamic_cast< ReferenceType * > ( dest ) ) {
    5757                        return convertToReferenceCost( src, refType, indexer, env );
     
    6363                        } else {
    6464                                // xxx - why are we adding cost 0 here?
    65                                 return converter.get_cost() + Cost( 0, 0, 0 );
     65                                return converter.get_cost() + Cost::zero;
    6666                        } // if
    6767                } // if
     
    7676                if ( destAsPointer && basicType->isInteger() ) {
    7777                        // necessary for, e.g. unsigned long => void*
    78                         cost = Cost( 1, 0, 0 );
     78                        cost = Cost::unsafe;
    7979                } else {
    8080                        cost = conversionCost( basicType, dest, indexer, env );
     
    8585                if ( PointerType *destAsPtr = dynamic_cast< PointerType* >( dest ) ) {
    8686                        if ( pointerType->get_qualifiers() <= destAsPtr->get_qualifiers() && typesCompatibleIgnoreQualifiers( pointerType->get_base(), destAsPtr->get_base(), indexer, env ) ) {
    87                                 cost = Cost( 0, 0, 1 );
     87                                cost = Cost::safe;
    8888                        } else {
    8989                                TypeEnvironment newEnv( env );
     
    9292                                int castResult = ptrsCastable( pointerType->get_base(), destAsPtr->get_base(), newEnv, indexer );
    9393                                if ( castResult > 0 ) {
    94                                         cost = Cost( 0, 0, 1 );
     94                                        cost = Cost::safe;
    9595                                } else if ( castResult < 0 ) {
    9696                                        cost = Cost::infinity;
     
    100100                        if ( destAsBasic->isInteger() ) {
    101101                                // necessary for, e.g. void* => unsigned long
    102                                 cost = Cost( 1, 0, 0 );
     102                                cost = Cost::unsafe;
    103103                        } // if
    104104                }
  • src/ResolvExpr/ConversionCost.cc

    rb46e3bd r89be1c68  
    4545                                assert( type );
    4646                                if ( type->get_base() ) {
    47                                         return conversionCost( src, type->get_base(), indexer, env ) + Cost( 0, 0, 1 );
     47                                        return conversionCost( src, type->get_base(), indexer, env ) + Cost::safe;
    4848                                } // if
    4949                        } // if
     
    5858                if ( typesCompatibleIgnoreQualifiers( src, dest, indexer, env ) ) {
    5959///     std::cout << "compatible!" << std::endl;
    60                         return Cost( 0, 0, 0 );
     60                        return Cost::zero;
    6161                } else if ( dynamic_cast< VoidType* >( dest ) ) {
    62                         return Cost( 0, 0, 1 );
     62                        return Cost::safe;
    6363                } else if ( ReferenceType * refType = dynamic_cast< ReferenceType * > ( dest ) ) {
    6464                        // std::cerr << "conversionCost: dest is reference" << std::endl;
     
    7070                                return Cost::infinity;
    7171                        } else {
    72                                 return converter.get_cost() + Cost( 0, 0, 0 );
    73                         } // if
    74                 } // if
    75         }
    76 
    77         Cost convertToReferenceCost( Type * src, ReferenceType * dest, const SymTab::Indexer & indexer, const TypeEnvironment & env ) {
    78                 // std::cerr << "convert to reference cost..." << std::endl;
    79                 if ( ReferenceType *srcAsRef = dynamic_cast< ReferenceType * >( src ) ) { // pointer-like conversions between references
    80                         // std::cerr << "converting between references" << std::endl;
    81                         if ( srcAsRef->get_base()->get_qualifiers() <= dest->get_base()->get_qualifiers() && typesCompatibleIgnoreQualifiers( srcAsRef->get_base(), dest->get_base(), indexer, env ) ) {
    82                                 return Cost( 0, 0, 1 );
    83                         } else {  // xxx - this discards reference qualifiers from consideration -- reducing qualifiers is a safe conversion; is this right?
    84                                 int assignResult = ptrsAssignable( srcAsRef->get_base(), dest->get_base(), env );
    85                                 if ( assignResult < 0 ) {
    86                                         return Cost( 0, 0, 1 );
    87                                 } else if ( assignResult > 0 ) {
    88                                         return Cost( 1, 0, 0 );
    89                                 } // if
    90                         } // if
    91                 } else if ( typesCompatibleIgnoreQualifiers( src, dest->get_base(), indexer, env ) ) {
    92                         // std::cerr << "converting compatible base type" << std::endl;
    93                         if ( src->get_lvalue() ) {
    94                                 // std::cerr << "lvalue to reference conversion" << std::endl;
    95                                 // lvalue-to-reference conversion:  cv lvalue T => cv T &
    96                                 if ( src->get_qualifiers() == dest->get_base()->get_qualifiers() ) {
    97                                         return Cost( 0, 0, 1 ); // cost needs to be non-zero to add cast
    98                                 } if ( src->get_qualifiers() < dest->get_base()->get_qualifiers() ) {
    99                                         return Cost( 0, 0, 2 ); // cost needs to be higher than previous cast to differentiate adding qualifiers vs. keeping same
     72                                return converter.get_cost() + Cost::zero;
     73                        } // if
     74                } // if
     75        }
     76
     77        Cost convertToReferenceCost( Type * src, Type * dest, int diff, const SymTab::Indexer & indexer, const TypeEnvironment & env ) {
     78                std::cerr << "convert to reference cost..." << std::endl;
     79                if ( diff > 0 ) {
     80                        // TODO: document this
     81                        Cost cost = convertToReferenceCost( safe_dynamic_cast< ReferenceType * >( src )->get_base(), dest, diff-1, indexer, env );
     82                        // TODO: increment reference cost
     83                        cost.incSafe();
     84                        return cost;
     85                } else if ( diff < -1 ) {
     86                        // TODO: document this
     87                        Cost cost = convertToReferenceCost( src, safe_dynamic_cast< ReferenceType * >( dest )->get_base(), diff+1, indexer, env );
     88                        // TODO: increment reference cost
     89                        cost.incSafe();
     90                        return cost;
     91                } else if ( diff == 0 ) {
     92                        ReferenceType * srcAsRef = dynamic_cast< ReferenceType * >( src );
     93                        ReferenceType * destAsRef = dynamic_cast< ReferenceType * >( dest );
     94                        if ( srcAsRef && destAsRef ) { // pointer-like conversions between references
     95                                std::cerr << "converting between references" << std::endl;
     96                                if ( srcAsRef->get_base()->get_qualifiers() <= destAsRef->get_base()->get_qualifiers() && typesCompatibleIgnoreQualifiers( srcAsRef->get_base(), destAsRef->get_base(), indexer, env ) ) {
     97                                        return Cost::safe;
     98                                } else {  // xxx - this discards reference qualifiers from consideration -- reducing qualifiers is a safe conversion; is this right?
     99                                        int assignResult = ptrsAssignable( srcAsRef->get_base(), destAsRef->get_base(), env );
     100                                        if ( assignResult < 0 ) {
     101                                                return Cost::safe;
     102                                        } else if ( assignResult > 0 ) {
     103                                                return Cost::unsafe;
     104                                        } // if
     105                                } // if
     106                        } else {
     107                                std::cerr << "reference to rvalue conversion" << std::endl;
     108                                ConversionCost converter( dest, indexer, env );
     109                                src->accept( converter );
     110                                return converter.get_cost();
     111                        } // if
     112                } else {
     113                        ReferenceType * destAsRef = dynamic_cast< ReferenceType * >( dest );
     114                        assert( diff == -1 && destAsRef );
     115                        if ( typesCompatibleIgnoreQualifiers( src, destAsRef->get_base(), indexer, env ) ) {
     116                                std::cerr << "converting compatible base type" << std::endl;
     117                                if ( src->get_lvalue() ) {
     118                                        std::cerr << "lvalue to reference conversion" << std::endl;
     119                                        // lvalue-to-reference conversion:  cv lvalue T => cv T &
     120                                        if ( src->get_qualifiers() == destAsRef->get_base()->get_qualifiers() ) {
     121                                                return Cost::safe; // cost needs to be non-zero to add cast
     122                                        } if ( src->get_qualifiers() < destAsRef->get_base()->get_qualifiers() ) {
     123                                                return Cost::safe + Cost::safe; // cost needs to be higher than previous cast to differentiate adding qualifiers vs. keeping same
     124                                        } else {
     125                                                return Cost::unsafe;
     126                                        } // if
     127                                } else if ( destAsRef->get_base()->get_const() ) {
     128                                        std::cerr << "rvalue to const ref conversion" << std::endl;
     129                                        // rvalue-to-const-reference conversion: T => const T &
     130                                        return Cost::safe;
    100131                                } else {
    101                                         return Cost( 1, 0, 0 );
    102                                 }
    103                         } else if ( dest->get_base()->get_const() ) {
    104                                 // std::cerr << "rvalue to const ref conversion" << std::endl;
    105                                 // rvalue-to-const-reference conversion: T => const T &
    106                                 return Cost( 0, 0, 1 );
    107                         } else {
    108                                 // std::cerr << "rvalue to non-const reference conversion" << std::endl;
    109                                 // rvalue-to-reference conversion: T => T &
    110                                 return Cost( 1, 0, 0 );
    111                         }
    112                 } else {
    113                         // std::cerr << "attempting to convert from incompatible base type -- fail" << std::endl;
     132                                        // std::cerr << "rvalue to non-const reference conversion" << std::endl;
     133                                        // rvalue-to-reference conversion: T => T &
     134                                        return Cost::unsafe;
     135                                } // if
     136                        } // if
     137                        std::cerr << "attempting to convert from incompatible base type -- fail" << std::endl;
    114138                }
    115139                return Cost::infinity;
     140        }
     141
     142        Cost convertToReferenceCost( Type * src, ReferenceType * dest, const SymTab::Indexer & indexer, const TypeEnvironment & env ) {
     143                int sdepth = src->referenceDepth(), ddepth = dest->referenceDepth();
     144                return convertToReferenceCost( src, dest, sdepth-ddepth, indexer, env );
    116145        }
    117146
     
    205234                        int tableResult = costMatrix[ basicType->get_kind() ][ destAsBasic->get_kind() ];
    206235                        if ( tableResult == -1 ) {
    207                                 cost = Cost( 1, 0, 0 );
    208                         } else {
    209                                 cost = Cost( 0, 0, tableResult );
     236                                cost = Cost::unsafe;
     237                        } else {
     238                                cost = Cost::zero;
     239                                cost.incSafe( tableResult );
    210240                        } // if
    211241                } else if ( dynamic_cast< EnumInstType *>( dest ) ) {
    212242                        // xxx - not positive this is correct, but appears to allow casting int => enum
    213                         cost = Cost( 1, 0, 0 );
     243                        cost = Cost::unsafe;
    214244                } else if ( dynamic_cast< ZeroType* >( dest ) != nullptr || dynamic_cast< OneType* >( dest ) != nullptr ) {
    215                         cost = Cost( 1, 0, 0 );
     245                        cost = Cost::unsafe;
    216246                } // if
    217247        }
     
    220250                if ( PointerType *destAsPtr = dynamic_cast< PointerType* >( dest ) ) {
    221251                        if ( pointerType->get_base()->get_qualifiers() <= destAsPtr->get_base()->get_qualifiers() && typesCompatibleIgnoreQualifiers( pointerType->get_base(), destAsPtr->get_base(), indexer, env ) ) {
    222                                 cost = Cost( 0, 0, 1 );
     252                                cost = Cost::safe;
    223253                        } else {  // xxx - this discards pointer qualifiers from consideration -- reducing qualifiers is a safe conversion; is this right?
    224254                                int assignResult = ptrsAssignable( pointerType->get_base(), destAsPtr->get_base(), env );
    225255                                if ( assignResult < 0 ) {
    226                                         cost = Cost( 0, 0, 1 );
     256                                        cost = Cost::safe;
    227257                                } else if ( assignResult > 0 ) {
    228                                         cost = Cost( 1, 0, 0 );
     258                                        cost = Cost::unsafe;
    229259                                } // if
    230260                        } // if
    231261                } else if ( dynamic_cast< ZeroType* >( dest ) != nullptr || dynamic_cast< OneType* >( dest ) != nullptr ) {
    232                         cost = Cost( 1, 0, 0 );
     262                        cost = Cost::unsafe;
    233263                } // if
    234264        }
     
    243273                // cv can be safely dropped because of 'implicit dereference' behavior.
    244274                refType->get_base()->accept( *this );
     275                // TODO: increment reference cost
     276                cost.incSafe();
    245277        }
    246278
     
    267299                static BasicType integer( q, BasicType::SignedInt );
    268300                integer.accept( *this );  // safe if dest >= int
    269                 if ( cost < Cost( 1, 0, 0 ) ) {
     301                if ( cost < Cost::unsafe ) {
    270302                        cost.incSafe();
    271303                } // if
     
    289321                        assert( type );
    290322                        if ( type->get_base() ) {
    291                                 cost = conversionCost( type->get_base(), dest, indexer, env ) + Cost( 0, 0, 1 );
     323                                cost = conversionCost( type->get_base(), dest, indexer, env ) + Cost::safe;
    292324                        } // if
    293325                } // if
     
    295327
    296328        void ConversionCost::visit( __attribute((unused)) TupleType *tupleType) {
    297                 Cost c;
     329                Cost c = Cost::zero;
    298330                if ( TupleType *destAsTuple = dynamic_cast< TupleType* >( dest ) ) {
    299331                        std::list< Type* >::const_iterator srcIt = tupleType->get_types().begin();
     
    327359                        int tableResult = costMatrix[ BasicType::SignedInt ][ destAsBasic->get_kind() ];
    328360                        if ( tableResult == -1 ) {
    329                                 cost = Cost( 1, 0, 0 );
    330                         } else {
    331                                 cost = Cost( 0, 0, tableResult + 1 );
     361                                cost = Cost::unsafe;
     362                        } else {
     363                                cost = Cost::zero;
     364                                cost.incSafe( tableResult + 1 );
    332365                        }
    333366                } else if ( dynamic_cast< PointerType* >( dest ) ) {
    334                         cost = Cost( 0, 0, 1 );
     367                        cost = Cost::safe;
    335368                }
    336369        }
     
    343376                        int tableResult = costMatrix[ BasicType::SignedInt ][ destAsBasic->get_kind() ];
    344377                        if ( tableResult == -1 ) {
    345                                 cost = Cost( 1, 0, 0 );
    346                         } else {
    347                                 cost = Cost( 0, 0, tableResult + 1 );
     378                                cost = Cost::unsafe;
     379                        } else {
     380                                cost = Cost::zero;
     381                                cost.incSafe( tableResult + 1 );
    348382                        }
    349383                }
  • src/ResolvExpr/Cost.h

    rb46e3bd r89be1c68  
    2121namespace ResolvExpr {
    2222        class Cost {
    23           public:
    24                 Cost();
     23          private:
    2524                Cost( int unsafeCost, int polyCost, int safeCost );
    2625
    27                 void incUnsafe( int inc = 1 );
    28                 void incPoly( int inc = 1 );
    29                 void incSafe( int inc = 1 );
     26          public:
     27                Cost & incUnsafe( int inc = 1 );
     28                Cost & incPoly( int inc = 1 );
     29                Cost & incSafe( int inc = 1 );
    3030
    3131                Cost operator+( const Cost &other ) const;
     
    5050        };
    5151
    52         inline Cost::Cost() : unsafeCost( 0 ), polyCost( 0 ), safeCost( 0 ) {}
    53 
    5452        inline Cost::Cost( int unsafeCost, int polyCost, int safeCost ) : unsafeCost( unsafeCost ), polyCost( polyCost ), safeCost( safeCost ) {}
    5553
    56         inline void Cost::incUnsafe( int inc ) {
     54        inline Cost & Cost::incUnsafe( int inc ) {
    5755                unsafeCost += inc;
     56                return *this;
    5857        }
    5958
    60         inline void Cost::incPoly( int inc ) {
     59        inline Cost & Cost::incPoly( int inc ) {
    6160                polyCost += inc;
     61                return *this;
    6262        }
    6363
    64         inline void Cost::incSafe( int inc ) {
     64        inline Cost & Cost::incSafe( int inc ) {
    6565                safeCost += inc;
     66                return *this;
    6667        }
    6768
Note: See TracChangeset for help on using the changeset viewer.