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