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