gfx/skia/trunk/src/animator/SkScript.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     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  */
    10 #include "SkScript.h"
    11 #include "SkMath.h"
    12 #include "SkParse.h"
    13 #include "SkString.h"
    14 #include "SkTypedArray.h"
    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 */
    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
    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 };
   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
   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 };
   158 static inline bool is_between(int c, int min, int max)
   159 {
   160     return (unsigned)(c - min) <= (unsigned)(max - min);
   161 }
   163 static inline bool is_ws(int c)
   164 {
   165     return is_between(c, 1, 32);
   166 }
   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 }
   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 }
   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 }
   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 }
   318 void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) {
   319     UserCallBack callBack;
   320     callBack.fBoxCallBack = func;
   321     commonCallBack(kBox, callBack, userStorage);
   322 }
   324 void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) {
   325     callBack.fCallBackType = type;
   326     callBack.fUserStorage = userStorage;
   327     *fUserCallBacks.prepend() = callBack;
   328 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   482 void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) {
   483     UserCallBack callBack;
   484     callBack.fFunctionCallBack = func;
   485     commonCallBack(kFunction, callBack, userStorage);
   486 }
   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 }
   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
   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;
   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 }
   856 void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) {
   857     UserCallBack callBack;
   858     callBack.fMemberCallBack = member;
   859     commonCallBack(kMember, callBack, userStorage);
   860 }
   862 void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) {
   863     UserCallBack callBack;
   864     callBack.fMemberFunctionCallBack = func;
   865     commonCallBack(kMemberFunction, callBack, userStorage);
   866 }
   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
   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 }
   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 }
   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 }
   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 }
   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;
  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;
  1021     return false;
  1022 done:
  1023     return success;
  1025 #endif
  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
  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);
  1044                 fOperandStack.push(callbackResult.fOperand);
  1045                 *fTypeStack.push() = ToOpType(callbackResult.fType);
  1046                 goto done;
  1050 done:
  1051     fTokenLength = 0;
  1052     return success;
  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;
  1067     return false;
  1068 done:
  1069     return success;
  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
  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;
  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;
  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;
  1130                 fOpStack.pop();
  1133         if (changedPrecedence == false)
  1134             goto goHome;
  1136     while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) {
  1137         if (processOp() == false)
  1138             return false;
  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;
  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;
  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;
  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;
  1176                 SkOpType resultType = array->getOpType();
  1177                 fTypeStack.push(resultType);
  1178                 fOperandStack.push(operand);
  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;
  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;
  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();
  1228         }   break;
  1229         default:
  1230             SkASSERT(0);
  1232 goHome:
  1233     return advance;
  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;
  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;
  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];
  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;
  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;
  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;
  1302                 if (type2 == kInt || type2 == kScalar) {
  1303                     convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float);
  1304                     type2 = kString;
  1306             } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) {
  1307                 if (type1 == kInt) {
  1308                     operand1.fScalar = IntToScalar(operand1.fS32);
  1309                     type1 = kScalar;
  1311                 if (type2 == kInt) {
  1312                     operand2.fScalar = IntToScalar(operand2.fS32);
  1313                      type2 = kScalar;
  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;
  1324                 type1 = kScalar;
  1326             if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) {
  1327                 operand1.fS32 = SkScalarFloorToInt(operand1.fScalar);
  1328                 type1 = kInt;
  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;
  1339             type2 = kScalar;
  1341         if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) {
  1342             operand2.fS32 = SkScalarFloorToInt(operand2.fScalar);
  1343             type2 = kInt;
  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);
  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;
  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);
  1459     fTypeStack.push(type2);
  1460     fOperandStack.push(operand2);
  1461     return true;
  1464 void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) {
  1465     UserCallBack callBack;
  1466     callBack.fPropertyCallBack = prop;
  1467     commonCallBack(kProperty, callBack, userStorage);
  1470 void SkScriptEngine::track(SkTypedArray* array) {
  1471     SkASSERT(fTrackArray.find(array) < 0);
  1472     *(fTrackArray.end() - 1) = array;
  1473     fTrackArray.appendClear();
  1476 void SkScriptEngine::track(SkString* string) {
  1477     SkASSERT(fTrackString.find(string) < 0);
  1478     *(fTrackString.end() - 1) = string;
  1479     fTrackString.appendClear();
  1482 void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) {
  1483     UserCallBack callBack;
  1484     callBack.fUnboxCallBack = func;
  1485     commonCallBack(kUnbox, callBack, userStorage);
  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
  1512                 success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
  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
  1528                 success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
  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;
  1544                 strPtr->appendScalar(operand.fScalar);
  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);
  1557     value->fType = toType;
  1558     if (success == false)
  1559         engine->fError = kTypeConversionFailed;
  1560     return success;
  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;
  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;
  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;
  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;
  1642     return true; // no error
  1645 #ifdef SK_SUPPORT_UNITTEST
  1647 #include "SkFloatingPoint.h"
  1649 #define DEF_SCALAR_ANSWER   0
  1650 #define DEF_STRING_ANSWER   NULL
  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 }
  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),
  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 };
  1864 #define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
  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);
  1890 #endif

mercurial