Home | History | Annotate | Download | only in animator
      1 /* libs/graphics/animator/SkScript.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #include "SkScript.h"
     19 #include "SkMath.h"
     20 #include "SkParse.h"
     21 #include "SkString.h"
     22 #include "SkTypedArray.h"
     23 
     24 /* things to do
     25     ? re-enable support for struct literals (e.g., for initializing points or rects)
     26         {x:1, y:2}
     27     ? use standard XML / script notation like document.getElementById("canvas");
     28     finish support for typed arrays
     29         ? allow indexing arrays by string
     30             this could map to the 'name' attribute of a given child of an array
     31         ? allow multiple types in the array
     32     remove SkDisplayType.h  // from SkOperand.h
     33     merge type and operand arrays into scriptvalue array
     34 */
     35 
     36 #ifdef SK_DEBUG
     37 static const char* errorStrings[] = {
     38         "array index of out bounds", // kArrayIndexOutOfBounds
     39         "could not find reference id", // kCouldNotFindReferencedID
     40         "dot operator expects object", // kDotOperatorExpectsObject
     41         "error in array index", // kErrorInArrrayIndex
     42         "error in function parameters", // kErrorInFunctionParameters
     43         "expected array", // kExpectedArray
     44         "expected boolean expression", // kExpectedBooleanExpression
     45         "expected field name", // kExpectedFieldName
     46         "expected hex", // kExpectedHex
     47         "expected int for condition operator", // kExpectedIntForConditionOperator
     48         "expected number", // kExpectedNumber
     49         "expected number for array index", // kExpectedNumberForArrayIndex
     50         "expected operator", // kExpectedOperator
     51         "expected token", // kExpectedToken
     52         "expected token before dot operator", // kExpectedTokenBeforeDotOperator
     53         "expected value", // kExpectedValue
     54         "handle member failed", // kHandleMemberFailed
     55         "handle member function failed", // kHandleMemberFunctionFailed
     56         "handle unbox failed", // kHandleUnboxFailed
     57         "index out of range", // kIndexOutOfRange
     58         "mismatched array brace", // kMismatchedArrayBrace
     59         "mismatched brackets", // kMismatchedBrackets
     60         "no function handler found", // kNoFunctionHandlerFound
     61         "premature end", // kPrematureEnd
     62         "too many parameters", // kTooManyParameters
     63         "type conversion failed", // kTypeConversionFailed
     64         "unterminated string" // kUnterminatedString
     65 };
     66 #endif
     67 
     68 const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = {
     69     { kNoType, kNoType, kNoBias }, //   kUnassigned,
     70     { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd
     71     // kAddInt = kAdd,
     72     { kNoType, kNoType, kNoBias },  // kAddScalar,
     73     { kNoType, kNoType, kNoBias },  // kAddString,
     74     { kNoType, kNoType, kNoBias },  // kArrayOp,
     75     { kInt, kInt, kNoBias }, // kBitAnd
     76     { kNoType, kInt, kNoBias }, // kBitNot
     77     { kInt, kInt, kNoBias }, // kBitOr
     78     { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide
     79     // kDivideInt = kDivide
     80     { kNoType, kNoType, kNoBias },  // kDivideScalar
     81     { kNoType, kNoType, kNoBias },  // kElse
     82     { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual
     83     // kEqualInt = kEqual
     84     { kNoType, kNoType, kNoBias },  // kEqualScalar
     85     { kNoType, kNoType, kNoBias },  // kEqualString
     86     { kInt, kNoType, kNoBias },     // kFlipOps
     87     { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual
     88     // kGreaterEqualInt = kGreaterEqual
     89     { kNoType, kNoType, kNoBias },  // kGreaterEqualScalar
     90     { kNoType, kNoType, kNoBias },  // kGreaterEqualString
     91     { kNoType, kNoType, kNoBias },  // kIf
     92     { kNoType, kInt, kNoBias }, // kLogicalAnd  (really, ToBool)
     93     { kNoType, kInt, kNoBias }, // kLogicalNot
     94     { kInt, kInt, kNoBias }, // kLogicalOr
     95     { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus
     96     // kMinusInt = kMinus
     97     { kNoType, kNoType, kNoBias },  // kMinusScalar
     98     { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo
     99     // kModuloInt = kModulo
    100     { kNoType, kNoType, kNoBias },  // kModuloScalar
    101     { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply
    102     // kMultiplyInt = kMultiply
    103     { kNoType, kNoType, kNoBias },  // kMultiplyScalar
    104     { kNoType, kNoType, kNoBias },  // kParen
    105     { kInt, kInt, kNoBias }, // kShiftLeft
    106     { kInt, kInt, kNoBias }, // kShiftRight
    107     { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract
    108     // kSubtractInt = kSubtract
    109     { kNoType, kNoType, kNoBias },  // kSubtractScalar
    110     { kInt, kInt, kNoBias } // kXor
    111 };
    112 
    113 // Note that the real precedence for () [] is '2'
    114 // but here, precedence means 'while an equal or smaller precedence than the current operator
    115 // is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply
    116 // is preformed, since the add precedence is not smaller than multiply.
    117 // But, (3*4 does not process the '(', since brackets are greater than all other precedences
    118 #define kBracketPrecedence 16
    119 #define kIfElsePrecedence 15
    120 
    121 const signed char SkScriptEngine::gPrecedence[] = {
    122         -1, //  kUnassigned,
    123         6, // kAdd,
    124         // kAddInt = kAdd,
    125         6, // kAddScalar,
    126         6, // kAddString,   // string concat
    127         kBracketPrecedence, // kArrayOp,
    128         10, // kBitAnd,
    129         4, // kBitNot,
    130         12, // kBitOr,
    131         5, // kDivide,
    132         // kDivideInt = kDivide,
    133         5, // kDivideScalar,
    134         kIfElsePrecedence, // kElse,
    135         9, // kEqual,
    136         // kEqualInt = kEqual,
    137         9, // kEqualScalar,
    138         9, // kEqualString,
    139         -1, // kFlipOps,
    140         8, // kGreaterEqual,
    141         // kGreaterEqualInt = kGreaterEqual,
    142         8, // kGreaterEqualScalar,
    143         8, // kGreaterEqualString,
    144         kIfElsePrecedence, // kIf,
    145         13, // kLogicalAnd,
    146         4, // kLogicalNot,
    147         14, // kLogicalOr,
    148         4, // kMinus,
    149         // kMinusInt = kMinus,
    150         4, // kMinusScalar,
    151         5, // kModulo,
    152         // kModuloInt = kModulo,
    153         5, // kModuloScalar,
    154         5, // kMultiply,
    155         // kMultiplyInt = kMultiply,
    156         5, // kMultiplyScalar,
    157         kBracketPrecedence, // kParen,
    158         7, // kShiftLeft,
    159         7, // kShiftRight,  // signed
    160         6, // kSubtract,
    161         // kSubtractInt = kSubtract,
    162         6, // kSubtractScalar,
    163         11, // kXor
    164 };
    165 
    166 static inline bool is_between(int c, int min, int max)
    167 {
    168     return (unsigned)(c - min) <= (unsigned)(max - min);
    169 }
    170 
    171 static inline bool is_ws(int c)
    172 {
    173     return is_between(c, 1, 32);
    174 }
    175 
    176 static int token_length(const char* start) {
    177     char ch = start[0];
    178     if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
    179         return -1;
    180     int length = 0;
    181     do
    182         ch = start[++length];
    183     while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
    184         ch == '_' || ch == '$');
    185     return length;
    186 }
    187 
    188 SkScriptEngine::SkScriptEngine(SkOpType returnType) :
    189     fTokenLength(0), fReturnType(returnType), fError(kNoError)
    190 {
    191     SkSuppress noInitialSuppress;
    192     noInitialSuppress.fOperator = kUnassigned;
    193     noInitialSuppress.fOpStackDepth = 0;
    194     noInitialSuppress.fSuppress = false;
    195     fSuppressStack.push(noInitialSuppress);
    196     *fOpStack.push() = kParen;
    197     fTrackArray.appendClear();
    198     fTrackString.appendClear();
    199 }
    200 
    201 SkScriptEngine::~SkScriptEngine() {
    202     for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
    203         delete *stringPtr;
    204     for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
    205         delete *arrayPtr;
    206 }
    207 
    208 int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) {
    209     SkOp op = kUnassigned;
    210     bool reverseOperands = false;
    211     bool negateResult = false;
    212     int advance = 1;
    213     switch (ch) {
    214         case '+':
    215             // !!! ignoring unary plus as implemented here has the side effect of
    216             // suppressing errors like +"hi"
    217             if (lastPush == false)  // unary plus, don't push an operator
    218                 goto returnAdv;
    219             op = kAdd;
    220             break;
    221         case '-':
    222             op = lastPush ? kSubtract : kMinus;
    223             break;
    224         case '*':
    225             op = kMultiply;
    226             break;
    227         case '/':
    228             op = kDivide;
    229             break;
    230         case '>':
    231             if (nextChar == '>') {
    232                 op = kShiftRight;
    233                 goto twoChar;
    234             }
    235             op = kGreaterEqual;
    236             if (nextChar == '=')
    237                 goto twoChar;
    238             reverseOperands = negateResult = true;
    239             break;
    240         case '<':
    241             if (nextChar == '<') {
    242                 op = kShiftLeft;
    243                 goto twoChar;
    244             }
    245             op = kGreaterEqual;
    246             reverseOperands = nextChar == '=';
    247             negateResult = ! reverseOperands;
    248             advance += reverseOperands;
    249             break;
    250         case '=':
    251             if (nextChar == '=') {
    252                 op = kEqual;
    253                 goto twoChar;
    254             }
    255             break;
    256         case '!':
    257             if (nextChar == '=') {
    258                 op = kEqual;
    259                 negateResult = true;
    260 twoChar:
    261                 advance++;
    262                 break;
    263             }
    264             op = kLogicalNot;
    265             break;
    266         case '?':
    267             op = kIf;
    268             break;
    269         case ':':
    270             op = kElse;
    271             break;
    272         case '^':
    273             op = kXor;
    274             break;
    275         case '(':
    276             *fOpStack.push() = kParen;  // push even if eval is suppressed
    277             goto returnAdv;
    278         case '&':
    279             SkASSERT(nextChar != '&');
    280             op = kBitAnd;
    281             break;
    282         case '|':
    283             SkASSERT(nextChar != '|');
    284             op = kBitOr;
    285             break;
    286         case '%':
    287             op = kModulo;
    288             break;
    289         case '~':
    290             op = kBitNot;
    291             break;
    292     }
    293     if (op == kUnassigned)
    294         return 0;
    295     if (fSuppressStack.top().fSuppress == false) {
    296         signed char precedence = gPrecedence[op];
    297         do {
    298             int idx = 0;
    299             SkOp compare;
    300             do {
    301                 compare = fOpStack.index(idx);
    302                 if ((compare & kArtificialOp) == 0)
    303                     break;
    304                 idx++;
    305             } while (true);
    306             signed char topPrecedence = gPrecedence[compare];
    307             SkASSERT(topPrecedence != -1);
    308             if (topPrecedence > precedence || topPrecedence == precedence &&
    309                     gOpAttributes[op].fLeftType == kNoType) {
    310                 break;
    311             }
    312             if (processOp() == false)
    313                 return 0;   // error
    314         } while (true);
    315         if (negateResult)
    316             *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp);
    317         fOpStack.push(op);
    318         if (reverseOperands)
    319             *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp);
    320     }
    321 returnAdv:
    322     return advance;
    323 }
    324 
    325 void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) {
    326     UserCallBack callBack;
    327     callBack.fBoxCallBack = func;
    328     commonCallBack(kBox, callBack, userStorage);
    329 }
    330 
    331 void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) {
    332     callBack.fCallBackType = type;
    333     callBack.fUserStorage = userStorage;
    334     *fUserCallBacks.prepend() = callBack;
    335 }
    336 
    337 bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params,
    338         const SkFunctionParamType* paramTypes, int paramCount) {
    339     if (params.count() > paramCount) {
    340         fError = kTooManyParameters;
    341         return false;   // too many parameters passed
    342     }
    343     for (int index = 0; index < params.count(); index++) {
    344         if (convertTo((SkDisplayTypes) paramTypes[index], &params[index]) == false)
    345             return false;
    346     }
    347     return true;
    348 }
    349 
    350 bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) {
    351     SkDisplayTypes type = value->fType;
    352     if (type == toType)
    353         return true;
    354     if (ToOpType(type) == kObject) {
    355 #if 0   // !!! I want object->string to get string from displaystringtype, not id
    356         if (ToOpType(toType) == kString) {
    357             bool success = handleObjectToString(value->fOperand.fObject);
    358             if (success == false)
    359                 return false;
    360             SkOpType type;
    361             fTypeStack.pop(&type);
    362             value->fType = ToDisplayType(type);
    363             fOperandStack.pop(&value->fOperand);
    364             return true;
    365         }
    366 #endif
    367         if (handleUnbox(value) == false) {
    368             fError = kHandleUnboxFailed;
    369             return false;
    370         }
    371         return convertTo(toType, value);
    372     }
    373     return ConvertTo(this, toType, value);
    374 }
    375 
    376 bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) {
    377     size_t fieldLength = token_length(++script);        // skip dot
    378     if (fieldLength == 0) {
    379         fError = kExpectedFieldName;
    380         return false;
    381     }
    382     const char* field = script;
    383     script += fieldLength;
    384     bool success = handleProperty(suppressed);
    385     if (success == false) {
    386         fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins
    387         return false;
    388     }
    389     return evaluateDotParam(script, suppressed, field, fieldLength);
    390 }
    391 
    392 bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed,
    393         const char* field, size_t fieldLength) {
    394     void* object;
    395     if (suppressed)
    396         object = NULL;
    397     else {
    398         if (fTypeStack.top() != kObject) {
    399             fError = kDotOperatorExpectsObject;
    400             return false;
    401         }
    402         object = fOperandStack.top().fObject;
    403         fTypeStack.pop();
    404         fOperandStack.pop();
    405     }
    406     char ch; // see if it is a simple member or a function
    407     while (is_ws(ch = script[0]))
    408         script++;
    409     bool success = true;
    410     if (ch != '(') {
    411             if (suppressed == false) {
    412                 if ((success = handleMember(field, fieldLength, object)) == false)
    413                     fError = kHandleMemberFailed;
    414             }
    415     } else {
    416         SkTDArray<SkScriptValue> params;
    417         *fBraceStack.push() = kFunctionBrace;
    418         success = functionParams(&script, params);
    419         if (success && suppressed == false &&
    420                 (success = handleMemberFunction(field, fieldLength, object, params)) == false)
    421             fError = kHandleMemberFunctionFailed;
    422     }
    423     return success;
    424 }
    425 
    426 bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) {
    427 #ifdef SK_DEBUG
    428     const char** original = scriptPtr;
    429 #endif
    430     bool success;
    431     const char* inner;
    432     if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
    433         *scriptPtr += sizeof("#script:") - 1;
    434         if (fReturnType == kNoType || fReturnType == kString) {
    435             success = innerScript(scriptPtr, value);
    436             if (success == false)
    437                 goto end;
    438             inner = value->fOperand.fString->c_str();
    439             scriptPtr = &inner;
    440         }
    441     }
    442     {
    443         success = innerScript(scriptPtr, value);
    444         if (success == false)
    445             goto end;
    446         const char* script = *scriptPtr;
    447         char ch;
    448         while (is_ws(ch = script[0]))
    449             script++;
    450         if (ch != '\0') {
    451             // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
    452             fError = kPrematureEnd;
    453             success = false;
    454         }
    455     }
    456 end:
    457 #ifdef SK_DEBUG
    458     if (success == false) {
    459         SkDebugf("script failed: %s", *original);
    460         if (fError)
    461             SkDebugf(" %s", errorStrings[fError - 1]);
    462         SkDebugf("\n");
    463     }
    464 #endif
    465     return success;
    466 }
    467 
    468 void SkScriptEngine::forget(SkTypedArray* array) {
    469     if (array->getType() == SkType_String) {
    470         for (int index = 0; index < array->count(); index++) {
    471             SkString* string = (*array)[index].fString;
    472             int found = fTrackString.find(string);
    473             if (found >= 0)
    474                 fTrackString.remove(found);
    475         }
    476         return;
    477     }
    478     if (array->getType() == SkType_Array) {
    479         for (int index = 0; index < array->count(); index++) {
    480             SkTypedArray* child = (*array)[index].fArray;
    481             forget(child);  // forgets children of child
    482             int found = fTrackArray.find(child);
    483             if (found >= 0)
    484                 fTrackArray.remove(found);
    485         }
    486     }
    487 }
    488 
    489 void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) {
    490     UserCallBack callBack;
    491     callBack.fFunctionCallBack = func;
    492     commonCallBack(kFunction, callBack, userStorage);
    493 }
    494 
    495 bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) {
    496     (*scriptPtr)++; // skip open paren
    497     *fOpStack.push() = kParen;
    498     *fBraceStack.push() = kFunctionBrace;
    499     SkBool suppressed = fSuppressStack.top().fSuppress;
    500     do {
    501         SkScriptValue value;
    502         bool success = innerScript(scriptPtr, suppressed ? NULL : &value);
    503         if (success == false) {
    504             fError = kErrorInFunctionParameters;
    505             return false;
    506         }
    507         if (suppressed)
    508             continue;
    509         *params.append() = value;
    510     } while ((*scriptPtr)[-1] == ',');
    511     fBraceStack.pop();
    512     fOpStack.pop(); // pop paren
    513     (*scriptPtr)++; // advance beyond close paren
    514     return true;
    515 }
    516 
    517 #ifdef SK_DEBUG
    518 bool SkScriptEngine::getErrorString(SkString* str) const {
    519     if (fError)
    520         str->set(errorStrings[fError - 1]);
    521     return fError != 0;
    522 }
    523 #endif
    524 
    525 bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) {
    526     const char* script = *scriptPtr;
    527     char ch;
    528     bool lastPush = false;
    529     bool success = true;
    530     int opBalance = fOpStack.count();
    531     int baseBrace = fBraceStack.count();
    532     int suppressBalance = fSuppressStack.count();
    533     while ((ch = script[0]) != '\0') {
    534         if (is_ws(ch)) {
    535             script++;
    536             continue;
    537         }
    538         SkBool suppressed = fSuppressStack.top().fSuppress;
    539         SkOperand operand;
    540         const char* dotCheck;
    541         if (fBraceStack.count() > baseBrace) {
    542 #if 0   // disable support for struct brace
    543             if (ch == ':') {
    544                 SkASSERT(fTokenLength > 0);
    545                 SkASSERT(fBraceStack.top() == kStructBrace);
    546                 ++script;
    547                 SkASSERT(fDisplayable);
    548                 SkString token(fToken, fTokenLength);
    549                 fTokenLength = 0;
    550                 const char* tokenName = token.c_str();
    551                 const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING;
    552                 if (suppressed == false) {
    553                     SkDisplayTypes type = fInfo->getType();
    554                     tokenInfo = SkDisplayType::GetMember(type, &tokenName);
    555                     SkASSERT(tokenInfo);
    556                 }
    557                 SkScriptValue tokenValue;
    558                 success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
    559                 SkASSERT(success);
    560                 if (suppressed == false) {
    561                     if (tokenValue.fType == SkType_Displayable) {
    562                         SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType()));
    563                         fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable);
    564                     } else {
    565                         if (tokenValue.fType != tokenInfo->getType()) {
    566                             if (convertTo(tokenInfo->getType(), &tokenValue) == false)
    567                                 return false;
    568                         }
    569                         tokenInfo->writeValue(fDisplayable, NULL, 0, 0,
    570                             (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset),
    571                             tokenInfo->getType(), tokenValue);
    572                     }
    573                 }
    574                 lastPush = false;
    575                 continue;
    576             } else
    577 #endif
    578             if (fBraceStack.top() == kArrayBrace) {
    579                 SkScriptValue tokenValue;
    580                 success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
    581                 if (success == false) {
    582                     fError = kErrorInArrrayIndex;
    583                     return false;
    584                 }
    585                 if (suppressed == false) {
    586 #if 0 // no support for structures for now
    587                     if (tokenValue.fType == SkType_Structure) {
    588                         fArrayOffset += (int) fInfo->getSize(fDisplayable);
    589                     } else
    590 #endif
    591                     {
    592                         SkDisplayTypes type = ToDisplayType(fReturnType);
    593                         if (fReturnType == kNoType) {
    594                             // !!! short sighted; in the future, allow each returned array component to carry
    595                             // its own type, and let caller do any needed conversions
    596                             if (value->fOperand.fArray->count() == 0)
    597                                 value->fOperand.fArray->setType(type = tokenValue.fType);
    598                             else
    599                                 type = value->fOperand.fArray->getType();
    600                         }
    601                         if (tokenValue.fType != type) {
    602                             if (convertTo(type, &tokenValue) == false)
    603                                 return false;
    604                         }
    605                         *value->fOperand.fArray->append() = tokenValue.fOperand;
    606                     }
    607                 }
    608                 lastPush = false;
    609                 continue;
    610             } else {
    611                 if (token_length(script) == 0) {
    612                     fError = kExpectedToken;
    613                     return false;
    614                 }
    615             }
    616         }
    617         if (lastPush != false && fTokenLength > 0) {
    618             if (ch == '(') {
    619                 *fBraceStack.push() = kFunctionBrace;
    620                 if (handleFunction(&script, SkToBool(suppressed)) == false)
    621                     return false;
    622                 lastPush = true;
    623                 continue;
    624             } else if (ch == '[') {
    625                 if (handleProperty(SkToBool(suppressed)) == false)
    626                     return false;   // note: never triggered by standard animator plugins
    627                 if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
    628                     return false;
    629                 lastPush = true;
    630                 continue;
    631             } else if (ch != '.') {
    632                 if (handleProperty(SkToBool(suppressed)) == false)
    633                     return false;   // note: never triggered by standard animator plugins
    634                 lastPush = true;
    635                 continue;
    636             }
    637         }
    638         if (ch == '0' && (script[1] & ~0x20) == 'X') {
    639             if (lastPush != false) {
    640                 fError = kExpectedOperator;
    641                 return false;
    642             }
    643             script += 2;
    644             script = SkParse::FindHex(script, (uint32_t*)&operand.fS32);
    645             if (script == NULL) {
    646                 fError = kExpectedHex;
    647                 return false;
    648             }
    649             goto intCommon;
    650         }
    651         if (lastPush == false && ch == '.')
    652             goto scalarCommon;
    653         if (ch >= '0' && ch <= '9') {
    654             if (lastPush != false) {
    655                 fError = kExpectedOperator;
    656                 return false;
    657             }
    658             dotCheck = SkParse::FindS32(script, &operand.fS32);
    659             if (dotCheck[0] != '.') {
    660                 script = dotCheck;
    661 intCommon:
    662                 if (suppressed == false)
    663                     *fTypeStack.push() = kInt;
    664             } else {
    665 scalarCommon:
    666                 script = SkParse::FindScalar(script, &operand.fScalar);
    667                 if (suppressed == false)
    668                     *fTypeStack.push() = kScalar;
    669             }
    670             if (suppressed == false)
    671                 fOperandStack.push(operand);
    672             lastPush = true;
    673             continue;
    674         }
    675         int length = token_length(script);
    676         if (length > 0) {
    677             if (lastPush != false) {
    678                 fError = kExpectedOperator;
    679                 return false;
    680             }
    681             fToken = script;
    682             fTokenLength = length;
    683             script += length;
    684             lastPush = true;
    685             continue;
    686         }
    687         char startQuote = ch;
    688         if (startQuote == '\'' || startQuote == '\"') {
    689             if (lastPush != false) {
    690                 fError = kExpectedOperator;
    691                 return false;
    692             }
    693             operand.fString = new SkString();
    694             track(operand.fString);
    695             ++script;
    696 
    697             // <mrr> this is a lot of calls to append() one char at at time
    698             // how hard to preflight script so we know how much to grow fString by?
    699             do {
    700                 if (script[0] == '\\')
    701                     ++script;
    702                 operand.fString->append(script, 1);
    703                 ++script;
    704                 if (script[0] == '\0') {
    705                     fError = kUnterminatedString;
    706                     return false;
    707                 }
    708             } while (script[0] != startQuote);
    709             ++script;
    710             if (suppressed == false) {
    711                 *fTypeStack.push() = kString;
    712                 fOperandStack.push(operand);
    713             }
    714             lastPush = true;
    715             continue;
    716         }
    717         ;
    718         if (ch ==  '.') {
    719             if (fTokenLength == 0) {
    720                 SkScriptValue scriptValue;
    721                 SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
    722                 int tokenLength = token_length(++script);
    723                 const char* token = script;
    724                 script += tokenLength;
    725                 if (suppressed == false) {
    726                     if (fTypeStack.count() == 0) {
    727                         fError = kExpectedTokenBeforeDotOperator;
    728                         return false;
    729                     }
    730                     SkOpType topType;
    731                     fTypeStack.pop(&topType);
    732                     fOperandStack.pop(&scriptValue.fOperand);
    733                     scriptValue.fType = ToDisplayType(topType);
    734                     handleBox(&scriptValue);
    735                 }
    736                 success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength);
    737                 if (success == false)
    738                     return false;
    739                 lastPush = true;
    740                 continue;
    741             }
    742             // get next token, and evaluate immediately
    743             success = evaluateDot(script, SkToBool(suppressed));
    744             if (success == false)
    745                 return false;
    746             lastPush = true;
    747             continue;
    748         }
    749         if (ch == '[') {
    750             if (lastPush == false) {
    751                 script++;
    752                 *fBraceStack.push() = kArrayBrace;
    753                 if (suppressed)
    754                     continue;
    755                 operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType));
    756                 track(value->fOperand.fArray);
    757                 *fTypeStack.push() = (SkOpType) kArray;
    758                 fOperandStack.push(operand);
    759                 continue;
    760             }
    761             if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
    762                 return false;
    763             lastPush = true;
    764             continue;
    765         }
    766 #if 0 // structs not supported for now
    767         if (ch == '{') {
    768             if (lastPush == false) {
    769                 script++;
    770                 *fBraceStack.push() = kStructBrace;
    771                 if (suppressed)
    772                     continue;
    773                 operand.fS32 = 0;
    774                 *fTypeStack.push() = (SkOpType) kStruct;
    775                 fOperandStack.push(operand);
    776                 continue;
    777             }
    778             SkASSERT(0); // braces in other contexts aren't supported yet
    779         }
    780 #endif
    781         if (ch == ')' && fBraceStack.count() > 0) {
    782             SkBraceStyle braceStyle = fBraceStack.top();
    783             if (braceStyle == kFunctionBrace) {
    784                 fBraceStack.pop();
    785                 break;
    786             }
    787         }
    788         if (ch == ',' || ch == ']') {
    789             if (ch != ',') {
    790                 SkBraceStyle match;
    791                 fBraceStack.pop(&match);
    792                 if (match != kArrayBrace) {
    793                     fError = kMismatchedArrayBrace;
    794                     return false;
    795                 }
    796             }
    797             script++;
    798             // !!! see if brace or bracket is correct closer
    799             break;
    800         }
    801         char nextChar = script[1];
    802         int advance = logicalOp(ch, nextChar);
    803         if (advance < 0)     // error
    804             return false;
    805         if (advance == 0)
    806             advance = arithmeticOp(ch, nextChar, lastPush);
    807         if (advance == 0) // unknown token
    808             return false;
    809         if (advance > 0)
    810             script += advance;
    811         lastPush = ch == ']' || ch == ')';
    812     }
    813     bool suppressed = SkToBool(fSuppressStack.top().fSuppress);
    814     if (fTokenLength > 0) {
    815         success = handleProperty(suppressed);
    816         if (success == false)
    817             return false;   // note: never triggered by standard animator plugins
    818     }
    819     while (fOpStack.count() > opBalance) {   // leave open paren
    820         if ((fError = opError()) != kNoError)
    821             return false;
    822         if (processOp() == false)
    823             return false;
    824     }
    825     SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType;
    826     if (suppressed == false && topType != fReturnType &&
    827             topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value
    828         SkString* string = fOperandStack.top().fString;
    829         fToken = string->c_str();
    830         fTokenLength = string->size();
    831         fOperandStack.pop();
    832         fTypeStack.pop();
    833         success = handleProperty(SkToBool(fSuppressStack.top().fSuppress));
    834         if (success == false) { // if it couldn't convert, return string (error?)
    835             SkOperand operand;
    836             operand.fS32 = 0;
    837             *fTypeStack.push() = kString;
    838             operand.fString = string;
    839             fOperandStack.push(operand);
    840         }
    841     }
    842     if (value) {
    843         if (fOperandStack.count() == 0)
    844             return false;
    845         SkASSERT(fOperandStack.count() >= 1);
    846         SkASSERT(fTypeStack.count() >= 1);
    847         fOperandStack.pop(&value->fOperand);
    848         SkOpType type;
    849         fTypeStack.pop(&type);
    850         value->fType = ToDisplayType(type);
    851 //      SkASSERT(value->fType != SkType_Unknown);
    852         if (topType != fReturnType && topType == kObject && fReturnType != kNoType) {
    853             if (convertTo(ToDisplayType(fReturnType), value) == false)
    854                 return false;
    855         }
    856     }
    857     while (fSuppressStack.count() > suppressBalance)
    858         fSuppressStack.pop();
    859     *scriptPtr = script;
    860     return true; // no error
    861 }
    862 
    863 void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) {
    864     UserCallBack callBack;
    865     callBack.fMemberCallBack = member;
    866     commonCallBack(kMember, callBack, userStorage);
    867 }
    868 
    869 void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) {
    870     UserCallBack callBack;
    871     callBack.fMemberFunctionCallBack = func;
    872     commonCallBack(kMemberFunction, callBack, userStorage);
    873 }
    874 
    875 #if 0
    876 void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) {
    877     UserCallBack callBack;
    878     callBack.fObjectToStringCallBack = func;
    879     commonCallBack(kObjectToString, callBack, userStorage);
    880 }
    881 #endif
    882 
    883 bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) {
    884     SkScriptValue scriptValue;
    885     (*scriptPtr)++;
    886     *fOpStack.push() = kParen;
    887     *fBraceStack.push() = kArrayBrace;
    888     SkOpType saveType = fReturnType;
    889     fReturnType = kInt;
    890     bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL);
    891     if (success == false)
    892         return false;
    893     fReturnType = saveType;
    894     if (suppressed == false) {
    895         if (convertTo(SkType_Int, &scriptValue) == false)
    896             return false;
    897         int index = scriptValue.fOperand.fS32;
    898         SkScriptValue scriptValue;
    899         SkOpType type;
    900         fTypeStack.pop(&type);
    901         fOperandStack.pop(&scriptValue.fOperand);
    902         scriptValue.fType = ToDisplayType(type);
    903         if (type == kObject) {
    904             success = handleUnbox(&scriptValue);
    905             if (success == false)
    906                 return false;
    907             if (ToOpType(scriptValue.fType) != kArray) {
    908                 fError = kExpectedArray;
    909                 return false;
    910             }
    911         }
    912         *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType();
    913 //      SkASSERT(index >= 0);
    914         if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
    915             fError = kArrayIndexOutOfBounds;
    916             return false;
    917         }
    918         scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
    919         fOperandStack.push(scriptValue.fOperand);
    920     }
    921     fOpStack.pop(); // pop paren
    922     return success;
    923 }
    924 
    925 bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) {
    926     bool success = true;
    927     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
    928         if (callBack->fCallBackType != kBox)
    929             continue;
    930         success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue);
    931         if (success) {
    932             fOperandStack.push(scriptValue->fOperand);
    933             *fTypeStack.push() = ToOpType(scriptValue->fType);
    934             goto done;
    935         }
    936     }
    937 done:
    938     return success;
    939 }
    940 
    941 bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) {
    942     SkScriptValue callbackResult;
    943     SkTDArray<SkScriptValue> params;
    944     SkString functionName(fToken, fTokenLength);
    945     fTokenLength = 0;
    946     bool success = functionParams(scriptPtr, params);
    947     if (success == false)
    948         goto done;
    949     if (suppressed == true)
    950         return true;
    951     {
    952         for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
    953             if (callBack->fCallBackType != kFunction)
    954                 continue;
    955             success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params,
    956                 callBack->fUserStorage, &callbackResult);
    957             if (success) {
    958                 fOperandStack.push(callbackResult.fOperand);
    959                 *fTypeStack.push() = ToOpType(callbackResult.fType);
    960                 goto done;
    961             }
    962         }
    963     }
    964     fError = kNoFunctionHandlerFound;
    965     return false;
    966 done:
    967     return success;
    968 }
    969 
    970 bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) {
    971     SkScriptValue callbackResult;
    972     bool success = true;
    973     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
    974         if (callBack->fCallBackType != kMember)
    975             continue;
    976         success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult);
    977         if (success) {
    978             if (callbackResult.fType == SkType_String)
    979                 track(callbackResult.fOperand.fString);
    980             fOperandStack.push(callbackResult.fOperand);
    981             *fTypeStack.push() = ToOpType(callbackResult.fType);
    982             goto done;
    983         }
    984     }
    985     return false;
    986 done:
    987     return success;
    988 }
    989 
    990 bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) {
    991     SkScriptValue callbackResult;
    992     bool success = true;
    993     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
    994         if (callBack->fCallBackType != kMemberFunction)
    995             continue;
    996         success = (*callBack->fMemberFunctionCallBack)(field, len, object, params,
    997             callBack->fUserStorage, &callbackResult);
    998         if (success) {
    999             if (callbackResult.fType == SkType_String)
   1000                 track(callbackResult.fOperand.fString);
   1001             fOperandStack.push(callbackResult.fOperand);
   1002             *fTypeStack.push() = ToOpType(callbackResult.fType);
   1003             goto done;
   1004         }
   1005     }
   1006     return false;
   1007 done:
   1008     return success;
   1009 }
   1010 
   1011 #if 0
   1012 bool SkScriptEngine::handleObjectToString(void* object) {
   1013     SkScriptValue callbackResult;
   1014     bool success = true;
   1015     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1016         if (callBack->fCallBackType != kObjectToString)
   1017             continue;
   1018         success = (*callBack->fObjectToStringCallBack)(object,
   1019             callBack->fUserStorage, &callbackResult);
   1020         if (success) {
   1021             if (callbackResult.fType == SkType_String)
   1022                 track(callbackResult.fOperand.fString);
   1023             fOperandStack.push(callbackResult.fOperand);
   1024             *fTypeStack.push() = ToOpType(callbackResult.fType);
   1025             goto done;
   1026         }
   1027     }
   1028     return false;
   1029 done:
   1030     return success;
   1031 }
   1032 #endif
   1033 
   1034 bool SkScriptEngine::handleProperty(bool suppressed) {
   1035     SkScriptValue callbackResult;
   1036     bool success = true;
   1037     if (suppressed)
   1038         goto done;
   1039     success = false; // note that with standard animator-script plugins, callback never returns false
   1040     {
   1041         for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1042             if (callBack->fCallBackType != kProperty)
   1043                 continue;
   1044             success = (*callBack->fPropertyCallBack)(fToken, fTokenLength,
   1045                 callBack->fUserStorage, &callbackResult);
   1046             if (success) {
   1047                 if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) {
   1048                     callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
   1049                     track(callbackResult.fOperand.fString);
   1050                 }
   1051                 fOperandStack.push(callbackResult.fOperand);
   1052                 *fTypeStack.push() = ToOpType(callbackResult.fType);
   1053                 goto done;
   1054             }
   1055         }
   1056     }
   1057 done:
   1058     fTokenLength = 0;
   1059     return success;
   1060 }
   1061 
   1062 bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) {
   1063     bool success = true;
   1064     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1065         if (callBack->fCallBackType != kUnbox)
   1066             continue;
   1067         success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue);
   1068         if (success) {
   1069             if (scriptValue->fType == SkType_String)
   1070                 track(scriptValue->fOperand.fString);
   1071             goto done;
   1072         }
   1073     }
   1074     return false;
   1075 done:
   1076     return success;
   1077 }
   1078 
   1079 // note that entire expression is treated as if it were enclosed in parens
   1080 // an open paren is always the first thing in the op stack
   1081 
   1082 int SkScriptEngine::logicalOp(char ch, char nextChar) {
   1083     int advance = 1;
   1084     SkOp match;
   1085     signed char precedence;
   1086     switch (ch) {
   1087         case ')':
   1088             match = kParen;
   1089             break;
   1090         case ']':
   1091             match = kArrayOp;
   1092             break;
   1093         case '?':
   1094             match = kIf;
   1095             break;
   1096         case ':':
   1097             match = kElse;
   1098             break;
   1099         case '&':
   1100             if (nextChar != '&')
   1101                 goto noMatch;
   1102             match = kLogicalAnd;
   1103             advance = 2;
   1104             break;
   1105         case '|':
   1106             if (nextChar != '|')
   1107                 goto noMatch;
   1108             match = kLogicalOr;
   1109             advance = 2;
   1110             break;
   1111         default:
   1112 noMatch:
   1113             return 0;
   1114     }
   1115     SkSuppress suppress;
   1116     precedence = gPrecedence[match];
   1117     if (fSuppressStack.top().fSuppress) {
   1118         if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) {
   1119             SkOp topOp = fOpStack.top();
   1120             if (gPrecedence[topOp] <= precedence)
   1121                 fOpStack.pop();
   1122             goto goHome;
   1123         }
   1124         bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence;
   1125         if (changedPrecedence)
   1126             fSuppressStack.pop();
   1127         if (precedence == kIfElsePrecedence) {
   1128             if (match == kIf) {
   1129                 if (changedPrecedence)
   1130                     fOpStack.pop();
   1131                 else
   1132                     *fOpStack.push() = kIf;
   1133             } else {
   1134                 if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) {
   1135                     goto flipSuppress;
   1136                 }
   1137                 fOpStack.pop();
   1138             }
   1139         }
   1140         if (changedPrecedence == false)
   1141             goto goHome;
   1142     }
   1143     while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) {
   1144         if (processOp() == false)
   1145             return false;
   1146     }
   1147     if (fSuppressStack.top().fOpStackDepth > fOpStack.count())
   1148         fSuppressStack.pop();
   1149     switch (match) {
   1150         case kParen:
   1151         case kArrayOp:
   1152             if (fOpStack.count() <= 1 || fOpStack.top() != match) {
   1153                 fError = kMismatchedBrackets;
   1154                 return -1;
   1155             }
   1156             if (match == kParen)
   1157                 fOpStack.pop();
   1158             else {
   1159                 SkOpType indexType;
   1160                 fTypeStack.pop(&indexType);
   1161                 if (indexType != kInt && indexType != kScalar) {
   1162                     fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually)
   1163                     return -1;
   1164                 }
   1165                 SkOperand indexOperand;
   1166                 fOperandStack.pop(&indexOperand);
   1167                 int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) :
   1168                     indexOperand.fS32;
   1169                 SkOpType arrayType;
   1170                 fTypeStack.pop(&arrayType);
   1171                 if ((unsigned)arrayType != (unsigned)kArray) {
   1172                     fError = kExpectedArray;
   1173                     return -1;
   1174                 }
   1175                 SkOperand arrayOperand;
   1176                 fOperandStack.pop(&arrayOperand);
   1177                 SkTypedArray* array = arrayOperand.fArray;
   1178                 SkOperand operand;
   1179                 if (array->getIndex(index, &operand) == false) {
   1180                     fError = kIndexOutOfRange;
   1181                     return -1;
   1182                 }
   1183                 SkOpType resultType = array->getOpType();
   1184                 fTypeStack.push(resultType);
   1185                 fOperandStack.push(operand);
   1186             }
   1187             break;
   1188         case kIf: {
   1189             SkScriptValue ifValue;
   1190             SkOpType ifType;
   1191             fTypeStack.pop(&ifType);
   1192             ifValue.fType = ToDisplayType(ifType);
   1193             fOperandStack.pop(&ifValue.fOperand);
   1194             if (convertTo(SkType_Int, &ifValue) == false)
   1195                 return -1;
   1196             if (ifValue.fType != SkType_Int) {
   1197                 fError = kExpectedIntForConditionOperator;
   1198                 return -1;
   1199             }
   1200             suppress.fSuppress = ifValue.fOperand.fS32 == 0;
   1201             suppress.fOperator = kIf;
   1202             suppress.fOpStackDepth = fOpStack.count();
   1203             suppress.fElse = false;
   1204             fSuppressStack.push(suppress);
   1205             // if left is true, do only up to colon
   1206             // if left is false, do only after colon
   1207             } break;
   1208         case kElse:
   1209 flipSuppress:
   1210             if (fSuppressStack.top().fElse == true)
   1211                 fSuppressStack.pop();
   1212             fSuppressStack.top().fElse = true;
   1213             fSuppressStack.top().fSuppress ^= true;
   1214             // flip last do / don't do consideration from last '?'
   1215             break;
   1216         case kLogicalAnd:
   1217         case kLogicalOr: {
   1218             if (fTypeStack.top() != kInt) {
   1219                 fError = kExpectedBooleanExpression;
   1220                 return -1;
   1221             }
   1222             int32_t topInt = fOperandStack.top().fS32;
   1223             if (fOpStack.top() != kLogicalAnd)
   1224                 *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or'
   1225             if (match == kLogicalOr ? topInt != 0 : topInt == 0) {
   1226                 suppress.fSuppress = true;
   1227                 suppress.fOperator = match;
   1228                 suppress.fOpStackDepth = fOpStack.count();
   1229                 fSuppressStack.push(suppress);
   1230             } else {
   1231                 fTypeStack.pop();
   1232                 fOperandStack.pop();
   1233             }
   1234         }   break;
   1235         default:
   1236             SkASSERT(0);
   1237     }
   1238 goHome:
   1239     return advance;
   1240 }
   1241 
   1242 SkScriptEngine::Error SkScriptEngine::opError() {
   1243     int opCount = fOpStack.count();
   1244     int operandCount = fOperandStack.count();
   1245     if (opCount == 0) {
   1246         if (operandCount != 1)
   1247             return kExpectedOperator;
   1248         return kNoError;
   1249     }
   1250     SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp);
   1251     const SkOperatorAttributes* attributes = &gOpAttributes[op];
   1252     if (attributes->fLeftType != kNoType && operandCount < 2)
   1253         return kExpectedValue;
   1254     if (attributes->fLeftType == kNoType && operandCount < 1)
   1255         return kExpectedValue;
   1256     return kNoError;
   1257 }
   1258 
   1259 bool SkScriptEngine::processOp() {
   1260     SkOp op;
   1261     fOpStack.pop(&op);
   1262     op = (SkOp) (op & ~kArtificialOp);
   1263     const SkOperatorAttributes* attributes = &gOpAttributes[op];
   1264     SkOpType type2;
   1265     fTypeStack.pop(&type2);
   1266     SkOpType type1 = type2;
   1267     SkOperand operand2;
   1268     fOperandStack.pop(&operand2);
   1269     SkOperand operand1 = operand2; // !!! not really needed, suppresses warning
   1270     if (attributes->fLeftType != kNoType) {
   1271         fTypeStack.pop(&type1);
   1272         fOperandStack.pop(&operand1);
   1273         if (op == kFlipOps) {
   1274             SkTSwap(type1, type2);
   1275             SkTSwap(operand1, operand2);
   1276             fOpStack.pop(&op);
   1277             op = (SkOp) (op & ~kArtificialOp);
   1278             attributes = &gOpAttributes[op];
   1279         }
   1280         if (type1 == kObject && (type1 & attributes->fLeftType) == 0) {
   1281             SkScriptValue val;
   1282             val.fType = ToDisplayType(type1);
   1283             val.fOperand = operand1;
   1284             bool success = handleUnbox(&val);
   1285             if (success == false)
   1286                 return false;
   1287             type1 = ToOpType(val.fType);
   1288             operand1 = val.fOperand;
   1289         }
   1290     }
   1291     if (type2 == kObject && (type2 & attributes->fLeftType) == 0) {
   1292         SkScriptValue val;
   1293         val.fType = ToDisplayType(type2);
   1294         val.fOperand = operand2;
   1295         bool success = handleUnbox(&val);
   1296         if (success == false)
   1297             return false;
   1298         type2 = ToOpType(val.fType);
   1299         operand2 = val.fOperand;
   1300     }
   1301     if (attributes->fLeftType != kNoType) {
   1302         if (type1 != type2) {
   1303             if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) {
   1304                 if (type1 == kInt || type1 == kScalar) {
   1305                     convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float);
   1306                     type1 = kString;
   1307                 }
   1308                 if (type2 == kInt || type2 == kScalar) {
   1309                     convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float);
   1310                     type2 = kString;
   1311                 }
   1312             } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) {
   1313                 if (type1 == kInt) {
   1314                     operand1.fScalar = IntToScalar(operand1.fS32);
   1315                     type1 = kScalar;
   1316                 }
   1317                 if (type2 == kInt) {
   1318                     operand2.fScalar = IntToScalar(operand2.fS32);
   1319                      type2 = kScalar;
   1320                 }
   1321             }
   1322         }
   1323         if ((type1 & attributes->fLeftType) == 0 || type1 != type2) {
   1324             if (type1 == kString) {
   1325                 const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar);
   1326                 if (result == NULL) {
   1327                     fError = kExpectedNumber;
   1328                     return false;
   1329                 }
   1330                 type1 = kScalar;
   1331             }
   1332             if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) {
   1333                 operand1.fS32 = SkScalarFloor(operand1.fScalar);
   1334                 type1 = kInt;
   1335             }
   1336         }
   1337     }
   1338     if ((type2 & attributes->fRightType) == 0 || type1 != type2) {
   1339         if (type2 == kString) {
   1340             const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar);
   1341             if (result == NULL) {
   1342                 fError = kExpectedNumber;
   1343                 return false;
   1344             }
   1345             type2 = kScalar;
   1346         }
   1347         if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) {
   1348             operand2.fS32 = SkScalarFloor(operand2.fScalar);
   1349             type2 = kInt;
   1350         }
   1351     }
   1352     if (type2 == kScalar)
   1353         op = (SkOp) (op + 1);
   1354     else if (type2 == kString)
   1355         op = (SkOp) (op + 2);
   1356     switch(op) {
   1357         case kAddInt:
   1358             operand2.fS32 += operand1.fS32;
   1359             break;
   1360         case kAddScalar:
   1361             operand2.fScalar += operand1.fScalar;
   1362             break;
   1363         case kAddString:
   1364             if (fTrackString.find(operand1.fString) < 0) {
   1365                 operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString));
   1366                 track(operand1.fString);
   1367             }
   1368             operand1.fString->append(*operand2.fString);
   1369             operand2 = operand1;
   1370             break;
   1371         case kBitAnd:
   1372             operand2.fS32 &= operand1.fS32;
   1373             break;
   1374         case kBitNot:
   1375             operand2.fS32 = ~operand2.fS32;
   1376             break;
   1377         case kBitOr:
   1378             operand2.fS32 |= operand1.fS32;
   1379             break;
   1380         case kDivideInt:
   1381             if (operand2.fS32 == 0) {
   1382                 operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
   1383                 break;
   1384             } else {
   1385                 int32_t original = operand2.fS32;
   1386                 operand2.fS32 = operand1.fS32 / operand2.fS32;
   1387                 if (original * operand2.fS32 == operand1.fS32)
   1388                     break;    // integer divide was good enough
   1389                 operand2.fS32 = original;
   1390                 type2 = kScalar;
   1391             }
   1392         case kDivideScalar:
   1393             if (operand2.fScalar == 0)
   1394                 operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
   1395             else
   1396                 operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar);
   1397             break;
   1398         case kEqualInt:
   1399             operand2.fS32 = operand1.fS32 == operand2.fS32;
   1400             break;
   1401         case kEqualScalar:
   1402             operand2.fS32 = operand1.fScalar == operand2.fScalar;
   1403             type2 = kInt;
   1404             break;
   1405         case kEqualString:
   1406             operand2.fS32 = *operand1.fString == *operand2.fString;
   1407             type2 = kInt;
   1408             break;
   1409         case kGreaterEqualInt:
   1410             operand2.fS32 = operand1.fS32 >= operand2.fS32;
   1411             break;
   1412         case kGreaterEqualScalar:
   1413             operand2.fS32 = operand1.fScalar >= operand2.fScalar;
   1414             type2 = kInt;
   1415             break;
   1416         case kGreaterEqualString:
   1417             operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0;
   1418             type2 = kInt;
   1419             break;
   1420         case kLogicalAnd:
   1421             operand2.fS32 = !! operand2.fS32;   // really, ToBool
   1422             break;
   1423         case kLogicalNot:
   1424             operand2.fS32 = ! operand2.fS32;
   1425             break;
   1426         case kLogicalOr:
   1427             SkASSERT(0);    // should have already been processed
   1428             break;
   1429         case kMinusInt:
   1430             operand2.fS32 = -operand2.fS32;
   1431             break;
   1432         case kMinusScalar:
   1433             operand2.fScalar = -operand2.fScalar;
   1434             break;
   1435         case kModuloInt:
   1436             operand2.fS32 = operand1.fS32 % operand2.fS32;
   1437             break;
   1438         case kModuloScalar:
   1439             operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar);
   1440             break;
   1441         case kMultiplyInt:
   1442             operand2.fS32 *= operand1.fS32;
   1443             break;
   1444         case kMultiplyScalar:
   1445             operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar);
   1446             break;
   1447         case kShiftLeft:
   1448             operand2.fS32 = operand1.fS32 << operand2.fS32;
   1449             break;
   1450         case kShiftRight:
   1451             operand2.fS32 = operand1.fS32 >> operand2.fS32;
   1452             break;
   1453         case kSubtractInt:
   1454             operand2.fS32 = operand1.fS32 - operand2.fS32;
   1455             break;
   1456         case kSubtractScalar:
   1457             operand2.fScalar = operand1.fScalar - operand2.fScalar;
   1458             break;
   1459         case kXor:
   1460             operand2.fS32 ^= operand1.fS32;
   1461             break;
   1462         default:
   1463             SkASSERT(0);
   1464     }
   1465     fTypeStack.push(type2);
   1466     fOperandStack.push(operand2);
   1467     return true;
   1468 }
   1469 
   1470 void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) {
   1471     UserCallBack callBack;
   1472     callBack.fPropertyCallBack = prop;
   1473     commonCallBack(kProperty, callBack, userStorage);
   1474 }
   1475 
   1476 void SkScriptEngine::track(SkTypedArray* array) {
   1477     SkASSERT(fTrackArray.find(array) < 0);
   1478     *(fTrackArray.end() - 1) = array;
   1479     fTrackArray.appendClear();
   1480 }
   1481 
   1482 void SkScriptEngine::track(SkString* string) {
   1483     SkASSERT(fTrackString.find(string) < 0);
   1484     *(fTrackString.end() - 1) = string;
   1485     fTrackString.appendClear();
   1486 }
   1487 
   1488 void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) {
   1489     UserCallBack callBack;
   1490     callBack.fUnboxCallBack = func;
   1491     commonCallBack(kUnbox, callBack, userStorage);
   1492 }
   1493 
   1494 bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) {
   1495     SkASSERT(value);
   1496     if (SkDisplayType::IsEnum(NULL /* fMaker */, toType))
   1497         toType = SkType_Int;
   1498     if (toType == SkType_Point || toType == SkType_3D_Point)
   1499         toType = SkType_Float;
   1500     if (toType == SkType_Drawable)
   1501         toType = SkType_Displayable;
   1502     SkDisplayTypes type = value->fType;
   1503     if (type == toType)
   1504         return true;
   1505     SkOperand& operand = value->fOperand;
   1506     bool success = true;
   1507     switch (toType) {
   1508         case SkType_Int:
   1509             if (type == SkType_Boolean)
   1510                 break;
   1511             if (type == SkType_Float)
   1512                 operand.fS32 = SkScalarFloor(operand.fScalar);
   1513             else {
   1514                 if (type != SkType_String) {
   1515                     success = false;
   1516                     break; // error
   1517                 }
   1518                 success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
   1519             }
   1520             break;
   1521         case SkType_Float:
   1522             if (type == SkType_Int) {
   1523                 if ((uint32_t)operand.fS32 == SK_NaN32)
   1524                     operand.fScalar = SK_ScalarNaN;
   1525                 else if (SkAbs32(operand.fS32) == SK_MaxS32)
   1526                     operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
   1527                 else
   1528                     operand.fScalar = SkIntToScalar(operand.fS32);
   1529             } else {
   1530                 if (type != SkType_String) {
   1531                     success = false;
   1532                     break; // error
   1533                 }
   1534                 success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
   1535             }
   1536             break;
   1537         case SkType_String: {
   1538             SkString* strPtr = new SkString();
   1539             SkASSERT(engine);
   1540             engine->track(strPtr);
   1541             if (type == SkType_Int)
   1542                 strPtr->appendS32(operand.fS32);
   1543             else if (type == SkType_Displayable)
   1544                 SkASSERT(0); // must call through instance version instead of static version
   1545             else {
   1546                 if (type != SkType_Float) {
   1547                     success = false;
   1548                     break;
   1549                 }
   1550                 strPtr->appendScalar(operand.fScalar);
   1551             }
   1552             operand.fString = strPtr;
   1553             } break;
   1554         case SkType_Array: {
   1555             SkTypedArray* array = new SkTypedArray(type);
   1556             *array->append() = operand;
   1557             engine->track(array);
   1558             operand.fArray = array;
   1559             } break;
   1560         default:
   1561             SkASSERT(0);
   1562     }
   1563     value->fType = toType;
   1564     if (success == false)
   1565         engine->fError = kTypeConversionFailed;
   1566     return success;
   1567 }
   1568 
   1569 SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
   1570     SkScalar scalar;
   1571     if ((uint32_t)s32 == SK_NaN32)
   1572         scalar = SK_ScalarNaN;
   1573     else if (SkAbs32(s32) == SK_MaxS32)
   1574         scalar = SkSign32(s32) * SK_ScalarMax;
   1575     else
   1576         scalar = SkIntToScalar(s32);
   1577     return scalar;
   1578 }
   1579 
   1580 SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) {
   1581     int val = type;
   1582     switch (val) {
   1583         case kNoType:
   1584             return SkType_Unknown;
   1585         case kInt:
   1586             return SkType_Int;
   1587         case kScalar:
   1588             return SkType_Float;
   1589         case kString:
   1590             return SkType_String;
   1591         case kArray:
   1592             return SkType_Array;
   1593         case kObject:
   1594             return SkType_Displayable;
   1595 //      case kStruct:
   1596 //          return SkType_Structure;
   1597         default:
   1598             SkASSERT(0);
   1599             return SkType_Unknown;
   1600     }
   1601 }
   1602 
   1603 SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) {
   1604     if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
   1605         return (SkOpType) kObject;
   1606     if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
   1607         return kInt;
   1608     switch (type) {
   1609         case SkType_ARGB:
   1610         case SkType_MSec:
   1611         case SkType_Int:
   1612             return kInt;
   1613         case SkType_Float:
   1614         case SkType_Point:
   1615         case SkType_3D_Point:
   1616             return kScalar;
   1617         case SkType_Base64:
   1618         case SkType_DynamicString:
   1619         case SkType_String:
   1620             return kString;
   1621         case SkType_Array:
   1622             return (SkOpType) kArray;
   1623         case SkType_Unknown:
   1624             return kNoType;
   1625         default:
   1626             SkASSERT(0);
   1627             return kNoType;
   1628     }
   1629 }
   1630 
   1631 bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) {
   1632     switch (value.fType) {
   1633         case kInt:
   1634             string->reset();
   1635             string->appendS32(value.fOperand.fS32);
   1636             break;
   1637         case kScalar:
   1638             string->reset();
   1639             string->appendScalar(value.fOperand.fScalar);
   1640             break;
   1641         case kString:
   1642             string->set(*value.fOperand.fString);
   1643             break;
   1644         default:
   1645             SkASSERT(0);
   1646             return false;
   1647     }
   1648     return true; // no error
   1649 }
   1650 
   1651 #ifdef SK_SUPPORT_UNITTEST
   1652 
   1653 #ifdef SK_CAN_USE_FLOAT
   1654     #include "SkFloatingPoint.h"
   1655 #endif
   1656 
   1657 #define DEF_SCALAR_ANSWER   0
   1658 #define DEF_STRING_ANSWER   NULL
   1659 
   1660 #define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
   1661 #ifdef SK_SCALAR_IS_FLOAT
   1662     #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER }
   1663     #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER }
   1664 #else
   1665     #ifdef SK_CAN_USE_FLOAT
   1666         #define testScalar(expression) { #expression, SkType_Float, 0, (int) ((expression) * 65536.0f), DEF_STRING_ANSWER }
   1667         #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, (int) (sk_float_mod(exp1, exp2)  * 65536.0f), DEF_STRING_ANSWER }
   1668     #endif
   1669 #endif
   1670 #define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
   1671 #define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
   1672 
   1673 static const SkScriptNAnswer scriptTests[]  = {
   1674     testInt(1>1/2),
   1675     testInt((6+7)*8),
   1676     testInt(0&&1?2:3),
   1677     testInt(3*(4+5)),
   1678 #ifdef SK_CAN_USE_FLOAT
   1679     testScalar(1.0+2.0),
   1680     testScalar(1.0+5),
   1681     testScalar(3.0-1.0),
   1682     testScalar(6-1.0),
   1683     testScalar(- -5.5- -1.5),
   1684     testScalar(2.5*6.),
   1685     testScalar(0.5*4),
   1686     testScalar(4.5/.5),
   1687     testScalar(9.5/19),
   1688     testRemainder(9.5, 0.5),
   1689     testRemainder(9.,2),
   1690     testRemainder(9,2.5),
   1691     testRemainder(-9,2.5),
   1692     testTrue(-9==-9.0),
   1693     testTrue(-9.==-4.0-5),
   1694     testTrue(-9.*1==-4-5),
   1695     testFalse(-9!=-9.0),
   1696     testFalse(-9.!=-4.0-5),
   1697     testFalse(-9.*1!=-4-5),
   1698 #endif
   1699     testInt(0x123),
   1700     testInt(0XABC),
   1701     testInt(0xdeadBEEF),
   1702     {   "'123'+\"456\"", SkType_String, 0, 0, "123456" },
   1703     {   "123+\"456\"", SkType_String, 0, 0, "123456" },
   1704     {   "'123'+456", SkType_String, 0, 0, "123456" },
   1705     {   "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
   1706     {   "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
   1707     {   "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
   1708     {   "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
   1709     {   "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
   1710     {   "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
   1711     testInt(123),
   1712     testInt(-345),
   1713     testInt(+678),
   1714     testInt(1+2+3),
   1715     testInt(3*4+5),
   1716     testInt(6+7*8),
   1717     testInt(-1-2-8/4),
   1718     testInt(-9%4),
   1719     testInt(9%-4),
   1720     testInt(-9%-4),
   1721     testInt(123|978),
   1722     testInt(123&978),
   1723     testInt(123^978),
   1724     testInt(2<<4),
   1725     testInt(99>>3),
   1726     testInt(~55),
   1727     testInt(~~55),
   1728     testInt(!55),
   1729     testInt(!!55),
   1730     // both int
   1731     testInt(2<2),
   1732     testInt(2<11),
   1733     testInt(20<11),
   1734     testInt(2<=2),
   1735     testInt(2<=11),
   1736     testInt(20<=11),
   1737     testInt(2>2),
   1738     testInt(2>11),
   1739     testInt(20>11),
   1740     testInt(2>=2),
   1741     testInt(2>=11),
   1742     testInt(20>=11),
   1743     testInt(2==2),
   1744     testInt(2==11),
   1745     testInt(20==11),
   1746     testInt(2!=2),
   1747     testInt(2!=11),
   1748     testInt(20!=11),
   1749 #ifdef SK_CAN_USE_FLOAT
   1750     // left int, right scalar
   1751     testInt(2<2.),
   1752     testInt(2<11.),
   1753     testInt(20<11.),
   1754     testInt(2<=2.),
   1755     testInt(2<=11.),
   1756     testInt(20<=11.),
   1757     testInt(2>2.),
   1758     testInt(2>11.),
   1759     testInt(20>11.),
   1760     testInt(2>=2.),
   1761     testInt(2>=11.),
   1762     testInt(20>=11.),
   1763     testInt(2==2.),
   1764     testInt(2==11.),
   1765     testInt(20==11.),
   1766     testInt(2!=2.),
   1767     testInt(2!=11.),
   1768     testInt(20!=11.),
   1769     // left scalar, right int
   1770         testInt(2.<2),
   1771     testInt(2.<11),
   1772     testInt(20.<11),
   1773     testInt(2.<=2),
   1774     testInt(2.<=11),
   1775     testInt(20.<=11),
   1776     testInt(2.>2),
   1777     testInt(2.>11),
   1778     testInt(20.>11),
   1779     testInt(2.>=2),
   1780     testInt(2.>=11),
   1781     testInt(20.>=11),
   1782     testInt(2.==2),
   1783     testInt(2.==11),
   1784     testInt(20.==11),
   1785     testInt(2.!=2),
   1786     testInt(2.!=11),
   1787     testInt(20.!=11),
   1788     // both scalar
   1789     testInt(2.<11.),
   1790     testInt(20.<11.),
   1791     testInt(2.<=2.),
   1792     testInt(2.<=11.),
   1793     testInt(20.<=11.),
   1794     testInt(2.>2.),
   1795     testInt(2.>11.),
   1796     testInt(20.>11.),
   1797     testInt(2.>=2.),
   1798     testInt(2.>=11.),
   1799     testInt(20.>=11.),
   1800     testInt(2.==2.),
   1801     testInt(2.==11.),
   1802     testInt(20.==11.),
   1803     testInt(2.!=2.),
   1804     testInt(2.!=11.),
   1805     testInt(20.!=11.),
   1806 #endif
   1807     // int, string (string is int)
   1808     testFalse(2<'2'),
   1809     testTrue(2<'11'),
   1810     testFalse(20<'11'),
   1811     testTrue(2<='2'),
   1812     testTrue(2<='11'),
   1813     testFalse(20<='11'),
   1814     testFalse(2>'2'),
   1815     testFalse(2>'11'),
   1816     testTrue(20>'11'),
   1817     testTrue(2>='2'),
   1818     testFalse(2>='11'),
   1819     testTrue(20>='11'),
   1820     testTrue(2=='2'),
   1821     testFalse(2=='11'),
   1822     testFalse(2!='2'),
   1823     testTrue(2!='11'),
   1824     // int, string (string is scalar)
   1825     testFalse(2<'2.'),
   1826     testTrue(2<'11.'),
   1827     testFalse(20<'11.'),
   1828     testTrue(2=='2.'),
   1829     testFalse(2=='11.'),
   1830 #ifdef SK_CAN_USE_FLOAT
   1831     // scalar, string
   1832     testFalse(2.<'2.'),
   1833     testTrue(2.<'11.'),
   1834     testFalse(20.<'11.'),
   1835     testTrue(2.=='2.'),
   1836     testFalse(2.=='11.'),
   1837     // string, int
   1838     testFalse('2'<2),
   1839     testTrue('2'<11),
   1840     testFalse('20'<11),
   1841     testTrue('2'==2),
   1842     testFalse('2'==11),
   1843     // string, scalar
   1844     testFalse('2'<2.),
   1845     testTrue('2'<11.),
   1846     testFalse('20'<11.),
   1847     testTrue('2'==2.),
   1848     testFalse('2'==11.),
   1849 #endif
   1850     // string, string
   1851     testFalse('2'<'2'),
   1852     testFalse('2'<'11'),
   1853     testFalse('20'<'11'),
   1854     testTrue('2'=='2'),
   1855     testFalse('2'=='11'),
   1856     // logic
   1857     testInt(1?2:3),
   1858     testInt(0?2:3),
   1859     testInt(1&&2||3),
   1860     testInt(1&&0||3),
   1861     testInt(1&&0||0),
   1862     testInt(1||0&&3),
   1863     testInt(0||0&&3),
   1864     testInt(0||1&&3),
   1865     testInt(1?(2?3:4):5),
   1866     testInt(0?(2?3:4):5),
   1867     testInt(1?(0?3:4):5),
   1868     testInt(0?(0?3:4):5),
   1869     testInt(1?2?3:4:5),
   1870     testInt(0?2?3:4:5),
   1871     testInt(1?0?3:4:5),
   1872     testInt(0?0?3:4:5),
   1873 
   1874     testInt(1?2:(3?4:5)),
   1875     testInt(0?2:(3?4:5)),
   1876     testInt(1?0:(3?4:5)),
   1877     testInt(0?0:(3?4:5)),
   1878     testInt(1?2:3?4:5),
   1879     testInt(0?2:3?4:5),
   1880     testInt(1?0:3?4:5),
   1881     testInt(0?0:3?4:5)
   1882 #ifdef SK_CAN_USE_FLOAT
   1883     , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER }
   1884 #endif
   1885 };
   1886 
   1887 #define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
   1888 
   1889 void SkScriptEngine::UnitTest() {
   1890     for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
   1891         SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType));
   1892         SkScriptValue value;
   1893         const char* script = scriptTests[index].fScript;
   1894         SkASSERT(engine.evaluateScript(&script, &value) == true);
   1895         SkASSERT(value.fType == scriptTests[index].fType);
   1896         SkScalar error;
   1897         switch (value.fType) {
   1898             case SkType_Int:
   1899                 SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
   1900                 break;
   1901             case SkType_Float:
   1902                 error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
   1903                 SkASSERT(error < SK_Scalar1 / 10000);
   1904                 break;
   1905             case SkType_String:
   1906                 SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
   1907                 break;
   1908             default:
   1909                 SkASSERT(0);
   1910         }
   1911     }
   1912 }
   1913 #endif
   1914 
   1915