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