1#include "SkScript2.h"
2#include "SkFloatingPoint.h"
3#include "SkMath.h"
4#include "SkParse.h"
5#include "SkScriptCallBack.h"
6#include "SkScriptRuntime.h"
7#include "SkString.h"
8#include "SkOpArray.h"
9
10const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = {
11{ SkOperand2::kNoType, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean },
12{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
13    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString, kResultIsNotBoolean },    // kAdd
14{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitAnd
15{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitNot
16{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitOr
17{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
18    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kDivide
19{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
20    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber,
21    kResultIsBoolean }, // kEqual
22{ SkOperand2::kS32, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean },     // kFlipOps
23{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
24    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsNumber,
25    kResultIsBoolean }, // kGreaterEqual
26{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalAnd    (really, ToBool)
27{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalNot
28{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalOr
29{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMinus
30{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
31    SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias, kResultIsNotBoolean }, // kModulo
32{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
33    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMultiply
34{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftLeft
35{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftRight
36{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
37    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kSubtract
38{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean } // kXor
39};
40
41#define kBracketPrecedence 16
42#define kIfElsePrecedence 15
43
44const signed char SkScriptEngine2::gPrecedence[] = {
45    17, // kUnassigned,
46    6, // kAdd,
47    10, // kBitAnd,
48    4, // kBitNot,
49    12, // kBitOr,
50    5, // kDivide,
51    9, // kEqual,
52    -1, // kFlipOps,
53    8, // kGreaterEqual,
54    13, // kLogicalAnd,
55    4, // kLogicalNot,
56    14, // kLogicalOr,
57    4, // kMinus,
58    5, // kModulo,
59    5, // kMultiply,
60    7, // kShiftLeft,
61    7, // kShiftRight,    // signed
62    6, // kSubtract,
63    11, // kXor
64    kBracketPrecedence, // kArrayOp
65    kIfElsePrecedence, // kElse
66    kIfElsePrecedence, // kIf
67    kBracketPrecedence, // kParen
68};
69
70const SkScriptEngine2::TypeOp SkScriptEngine2::gTokens[] = {
71    kNop, // unassigned
72    kAddInt, // kAdd,
73    kBitAndInt, // kBitAnd,
74    kBitNotInt, // kBitNot,
75    kBitOrInt, // kBitOr,
76    kDivideInt, // kDivide,
77    kEqualInt, // kEqual,
78    kFlipOpsOp, // kFlipOps,
79    kGreaterEqualInt, // kGreaterEqual,
80    kLogicalAndInt, // kLogicalAnd,
81    kLogicalNotInt, // kLogicalNot,
82    kLogicalOrInt, // kLogicalOr,
83    kMinusInt, // kMinus,
84    kModuloInt, // kModulo,
85    kMultiplyInt, // kMultiply,
86    kShiftLeftInt, // kShiftLeft,
87    kShiftRightInt, // kShiftRight,    // signed
88    kSubtractInt, // kSubtract,
89    kXorInt // kXor
90};
91
92static inline bool is_between(int c, int min, int max)
93{
94    return (unsigned)(c - min) <= (unsigned)(max - min);
95}
96
97static inline bool is_ws(int c)
98{
99    return is_between(c, 1, 32);
100}
101
102static int token_length(const char* start) {
103    char ch = start[0];
104    if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
105        return -1;
106    int length = 0;
107    do
108        ch = start[++length];
109    while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
110           ch == '_' || ch == '$');
111    return length;
112}
113
114SkScriptEngine2::SkScriptEngine2(SkOperand2::OpType returnType) : fActiveStream(&fStream),
115fTokenLength(0), fReturnType(returnType), fError(kNoError),
116fAccumulatorType(SkOperand2::kNoType),
117fBranchPopAllowed(true), fConstExpression(true), fOperandInUse(false)
118{
119    Branch branch(kUnassigned, 0, 0);
120    fBranchStack.push(branch);
121    *fOpStack.push() = (Op) kParen;
122}
123
124SkScriptEngine2::~SkScriptEngine2() {
125    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
126        delete *stringPtr;
127    for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
128        delete *arrayPtr;
129}
130
131void SkScriptEngine2::addToken(SkScriptEngine2::TypeOp op) {
132    int limit = fBranchStack.count() - 1;
133    for (int index = 0; index < limit; index++) {
134        Branch& branch = fBranchStack.index(index);
135        if (branch.fPrimed == Branch::kIsPrimed)
136            resolveBranch(branch);
137    }
138    if (fBranchPopAllowed) {
139        while (fBranchStack.top().fDone == Branch::kIsDone)
140            fBranchStack.pop();
141    }
142    unsigned char charOp = (unsigned char) op;
143    fActiveStream->write(&charOp, sizeof(charOp));
144}
145
146void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg,
147                                    SkOperand2::OpType toType, SkScriptEngine2::TypeOp op) {
148    if (value->fIsConstant == SkScriptValue2::kConstant && convertTo(toType, value))
149        return;
150    addTokenValue(*value, reg);
151    addToken(op);
152    value->fIsWritten = SkScriptValue2::kWritten;
153    value->fType = toType;
154}
155
156void SkScriptEngine2::addTokenInt(int integer) {
157    fActiveStream->write(&integer, sizeof(integer));
158}
159
160void SkScriptEngine2::addTokenScalar(SkScalar scalar) {
161    fActiveStream->write(&scalar, sizeof(scalar));
162}
163
164void SkScriptEngine2::addTokenString(const SkString& string) {
165    int size = string.size();
166    addTokenInt(size);
167    fActiveStream->write(string.c_str(), size);
168}
169
170void SkScriptEngine2::addTokenValue(const SkScriptValue2& value, AddTokenRegister reg) {
171    if (value.isConstant() == false) {
172        if (reg == kAccumulator) {
173            if (fAccumulatorType == SkOperand2::kNoType)
174                addToken(kAccumulatorPop);
175        } else {
176            ; // !!! incomplete?
177        }
178        return;
179    }
180    if (reg == kAccumulator && fAccumulatorType != SkOperand2::kNoType)
181        addToken(kAccumulatorPush);
182    switch (value.fType) {
183        case SkOperand2::kS32:
184            addToken(reg == kAccumulator ? kIntegerAccumulator : kIntegerOperand);
185            addTokenInt(value.fOperand.fS32);
186            if (reg == kAccumulator)
187                fAccumulatorType = SkOperand2::kS32;
188            else
189                fOperandInUse = true;
190            break;
191        case SkOperand2::kScalar:
192            addToken(reg == kAccumulator ? kScalarAccumulator : kScalarOperand);
193            addTokenScalar(value.fOperand.fScalar);
194            if (reg == kAccumulator)
195                fAccumulatorType = SkOperand2::kScalar;
196            else
197                fOperandInUse = true;
198            break;
199        case SkOperand2::kString:
200            addToken(reg == kAccumulator ? kStringAccumulator : kStringOperand);
201            addTokenString(*value.fOperand.fString);
202            if (reg == kAccumulator)
203                fAccumulatorType = SkOperand2::kString;
204            else
205                fOperandInUse = true;
206            break;
207        default:
208            SkASSERT(0); //!!! not implemented yet
209    }
210}
211
212int SkScriptEngine2::arithmeticOp(char ch, char nextChar, bool lastPush) {
213    Op op = kUnassigned;
214    bool reverseOperands = false;
215    bool negateResult = false;
216    int advance = 1;
217    switch (ch) {
218        case '+':
219            // !!! ignoring unary plus as implemented here has the side effect of
220            // suppressing errors like +"hi"
221            if (lastPush == false)    // unary plus, don't push an operator
222                return advance;
223            op = kAdd;
224            break;
225        case '-':
226            op = lastPush ? kSubtract : kMinus;
227            break;
228        case '*':
229            op = kMultiply;
230            break;
231        case '/':
232            op = kDivide;
233            break;
234        case '>':
235            if (nextChar == '>') {
236                op = kShiftRight;
237                goto twoChar;
238            }
239            op = kGreaterEqual;
240            if (nextChar == '=')
241                goto twoChar;
242                reverseOperands = negateResult = true;
243            break;
244        case '<':
245            if (nextChar == '<') {
246                op = kShiftLeft;
247                goto twoChar;
248            }
249            op = kGreaterEqual;
250            reverseOperands = nextChar == '=';
251            negateResult = ! reverseOperands;
252            advance += reverseOperands;
253            break;
254        case '=':
255            if (nextChar == '=') {
256                op = kEqual;
257                goto twoChar;
258            }
259            break;
260        case '!':
261            if (nextChar == '=') {
262                op = kEqual;
263                negateResult = true;
264twoChar:
265                    advance++;
266                break;
267            }
268            op = kLogicalNot;
269            break;
270        case '?':
271            op =(Op)  kIf;
272            break;
273        case ':':
274            op = (Op) kElse;
275            break;
276        case '^':
277            op = kXor;
278            break;
279        case '(':
280            *fOpStack.push() = (Op) kParen;
281            return advance;
282        case '&':
283            SkASSERT(nextChar != '&');
284            op = kBitAnd;
285            break;
286        case '|':
287            SkASSERT(nextChar != '|');
288            op = kBitOr;
289            break;
290        case '%':
291            op = kModulo;
292            break;
293        case '~':
294            op = kBitNot;
295            break;
296    }
297    if (op == kUnassigned)
298        return 0;
299    signed char precedence = gPrecedence[op];
300    do {
301        int idx = 0;
302        Op compare;
303        do {
304            compare = fOpStack.index(idx);
305            if ((compare & kArtificialOp) == 0)
306                break;
307            idx++;
308        } while (true);
309        signed char topPrecedence = gPrecedence[compare];
310        SkASSERT(topPrecedence != -1);
311        if (topPrecedence > precedence || (topPrecedence == precedence &&
312            gOpAttributes[op].fLeftType == SkOperand2::kNoType)) {
313            break;
314        }
315        processOp();
316    } while (true);
317    if (negateResult)
318        *fOpStack.push() = (Op) (kLogicalNot | kArtificialOp);
319    fOpStack.push(op);
320    if (reverseOperands)
321        *fOpStack.push() = (Op) (kFlipOps | kArtificialOp);
322
323    return advance;
324}
325
326bool SkScriptEngine2::convertParams(SkTDArray<SkScriptValue2>* params,
327                                    const SkOperand2::OpType* paramTypes, int paramCount) {
328    int count = params->count();
329    if (count > paramCount) {
330        SkASSERT(0);
331        return false;    // too many parameters passed
332    }
333    for (int index = 0; index < count; index++)
334        convertTo(paramTypes[index], &(*params)[index]);
335    return true;
336}
337
338bool SkScriptEngine2::convertTo(SkOperand2::OpType toType, SkScriptValue2* value ) {
339    SkOperand2::OpType type = value->fType;
340    if (type == toType)
341        return true;
342    if (type == SkOperand2::kObject) {
343        if (handleUnbox(value) == false)
344            return false;
345        return convertTo(toType, value);
346    }
347    return ConvertTo(this, toType, value);
348}
349
350bool SkScriptEngine2::evaluateDot(const char*& script) {
351    size_t fieldLength = token_length(++script);        // skip dot
352    SkASSERT(fieldLength > 0); // !!! add error handling
353    const char* field = script;
354    script += fieldLength;
355    bool success = handleProperty();
356    if (success == false) {
357        fError = kCouldNotFindReferencedID;
358        goto error;
359    }
360    return evaluateDotParam(script, field, fieldLength);
361error:
362        return false;
363}
364
365bool SkScriptEngine2::evaluateDotParam(const char*& script, const char* field, size_t fieldLength) {
366    SkScriptValue2& top = fValueStack.top();
367    if (top.fType != SkOperand2::kObject)
368        return false;
369    void* object = top.fOperand.fObject;
370    fValueStack.pop();
371    char ch; // see if it is a simple member or a function
372    while (is_ws(ch = script[0]))
373        script++;
374    bool success = true;
375    if (ch != '(')
376        success = handleMember(field, fieldLength, object);
377    else {
378        SkTDArray<SkScriptValue2> params;
379        *fBraceStack.push() = kFunctionBrace;
380        success = functionParams(&script, &params);
381        if (success)
382            success = handleMemberFunction(field, fieldLength, object, &params);
383    }
384    return success;
385}
386
387bool SkScriptEngine2::evaluateScript(const char** scriptPtr, SkScriptValue2* value) {
388    //    fArrayOffset = 0;        // no support for structures for now
389    bool success;
390    const char* inner;
391    if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
392        *scriptPtr += sizeof("#script:") - 1;
393        if (fReturnType == SkOperand2::kNoType || fReturnType == SkOperand2::kString) {
394            success = innerScript(scriptPtr, value);
395            SkASSERT(success);
396            inner = value->fOperand.fString->c_str();
397            scriptPtr = &inner;
398        }
399    }
400    success = innerScript(scriptPtr, value);
401    const char* script = *scriptPtr;
402    char ch;
403    while (is_ws(ch = script[0]))
404        script++;
405    if (ch != '\0') {
406        // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
407        return false;
408    }
409    return success;
410}
411
412void SkScriptEngine2::forget(SkOpArray* array) {
413    if (array->getType() == SkOperand2::kString) {
414        for (int index = 0; index < array->count(); index++) {
415            SkString* string = (*array)[index].fString;
416            int found = fTrackString.find(string);
417            if (found >= 0)
418                fTrackString.remove(found);
419        }
420        return;
421    }
422    if (array->getType() == SkOperand2::kArray) {
423        for (int index = 0; index < array->count(); index++) {
424            SkOpArray* child = (*array)[index].fArray;
425            forget(child);    // forgets children of child
426            int found = fTrackArray.find(child);
427            if (found >= 0)
428                fTrackArray.remove(found);
429        }
430    }
431}
432
433bool SkScriptEngine2::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params) {
434    (*scriptPtr)++; // skip open paren
435    *fOpStack.push() = (Op) kParen;
436    *fBraceStack.push() = kFunctionBrace;
437    do {
438        SkScriptValue2 value;
439        bool success = innerScript(scriptPtr, &value);
440        SkASSERT(success);
441        if (success == false)
442            return false;
443        *params->append() = value;
444    } while ((*scriptPtr)[-1] == ',');
445    fBraceStack.pop();
446    fOpStack.pop(); // pop paren
447    (*scriptPtr)++; // advance beyond close paren
448    return true;
449}
450
451size_t SkScriptEngine2::getTokenOffset() {
452    return fActiveStream->getOffset();
453}
454
455SkOperand2::OpType SkScriptEngine2::getUnboxType(SkOperand2 scriptValue) {
456    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
457        if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
458            continue;
459        return (*callBack)->getReturnType(0, &scriptValue);
460    }
461    return SkOperand2::kObject;
462}
463
464bool SkScriptEngine2::innerScript(const char** scriptPtr, SkScriptValue2* value) {
465    const char* script = *scriptPtr;
466    char ch;
467    bool lastPush = false;
468    bool success = true;
469    int opBalance = fOpStack.count();
470    int baseBrace = fBraceStack.count();
471    int branchBalance = fBranchStack.count();
472    while ((ch = script[0]) != '\0') {
473        if (is_ws(ch)) {
474            script++;
475            continue;
476        }
477        SkScriptValue2 operand;
478        const char* dotCheck;
479        if (fBraceStack.count() > baseBrace) {
480            if (fBraceStack.top() == kArrayBrace) {
481                SkScriptValue2 tokenValue;
482                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
483                SkASSERT(success);
484                {
485                    SkOperand2::OpType type = fReturnType;
486                    if (fReturnType == SkOperand2::kNoType) {
487                        // !!! short sighted; in the future, allow each returned array component to carry
488                        // its own type, and let caller do any needed conversions
489                        if (value->fOperand.fArray->count() == 0)
490                            value->fOperand.fArray->setType(type = tokenValue.fType);
491                        else
492                            type = value->fOperand.fArray->getType();
493                    }
494                    if (tokenValue.fType != type)
495                        convertTo(type, &tokenValue);
496                    *value->fOperand.fArray->append() = tokenValue.fOperand;
497                }
498                lastPush = false;
499                continue;
500            } else
501                SkASSERT(token_length(script) > 0);
502        }
503        if (lastPush != false && fTokenLength > 0) {
504            if (ch == '(') {
505                *fBraceStack.push() = kFunctionBrace;
506                SkString functionName(fToken, fTokenLength);
507
508                if (handleFunction(&script) == false)
509                    return false;
510                lastPush = true;
511                continue;
512            } else if (ch == '[') {
513                if (handleProperty() == false) {
514                    SkASSERT(0);
515                    return false;
516                }
517                if (handleArrayIndexer(&script) == false)
518                    return false;
519                lastPush = true;
520                continue;
521            } else if (ch != '.') {
522                if (handleProperty() == false) {
523                    SkASSERT(0);
524                    return false;
525                }
526                lastPush = true;
527                continue;
528            }
529        }
530        if (ch == '0' && (script[1] & ~0x20) == 'X') {
531            SkASSERT(lastPush == false);
532            script += 2;
533            script = SkParse::FindHex(script, (uint32_t*) &operand.fOperand.fS32);
534            SkASSERT(script);
535            goto intCommon;
536        }
537        if (lastPush == false && ch == '.')
538            goto scalarCommon;
539        if (ch >= '0' && ch <= '9') {
540            SkASSERT(lastPush == false);
541            dotCheck = SkParse::FindS32(script, &operand.fOperand.fS32);
542            if (dotCheck[0] != '.') {
543                script = dotCheck;
544intCommon:
545                operand.fType = SkOperand2::kS32;
546            } else {
547scalarCommon:
548                script = SkParse::FindScalar(script, &operand.fOperand.fScalar);
549                operand.fType = SkOperand2::kScalar;
550            }
551            operand.fIsConstant = SkScriptValue2::kConstant;
552            fValueStack.push(operand);
553            lastPush = true;
554            continue;
555        }
556        int length = token_length(script);
557        if (length > 0) {
558            SkASSERT(lastPush == false);
559            fToken = script;
560            fTokenLength = length;
561            script += length;
562            lastPush = true;
563            continue;
564        }
565        char startQuote = ch;
566        if (startQuote == '\'' || startQuote == '\"') {
567            SkASSERT(lastPush == false);
568            operand.fOperand.fString = new SkString();
569            ++script;
570            const char* stringStart = script;
571            do {    // measure string
572                if (script[0] == '\\')
573                    ++script;
574                ++script;
575                SkASSERT(script[0]); // !!! throw an error
576            } while (script[0] != startQuote);
577            operand.fOperand.fString->set(stringStart, script - stringStart);
578            script = stringStart;
579            char* stringWrite = operand.fOperand.fString->writable_str();
580            do {    // copy string
581                if (script[0] == '\\')
582                    ++script;
583                *stringWrite++ = script[0];
584                ++script;
585                SkASSERT(script[0]); // !!! throw an error
586            } while (script[0] != startQuote);
587            ++script;
588            track(operand.fOperand.fString);
589            operand.fType = SkOperand2::kString;
590            operand.fIsConstant = SkScriptValue2::kConstant;
591            fValueStack.push(operand);
592            lastPush = true;
593            continue;
594        }
595        if (ch ==  '.') {
596            if (fTokenLength == 0) {
597                SkScriptValue2 scriptValue;
598                SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
599                int tokenLength = token_length(++script);
600                const char* token = script;
601                script += tokenLength;
602                SkASSERT(fValueStack.count() > 0); // !!! add error handling
603                SkScriptValue2 top;
604                fValueStack.pop(&top);
605
606                addTokenInt(top.fType);
607                addToken(kBoxToken);
608                top.fType = SkOperand2::kObject;
609                top.fIsConstant = SkScriptValue2::kVariable;
610                fConstExpression = false;
611                fValueStack.push(top);
612                success = evaluateDotParam(script, token, tokenLength);
613                SkASSERT(success);
614                lastPush = true;
615                continue;
616            }
617            // get next token, and evaluate immediately
618            success = evaluateDot(script);
619            if (success == false) {
620                //                SkASSERT(0);
621                return false;
622            }
623            lastPush = true;
624            continue;
625        }
626        if (ch == '[') {
627            if (lastPush == false) {
628                script++;
629                *fBraceStack.push() = kArrayBrace;
630                operand.fOperand.fArray = value->fOperand.fArray = new SkOpArray(fReturnType);
631                track(value->fOperand.fArray);
632
633                operand.fType = SkOperand2::kArray;
634                operand.fIsConstant = SkScriptValue2::kVariable;
635                fValueStack.push(operand);
636                continue;
637            }
638            if (handleArrayIndexer(&script) == false)
639                return false;
640            lastPush = true;
641            continue;
642        }
643#if 0 // structs not supported for now
644        if (ch == '{') {
645            if (lastPush == false) {
646                script++;
647                *fBraceStack.push() = kStructBrace;
648                operand.fS32 = 0;
649                *fTypeStack.push() = (SkOpType) kStruct;
650                fOperandStack.push(operand);
651                continue;
652            }
653            SkASSERT(0); // braces in other contexts aren't supported yet
654        }
655#endif
656        if (ch == ')' && fBraceStack.count() > 0) {
657            BraceStyle braceStyle = fBraceStack.top();
658            if (braceStyle == kFunctionBrace) {
659                fBraceStack.pop();
660                break;
661            }
662        }
663        if (ch == ',' || ch == ']') {
664            if (ch != ',') {
665                BraceStyle match;
666                fBraceStack.pop(&match);
667                SkASSERT(match == kArrayBrace);
668            }
669            script++;
670            // !!! see if brace or bracket is correct closer
671            break;
672        }
673        char nextChar = script[1];
674        int advance = logicalOp(ch, nextChar);
675        if (advance == 0)
676            advance = arithmeticOp(ch, nextChar, lastPush);
677        if (advance == 0) // unknown token
678            return false;
679        if (advance > 0)
680            script += advance;
681        lastPush = ch == ']' || ch == ')';
682    }
683    if (fTokenLength > 0) {
684        success = handleProperty();
685        SkASSERT(success);
686    }
687    int branchIndex = 0;
688    branchBalance = fBranchStack.count() - branchBalance;
689    fBranchPopAllowed = false;
690    while (branchIndex < branchBalance) {
691        Branch& branch = fBranchStack.index(branchIndex++);
692        if (branch.fPrimed == Branch::kIsPrimed)
693            break;
694        Op branchOp = branch.fOperator;
695        SkOperand2::OpType lastType = fValueStack.top().fType;
696        addTokenValue(fValueStack.top(), kAccumulator);
697        fValueStack.pop();
698        if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
699            if (branch.fOperator == kLogicalAnd)
700                branch.prime();
701            addToken(kToBool);
702        } else {
703            resolveBranch(branch);
704            SkScriptValue2 operand;
705            operand.fType = lastType;
706            // !!! note that many branching expressions could be constant
707            // today, we always evaluate branches as returning variables
708            operand.fIsConstant = SkScriptValue2::kVariable;
709            fValueStack.push(operand);
710        }
711        if (branch.fDone == Branch::kIsNotDone)
712            branch.prime();
713    }
714    fBranchPopAllowed = true;
715    while (fBranchStack.top().fDone == Branch::kIsDone)
716        fBranchStack.pop();
717    while (fOpStack.count() > opBalance) {     // leave open paren
718        if (processOp() == false)
719            return false;
720    }
721    SkOperand2::OpType topType = fValueStack.count() > 0 ? fValueStack.top().fType : SkOperand2::kNoType;
722    if (topType != fReturnType &&
723        topType == SkOperand2::kString && fReturnType != SkOperand2::kNoType) { // if result is a string, give handle property a chance to convert it to the property value
724        SkString* string = fValueStack.top().fOperand.fString;
725        fToken = string->c_str();
726        fTokenLength = string->size();
727        fValueStack.pop();
728        success = handleProperty();
729        if (success == false) {    // if it couldn't convert, return string (error?)
730            SkScriptValue2 operand;
731            operand.fType = SkOperand2::kString;
732            operand.fOperand.fString = string;
733            operand.fIsConstant = SkScriptValue2::kVariable;     // !!! ?
734            fValueStack.push(operand);
735        }
736    }
737    if (fStream.getOffset() > 0) {
738        addToken(kEnd);
739#ifdef SK_DEBUG
740        decompile((const unsigned char*)fStream.getStream(), fStream.getOffset());
741#endif
742        SkScriptRuntime runtime(fCallBackArray);
743        runtime.executeTokens((unsigned char*) fStream.getStream());
744        SkScriptValue2 value1;
745        runtime.getResult(&value1.fOperand);
746        value1.fType = fReturnType;
747        fValueStack.push(value1);
748    }
749    if (value) {
750        if (fValueStack.count() == 0)
751            return false;
752        fValueStack.pop(value);
753        if (value->fType != fReturnType && value->fType == SkOperand2::kObject &&
754            fReturnType != SkOperand2::kNoType)
755            convertTo(fReturnType, value);
756    }
757    //    if (fBranchStack.top().fOpStackDepth > fOpStack.count())
758    //        resolveBranch();
759    *scriptPtr = script;
760    return true; // no error
761}
762
763bool SkScriptEngine2::handleArrayIndexer(const char** scriptPtr) {
764    SkScriptValue2 scriptValue;
765    (*scriptPtr)++;
766    *fOpStack.push() = (Op) kParen;
767    *fBraceStack.push() = kArrayBrace;
768    SkOperand2::OpType saveType = fReturnType;
769    fReturnType = SkOperand2::kS32;
770    bool success = innerScript(scriptPtr, &scriptValue);
771    fReturnType = saveType;
772    SkASSERT(success);
773    success = convertTo(SkOperand2::kS32, &scriptValue);
774    SkASSERT(success);
775    int index = scriptValue.fOperand.fS32;
776    fValueStack.pop(&scriptValue);
777    if (scriptValue.fType == SkOperand2::kObject) {
778        success = handleUnbox(&scriptValue);
779        SkASSERT(success);
780        SkASSERT(scriptValue.fType == SkOperand2::kArray);
781    }
782    scriptValue.fType = scriptValue.fOperand.fArray->getType();
783    //    SkASSERT(index >= 0);
784    if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
785        fError = kArrayIndexOutOfBounds;
786        return false;
787    }
788    scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
789    scriptValue.fIsConstant = SkScriptValue2::kVariable;
790    fValueStack.push(scriptValue);
791    fOpStack.pop(); // pop paren
792    return success;
793}
794
795bool SkScriptEngine2::handleFunction(const char** scriptPtr) {
796    const char* functionName = fToken;
797    size_t functionNameLen = fTokenLength;
798    fTokenLength = 0;
799    SkTDArray<SkScriptValue2> params;
800    bool success = functionParams(scriptPtr, &params);
801    if (success == false)
802        goto done;
803    {
804        for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
805            if ((*callBack)->getType() != SkScriptCallBack::kFunction)
806                continue;
807            SkScriptValue2 callbackResult;
808            success = (*callBack)->getReference(functionName, functionNameLen, &callbackResult);
809            if (success) {
810                callbackResult.fType = (*callBack)->getReturnType(callbackResult.fOperand.fReference, NULL);
811                callbackResult.fIsConstant = SkScriptValue2::kVariable;
812                fValueStack.push(callbackResult);
813                goto done;
814            }
815        }
816    }
817    return false;
818done:
819        fOpStack.pop();
820    return success;
821}
822
823bool SkScriptEngine2::handleMember(const char* field, size_t len, void* object) {
824    bool success = true;
825    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
826        if ((*callBack)->getType() != SkScriptCallBack::kMember)
827            continue;
828        SkScriptValue2 callbackResult;
829        success = (*callBack)->getReference(field, len, &callbackResult);
830        if (success) {
831            if (callbackResult.fType == SkOperand2::kString)
832                track(callbackResult.fOperand.fString);
833            callbackResult.fIsConstant = SkScriptValue2::kVariable;
834            fValueStack.push(callbackResult);
835            goto done;
836        }
837    }
838    return false;
839done:
840        return success;
841}
842
843bool SkScriptEngine2::handleMemberFunction(const char* field, size_t len, void* object,
844                                           SkTDArray<SkScriptValue2>* params) {
845    bool success = true;
846    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
847        if ((*callBack)->getType() != SkScriptCallBack::kMemberFunction)
848            continue;
849        SkScriptValue2 callbackResult;
850        success = (*callBack)->getReference(field, len, &callbackResult);
851        if (success) {
852            if (callbackResult.fType == SkOperand2::kString)
853                track(callbackResult.fOperand.fString);
854            callbackResult.fIsConstant = SkScriptValue2::kVariable;
855            fValueStack.push(callbackResult);
856            goto done;
857        }
858    }
859    return false;
860done:
861        return success;
862}
863
864bool SkScriptEngine2::handleProperty() {
865    bool success = true;
866    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
867        if ((*callBack)->getType() != SkScriptCallBack::kProperty)
868            continue;
869        SkScriptValue2 callbackResult;
870        success = (*callBack)->getReference(fToken, fTokenLength, &callbackResult);
871        if (success) {
872            if (callbackResult.fType == SkOperand2::kString && callbackResult.fOperand.fString == NULL) {
873                callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
874                track(callbackResult.fOperand.fString);
875            }
876            callbackResult.fIsConstant = SkScriptValue2::kVariable;
877            fValueStack.push(callbackResult);
878            goto done;
879        }
880    }
881done:
882        fTokenLength = 0;
883    return success;
884}
885
886bool SkScriptEngine2::handleUnbox(SkScriptValue2* scriptValue) {
887    bool success = true;
888    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
889        if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
890            continue;
891        SkScriptCallBackConvert* callBackConvert = (SkScriptCallBackConvert*) *callBack;
892        success = callBackConvert->convert(scriptValue->fType, &scriptValue->fOperand);
893        if (success) {
894            if (scriptValue->fType == SkOperand2::kString)
895                track(scriptValue->fOperand.fString);
896            goto done;
897        }
898    }
899    return false;
900done:
901        return success;
902}
903
904// note that entire expression is treated as if it were enclosed in parens
905// an open paren is always the first thing in the op stack
906
907int SkScriptEngine2::logicalOp(char ch, char nextChar) {
908    int advance = 1;
909    Op op;
910    signed char precedence;
911    switch (ch) {
912        case ')':
913            op = (Op) kParen;
914            break;
915        case ']':
916            op = (Op) kArrayOp;
917            break;
918        case '?':
919            op = (Op) kIf;
920            break;
921        case ':':
922            op = (Op) kElse;
923            break;
924        case '&':
925            if (nextChar != '&')
926                goto noMatch;
927            op = kLogicalAnd;
928            advance = 2;
929            break;
930        case '|':
931            if (nextChar != '|')
932                goto noMatch;
933            op = kLogicalOr;
934            advance = 2;
935            break;
936        default:
937            noMatch:
938            return 0;
939    }
940    precedence = gPrecedence[op];
941    int branchIndex = 0;
942    fBranchPopAllowed = false;
943    do {
944        while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence)
945            processOp();
946        Branch& branch = fBranchStack.index(branchIndex++);
947        Op branchOp = branch.fOperator;
948        if (gPrecedence[branchOp] >= precedence)
949            break;
950        addTokenValue(fValueStack.top(), kAccumulator);
951        fValueStack.pop();
952        if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
953            if (branch.fOperator == kLogicalAnd)
954                branch.prime();
955            addToken(kToBool);
956        } else
957            resolveBranch(branch);
958        if (branch.fDone == Branch::kIsNotDone)
959            branch.prime();
960    } while (true);
961    fBranchPopAllowed = true;
962    while (fBranchStack.top().fDone == Branch::kIsDone)
963        fBranchStack.pop();
964    processLogicalOp(op);
965    return advance;
966}
967
968void SkScriptEngine2::processLogicalOp(Op op) {
969    switch (op) {
970        case kParen:
971        case kArrayOp:
972            SkASSERT(fOpStack.count() > 1 && fOpStack.top() == op);    // !!! add error handling
973            if (op == kParen)
974                fOpStack.pop();
975            else {
976                SkScriptValue2 value;
977                fValueStack.pop(&value);
978                SkASSERT(value.fType == SkOperand2::kS32 || value.fType == SkOperand2::kScalar); // !!! add error handling (although, could permit strings eventually)
979                int index = value.fType == SkOperand2::kScalar ? SkScalarFloor(value.fOperand.fScalar) :
980                    value.fOperand.fS32;
981                SkScriptValue2 arrayValue;
982                fValueStack.pop(&arrayValue);
983                SkASSERT(arrayValue.fType == SkOperand2::kArray);  // !!! add error handling
984                SkOpArray* array = arrayValue.fOperand.fArray;
985                SkOperand2 operand;
986                bool success = array->getIndex(index, &operand);
987                SkASSERT(success); // !!! add error handling
988                SkScriptValue2 resultValue;
989                resultValue.fType = array->getType();
990                resultValue.fOperand = operand;
991                resultValue.fIsConstant = SkScriptValue2::kVariable;
992                fValueStack.push(resultValue);
993            }
994                break;
995        case kIf: {
996            if (fAccumulatorType == SkOperand2::kNoType) {
997                addTokenValue(fValueStack.top(), kAccumulator);
998                fValueStack.pop();
999            }
1000            SkASSERT(fAccumulatorType != SkOperand2::kString); // !!! add error handling
1001            addToken(kIfOp);
1002            Branch branch(op, fOpStack.count(), getTokenOffset());
1003            *fBranchStack.push() = branch;
1004            addTokenInt(0); // placeholder for future branch
1005            fAccumulatorType = SkOperand2::kNoType;
1006        } break;
1007        case kElse: {
1008            addTokenValue(fValueStack.top(), kAccumulator);
1009            fValueStack.pop();
1010            addToken(kElseOp);
1011            size_t newOffset = getTokenOffset();
1012            addTokenInt(0); // placeholder for future branch
1013            Branch& branch = fBranchStack.top();
1014            resolveBranch(branch);
1015            branch.fOperator = op;
1016            branch.fDone = Branch::kIsNotDone;
1017            SkASSERT(branch.fOpStackDepth == fOpStack.count());
1018            branch.fOffset = newOffset;
1019            fAccumulatorType = SkOperand2::kNoType;
1020        } break;
1021        case kLogicalAnd:
1022        case kLogicalOr: {
1023            Branch& oldTop = fBranchStack.top();
1024            Branch::Primed wasPrime = oldTop.fPrimed;
1025            Branch::Done wasDone = oldTop.fDone;
1026            oldTop.fPrimed = Branch::kIsNotPrimed;
1027            oldTop.fDone = Branch::kIsNotDone;
1028            if (fAccumulatorType == SkOperand2::kNoType) {
1029                SkASSERT(fValueStack.top().fType == SkOperand2::kS32); // !!! add error handling, and conversion to int?
1030                addTokenValue(fValueStack.top(), kAccumulator);
1031                fValueStack.pop();
1032            } else
1033                SkASSERT(fAccumulatorType == SkOperand2::kS32);
1034            // if 'and', write beq goto opcode after end of predicate (after to bool)
1035            // if 'or', write bne goto to bool
1036            addToken(op == kLogicalAnd ? kLogicalAndInt : kLogicalOrInt);
1037            Branch branch(op, fOpStack.count(), getTokenOffset());
1038            addTokenInt(0); // placeholder for future branch
1039            oldTop.fPrimed = wasPrime;
1040            oldTop.fDone = wasDone;
1041            *fBranchStack.push() = branch;
1042            fAccumulatorType = SkOperand2::kNoType;
1043        }    break;
1044        default:
1045            SkASSERT(0);
1046    }
1047}
1048
1049bool SkScriptEngine2::processOp() {
1050    Op op;
1051    fOpStack.pop(&op);
1052    op = (Op) (op & ~kArtificialOp);
1053    const OperatorAttributes* attributes = &gOpAttributes[op];
1054    SkScriptValue2 value1;
1055    memset(&value1, 0, sizeof(SkScriptValue2));
1056    SkScriptValue2 value2;
1057    fValueStack.pop(&value2);
1058    value2.fIsWritten = SkScriptValue2::kUnwritten;
1059    //    SkScriptEngine2::SkTypeOp convert1[3];
1060    //    SkScriptEngine2::SkTypeOp convert2[3];
1061    //    SkScriptEngine2::SkTypeOp* convert2Ptr = convert2;
1062    bool constantOperands = value2.fIsConstant == SkScriptValue2::kConstant;
1063    if (attributes->fLeftType != SkOperand2::kNoType) {
1064        fValueStack.pop(&value1);
1065        constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant;
1066        value1.fIsWritten = SkScriptValue2::kUnwritten;
1067        if (op == kFlipOps) {
1068            SkTSwap(value1, value2);
1069            fOpStack.pop(&op);
1070            op = (Op) (op & ~kArtificialOp);
1071            attributes = &gOpAttributes[op];
1072            if (constantOperands == false)
1073                addToken(kFlipOpsOp);
1074        }
1075        if (value1.fType == SkOperand2::kObject && (value1.fType & attributes->fLeftType) == 0) {
1076            value1.fType = getUnboxType(value1.fOperand);
1077            addToken(kUnboxToken);
1078        }
1079    }
1080    if (value2.fType == SkOperand2::kObject && (value2.fType & attributes->fLeftType) == 0) {
1081        value1.fType = getUnboxType(value2.fOperand);
1082        addToken(kUnboxToken2);
1083    }
1084    if (attributes->fLeftType != SkOperand2::kNoType) {
1085        if (value1.fType != value2.fType) {
1086            if ((attributes->fLeftType & SkOperand2::kString) && attributes->fBias & kTowardsString &&
1087                ((value1.fType | value2.fType) & SkOperand2::kString)) {
1088                if (value1.fType == SkOperand2::kS32 || value1.fType == SkOperand2::kScalar) {
1089                    addTokenConst(&value1, kAccumulator, SkOperand2::kString,
1090                                  value1.fType == SkOperand2::kS32 ? kIntToString : kScalarToString);
1091                }
1092                if (value2.fType == SkOperand2::kS32 || value2.fType == SkOperand2::kScalar) {
1093                    addTokenConst(&value2, kOperand, SkOperand2::kString,
1094                                  value2.fType == SkOperand2::kS32 ? kIntToString2 : kScalarToString2);
1095                }
1096            } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) &
1097                                                                       SkOperand2::kScalar)) {
1098                if (value1.fType == SkOperand2::kS32)
1099                    addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kIntToScalar);
1100                if (value2.fType == SkOperand2::kS32)
1101                    addTokenConst(&value2, kOperand, SkOperand2::kScalar, kIntToScalar2);
1102            }
1103        }
1104        if ((value1.fType & attributes->fLeftType) == 0 || value1.fType != value2.fType) {
1105            if (value1.fType == SkOperand2::kString)
1106                addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kStringToScalar);
1107            if (value1.fType == SkOperand2::kScalar && (attributes->fLeftType == SkOperand2::kS32 ||
1108                                                        value2.fType == SkOperand2::kS32))
1109                addTokenConst(&value1, kAccumulator, SkOperand2::kS32, kScalarToInt);
1110        }
1111    }
1112    AddTokenRegister rhRegister = attributes->fLeftType != SkOperand2::kNoType ?
1113        kOperand : kAccumulator;
1114    if ((value2.fType & attributes->fRightType) == 0 || value1.fType != value2.fType) {
1115        if (value2.fType == SkOperand2::kString)
1116            addTokenConst(&value2, rhRegister, SkOperand2::kScalar, kStringToScalar2);
1117        if (value2.fType == SkOperand2::kScalar && (attributes->fRightType == SkOperand2::kS32 ||
1118                                                    value1.fType == SkOperand2::kS32))
1119            addTokenConst(&value2, rhRegister, SkOperand2::kS32, kScalarToInt2);
1120    }
1121    TypeOp typeOp = gTokens[op];
1122    if (value2.fType == SkOperand2::kScalar)
1123        typeOp = (TypeOp) (typeOp + 1);
1124    else if (value2.fType == SkOperand2::kString)
1125        typeOp = (TypeOp) (typeOp + 2);
1126    SkDynamicMemoryWStream stream;
1127    SkOperand2::OpType saveType;
1128    SkBool saveOperand;
1129    if (constantOperands) {
1130        fActiveStream = &stream;
1131        saveType = fAccumulatorType;
1132        saveOperand = fOperandInUse;
1133        fAccumulatorType = SkOperand2::kNoType;
1134        fOperandInUse = false;
1135    }
1136    if (attributes->fLeftType != SkOperand2::kNoType) {    // two operands
1137        if (value1.fIsWritten == SkScriptValue2::kUnwritten)
1138            addTokenValue(value1, kAccumulator);
1139    }
1140    if (value2.fIsWritten == SkScriptValue2::kUnwritten)
1141        addTokenValue(value2, rhRegister);
1142    addToken(typeOp);
1143    if (constantOperands) {
1144        addToken(kEnd);
1145#ifdef SK_DEBUG
1146        decompile((const unsigned char*) stream.getStream(), stream.getOffset());
1147#endif
1148        SkScriptRuntime runtime(fCallBackArray);
1149        runtime.executeTokens((unsigned char*) stream.getStream());
1150        runtime.getResult(&value1.fOperand);
1151        if (attributes->fResultIsBoolean == kResultIsBoolean)
1152            value1.fType = SkOperand2::kS32;
1153        else if (attributes->fLeftType == SkOperand2::kNoType) // unary operand
1154            value1.fType = value2.fType;
1155        fValueStack.push(value1);
1156        if (value1.fType == SkOperand2::kString)
1157            runtime.untrack(value1.fOperand.fString);
1158        else if (value1.fType == SkOperand2::kArray)
1159            runtime.untrack(value1.fOperand.fArray);
1160        fActiveStream = &fStream;
1161        fAccumulatorType = saveType;
1162        fOperandInUse = saveOperand;
1163        return true;
1164    }
1165    value2.fIsConstant = SkScriptValue2::kVariable;
1166    fValueStack.push(value2);
1167    return true;
1168}
1169
1170void SkScriptEngine2::Branch::resolve(SkDynamicMemoryWStream* stream, size_t off) {
1171    SkASSERT(fDone == kIsNotDone);
1172    fPrimed = kIsNotPrimed;
1173    fDone = kIsDone;
1174    SkASSERT(off > fOffset + sizeof(size_t));
1175    size_t offset = off - fOffset - sizeof(offset);
1176    stream->write(&offset, fOffset, sizeof(offset));
1177}
1178
1179void SkScriptEngine2::resolveBranch(SkScriptEngine2::Branch& branch) {
1180    branch.resolve(fActiveStream, getTokenOffset());
1181}
1182
1183bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toType, SkScriptValue2* value ) {
1184    SkASSERT(value);
1185    SkOperand2::OpType type = value->fType;
1186    if (type == toType)
1187        return true;
1188    SkOperand2& operand = value->fOperand;
1189    bool success = true;
1190    switch (toType) {
1191        case SkOperand2::kS32:
1192            if (type == SkOperand2::kScalar)
1193                operand.fS32 = SkScalarFloor(operand.fScalar);
1194            else {
1195                SkASSERT(type == SkOperand2::kString);
1196                success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
1197            }
1198                break;
1199        case SkOperand2::kScalar:
1200            if (type == SkOperand2::kS32)
1201                operand.fScalar = IntToScalar(operand.fS32);
1202            else {
1203                SkASSERT(type == SkOperand2::kString);
1204                success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
1205            }
1206                break;
1207        case SkOperand2::kString: {
1208            SkString* strPtr = new SkString();
1209            SkASSERT(engine);
1210            engine->track(strPtr);
1211            if (type == SkOperand2::kS32)
1212                strPtr->appendS32(operand.fS32);
1213            else {
1214                SkASSERT(type == SkOperand2::kScalar);
1215                strPtr->appendScalar(operand.fScalar);
1216            }
1217            operand.fString = strPtr;
1218        } break;
1219        case SkOperand2::kArray: {
1220            SkOpArray* array = new SkOpArray(type);
1221            *array->append() = operand;
1222            engine->track(array);
1223            operand.fArray = array;
1224        } break;
1225        default:
1226            SkASSERT(0);
1227    }
1228    value->fType = toType;
1229    return success;
1230}
1231
1232SkScalar SkScriptEngine2::IntToScalar(int32_t s32) {
1233    SkScalar scalar;
1234    if (s32 == (int32_t) SK_NaN32)
1235        scalar = SK_ScalarNaN;
1236    else if (SkAbs32(s32) == SK_MaxS32)
1237        scalar = SkSign32(s32) * SK_ScalarMax;
1238    else
1239        scalar = SkIntToScalar(s32);
1240    return scalar;
1241}
1242
1243bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* string) {
1244    switch (value.fType) {
1245        case SkOperand2::kS32:
1246            string->reset();
1247            string->appendS32(value.fOperand.fS32);
1248            break;
1249        case SkOperand2::kScalar:
1250            string->reset();
1251            string->appendScalar(value.fOperand.fScalar);
1252            break;
1253        case SkOperand2::kString:
1254            string->set(*value.fOperand.fString);
1255            break;
1256        default:
1257            SkASSERT(0);
1258            return false;
1259    }
1260    return true; // no error
1261}
1262
1263#ifdef SK_DEBUG
1264
1265#define testInt(expression) { #expression, SkOperand2::kS32, expression, 0, NULL }
1266#ifdef SK_SCALAR_IS_FLOAT
1267#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression, NULL }
1268#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2), NULL }
1269#else
1270#ifdef SK_CAN_USE_FLOAT
1271#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f), NULL }
1272#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2)  * 65536.0f), NULL }
1273#endif
1274#endif
1275#define testTrue(expression) { #expression, SkOperand2::kS32, 1, 0, NULL }
1276#define testFalse(expression) { #expression, SkOperand2::kS32, 0, 0, NULL }
1277
1278static const SkScriptNAnswer2 scriptTests[]  = {
1279    testInt(1||(0&&3)),
1280#ifdef SK_CAN_USE_FLOAT
1281    testScalar(- -5.5- -1.5),
1282    testScalar(1.0+5),
1283#endif
1284    testInt((6+7)*8),
1285    testInt(3*(4+5)),
1286#ifdef SK_CAN_USE_FLOAT
1287    testScalar(1.0+2.0),
1288    testScalar(3.0-1.0),
1289    testScalar(6-1.0),
1290    testScalar(2.5*6.),
1291    testScalar(0.5*4),
1292    testScalar(4.5/.5),
1293    testScalar(9.5/19),
1294    testRemainder(9.5, 0.5),
1295    testRemainder(9.,2),
1296    testRemainder(9,2.5),
1297    testRemainder(-9,2.5),
1298    testTrue(-9==-9.0),
1299    testTrue(-9.==-4.0-5),
1300    testTrue(-9.*1==-4-5),
1301    testFalse(-9!=-9.0),
1302    testFalse(-9.!=-4.0-5),
1303    testFalse(-9.*1!=-4-5),
1304#endif
1305    testInt(0x123),
1306    testInt(0XABC),
1307    testInt(0xdeadBEEF),
1308    {    "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" },
1309    {    "123+\"456\"", SkOperand2::kString, 0, 0, "123456" },
1310    {    "'123'+456", SkOperand2::kString, 0, 0, "123456" },
1311    {    "'123'|\"456\"", SkOperand2::kS32, 123|456, 0, NULL },
1312    {    "123|\"456\"", SkOperand2::kS32, 123|456, 0, NULL },
1313    {    "'123'|456", SkOperand2::kS32, 123|456, 0, NULL },
1314    {    "'2'<11", SkOperand2::kS32, 1, 0, NULL },
1315    {    "2<'11'", SkOperand2::kS32, 1, 0, NULL },
1316    {    "'2'<'11'", SkOperand2::kS32, 0, 0, NULL },
1317    testInt(123),
1318    testInt(-345),
1319    testInt(+678),
1320    testInt(1+2+3),
1321    testInt(3*4+5),
1322    testInt(6+7*8),
1323    testInt(-1-2-8/4),
1324    testInt(-9%4),
1325    testInt(9%-4),
1326    testInt(-9%-4),
1327    testInt(123|978),
1328    testInt(123&978),
1329    testInt(123^978),
1330    testInt(2<<4),
1331    testInt(99>>3),
1332    testInt(~55),
1333    testInt(~~55),
1334    testInt(!55),
1335    testInt(!!55),
1336    // both int
1337    testInt(2<2),
1338    testInt(2<11),
1339    testInt(20<11),
1340    testInt(2<=2),
1341    testInt(2<=11),
1342    testInt(20<=11),
1343    testInt(2>2),
1344    testInt(2>11),
1345    testInt(20>11),
1346    testInt(2>=2),
1347    testInt(2>=11),
1348    testInt(20>=11),
1349    testInt(2==2),
1350    testInt(2==11),
1351    testInt(20==11),
1352    testInt(2!=2),
1353    testInt(2!=11),
1354    testInt(20!=11),
1355#ifdef SK_CAN_USE_FLOAT
1356    // left int, right scalar
1357    testInt(2<2.),
1358    testInt(2<11.),
1359    testInt(20<11.),
1360    testInt(2<=2.),
1361    testInt(2<=11.),
1362    testInt(20<=11.),
1363    testInt(2>2.),
1364    testInt(2>11.),
1365    testInt(20>11.),
1366    testInt(2>=2.),
1367    testInt(2>=11.),
1368    testInt(20>=11.),
1369    testInt(2==2.),
1370    testInt(2==11.),
1371    testInt(20==11.),
1372    testInt(2!=2.),
1373    testInt(2!=11.),
1374    testInt(20!=11.),
1375    // left scalar, right int
1376    testInt(2.<2),
1377    testInt(2.<11),
1378    testInt(20.<11),
1379    testInt(2.<=2),
1380    testInt(2.<=11),
1381    testInt(20.<=11),
1382    testInt(2.>2),
1383    testInt(2.>11),
1384    testInt(20.>11),
1385    testInt(2.>=2),
1386    testInt(2.>=11),
1387    testInt(20.>=11),
1388    testInt(2.==2),
1389    testInt(2.==11),
1390    testInt(20.==11),
1391    testInt(2.!=2),
1392    testInt(2.!=11),
1393    testInt(20.!=11),
1394    // both scalar
1395    testInt(2.<11.),
1396    testInt(20.<11.),
1397    testInt(2.<=2.),
1398    testInt(2.<=11.),
1399    testInt(20.<=11.),
1400    testInt(2.>2.),
1401    testInt(2.>11.),
1402    testInt(20.>11.),
1403    testInt(2.>=2.),
1404    testInt(2.>=11.),
1405    testInt(20.>=11.),
1406    testInt(2.==2.),
1407    testInt(2.==11.),
1408    testInt(20.==11.),
1409    testInt(2.!=2.),
1410    testInt(2.!=11.),
1411    testInt(20.!=11.),
1412#endif
1413    // int, string (string is int)
1414    testFalse(2<'2'),
1415    testTrue(2<'11'),
1416    testFalse(20<'11'),
1417    testTrue(2<='2'),
1418    testTrue(2<='11'),
1419    testFalse(20<='11'),
1420    testFalse(2>'2'),
1421    testFalse(2>'11'),
1422    testTrue(20>'11'),
1423    testTrue(2>='2'),
1424    testFalse(2>='11'),
1425    testTrue(20>='11'),
1426    testTrue(2=='2'),
1427    testFalse(2=='11'),
1428    testFalse(2!='2'),
1429    testTrue(2!='11'),
1430    // int, string (string is scalar)
1431    testFalse(2<'2.'),
1432    testTrue(2<'11.'),
1433    testFalse(20<'11.'),
1434    testTrue(2=='2.'),
1435    testFalse(2=='11.'),
1436#ifdef SK_CAN_USE_FLOAT
1437    // scalar, string
1438    testFalse(2.<'2.'),
1439    testTrue(2.<'11.'),
1440    testFalse(20.<'11.'),
1441    testTrue(2.=='2.'),
1442    testFalse(2.=='11.'),
1443    // string, int
1444    testFalse('2'<2),
1445    testTrue('2'<11),
1446    testFalse('20'<11),
1447    testTrue('2'==2),
1448    testFalse('2'==11),
1449    // string, scalar
1450    testFalse('2'<2.),
1451    testTrue('2'<11.),
1452    testFalse('20'<11.),
1453    testTrue('2'==2.),
1454    testFalse('2'==11.),
1455#endif
1456    // string, string
1457    testFalse('2'<'2'),
1458    testFalse('2'<'11'),
1459    testFalse('20'<'11'),
1460    testTrue('2'=='2'),
1461    testFalse('2'=='11'),
1462    // logic
1463    testInt(1?2:3),
1464    testInt(0?2:3),
1465    testInt((1&&2)||3),
1466    testInt((1&&0)||3),
1467    testInt((1&&0)||0),
1468    testInt(1||(0&&3)),
1469    testInt(0||(0&&3)),
1470    testInt(0||(1&&3)),
1471    testInt(0&&1?2:3)
1472#ifdef SK_CAN_USE_FLOAT
1473    , {    "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2, NULL }
1474#endif
1475};
1476
1477#define SkScriptNAnswer_testCount    SK_ARRAY_COUNT(scriptTests)
1478
1479void SkScriptEngine2::UnitTest() {
1480#if defined(SK_SUPPORT_UNITTEST)
1481    ValidateDecompileTable();
1482    for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
1483        SkScriptEngine2 engine(scriptTests[index].fType);
1484        SkScriptValue2 value;
1485        const char* script = scriptTests[index].fScript;
1486        const char* scriptPtr = script;
1487        SkASSERT(engine.evaluateScript(&scriptPtr, &value) == true);
1488        SkASSERT(value.fType == scriptTests[index].fType);
1489        SkScalar error;
1490        switch (value.fType) {
1491            case SkOperand2::kS32:
1492                if (value.fOperand.fS32 != scriptTests[index].fIntAnswer)
1493                    SkDEBUGF(("script '%s' == value %d != expected answer %d\n", script, value.fOperand.fS32, scriptTests[index].fIntAnswer));
1494                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
1495                break;
1496            case SkOperand2::kScalar:
1497                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
1498#ifdef SK_CAN_USE_FLOAT
1499                if (error >= SK_Scalar1 / 10000)
1500                    SkDEBUGF(("script '%s' == value %g != expected answer %g\n", script, value.fOperand.fScalar / (1.0f * SK_Scalar1), scriptTests[index].fScalarAnswer / (1.0f * SK_Scalar1)));
1501#endif
1502                SkASSERT(error < SK_Scalar1 / 10000);
1503                break;
1504            case SkOperand2::kString:
1505                SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
1506                break;
1507            default:
1508                SkASSERT(0);
1509        }
1510    }
1511#endif
1512}
1513#endif
1514