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