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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/animator/SkScript.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1890 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2006 The Android Open Source Project
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "SkScript.h"
    1.14 +#include "SkMath.h"
    1.15 +#include "SkParse.h"
    1.16 +#include "SkString.h"
    1.17 +#include "SkTypedArray.h"
    1.18 +
    1.19 +/* things to do
    1.20 +    ? re-enable support for struct literals (e.g., for initializing points or rects)
    1.21 +        {x:1, y:2}
    1.22 +    ? use standard XML / script notation like document.getElementById("canvas");
    1.23 +    finish support for typed arrays
    1.24 +        ? allow indexing arrays by string
    1.25 +            this could map to the 'name' attribute of a given child of an array
    1.26 +        ? allow multiple types in the array
    1.27 +    remove SkDisplayType.h  // from SkOperand.h
    1.28 +    merge type and operand arrays into scriptvalue array
    1.29 +*/
    1.30 +
    1.31 +#ifdef SK_DEBUG
    1.32 +static const char* errorStrings[] = {
    1.33 +        "array index of out bounds", // kArrayIndexOutOfBounds
    1.34 +        "could not find reference id", // kCouldNotFindReferencedID
    1.35 +        "dot operator expects object", // kDotOperatorExpectsObject
    1.36 +        "error in array index", // kErrorInArrrayIndex
    1.37 +        "error in function parameters", // kErrorInFunctionParameters
    1.38 +        "expected array", // kExpectedArray
    1.39 +        "expected boolean expression", // kExpectedBooleanExpression
    1.40 +        "expected field name", // kExpectedFieldName
    1.41 +        "expected hex", // kExpectedHex
    1.42 +        "expected int for condition operator", // kExpectedIntForConditionOperator
    1.43 +        "expected number", // kExpectedNumber
    1.44 +        "expected number for array index", // kExpectedNumberForArrayIndex
    1.45 +        "expected operator", // kExpectedOperator
    1.46 +        "expected token", // kExpectedToken
    1.47 +        "expected token before dot operator", // kExpectedTokenBeforeDotOperator
    1.48 +        "expected value", // kExpectedValue
    1.49 +        "handle member failed", // kHandleMemberFailed
    1.50 +        "handle member function failed", // kHandleMemberFunctionFailed
    1.51 +        "handle unbox failed", // kHandleUnboxFailed
    1.52 +        "index out of range", // kIndexOutOfRange
    1.53 +        "mismatched array brace", // kMismatchedArrayBrace
    1.54 +        "mismatched brackets", // kMismatchedBrackets
    1.55 +        "no function handler found", // kNoFunctionHandlerFound
    1.56 +        "premature end", // kPrematureEnd
    1.57 +        "too many parameters", // kTooManyParameters
    1.58 +        "type conversion failed", // kTypeConversionFailed
    1.59 +        "unterminated string" // kUnterminatedString
    1.60 +};
    1.61 +#endif
    1.62 +
    1.63 +const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = {
    1.64 +    { kNoType, kNoType, kNoBias }, //   kUnassigned,
    1.65 +    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd
    1.66 +    // kAddInt = kAdd,
    1.67 +    { kNoType, kNoType, kNoBias },  // kAddScalar,
    1.68 +    { kNoType, kNoType, kNoBias },  // kAddString,
    1.69 +    { kNoType, kNoType, kNoBias },  // kArrayOp,
    1.70 +    { kInt, kInt, kNoBias }, // kBitAnd
    1.71 +    { kNoType, kInt, kNoBias }, // kBitNot
    1.72 +    { kInt, kInt, kNoBias }, // kBitOr
    1.73 +    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide
    1.74 +    // kDivideInt = kDivide
    1.75 +    { kNoType, kNoType, kNoBias },  // kDivideScalar
    1.76 +    { kNoType, kNoType, kNoBias },  // kElse
    1.77 +    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual
    1.78 +    // kEqualInt = kEqual
    1.79 +    { kNoType, kNoType, kNoBias },  // kEqualScalar
    1.80 +    { kNoType, kNoType, kNoBias },  // kEqualString
    1.81 +    { kInt, kNoType, kNoBias },     // kFlipOps
    1.82 +    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual
    1.83 +    // kGreaterEqualInt = kGreaterEqual
    1.84 +    { kNoType, kNoType, kNoBias },  // kGreaterEqualScalar
    1.85 +    { kNoType, kNoType, kNoBias },  // kGreaterEqualString
    1.86 +    { kNoType, kNoType, kNoBias },  // kIf
    1.87 +    { kNoType, kInt, kNoBias }, // kLogicalAnd  (really, ToBool)
    1.88 +    { kNoType, kInt, kNoBias }, // kLogicalNot
    1.89 +    { kInt, kInt, kNoBias }, // kLogicalOr
    1.90 +    { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus
    1.91 +    // kMinusInt = kMinus
    1.92 +    { kNoType, kNoType, kNoBias },  // kMinusScalar
    1.93 +    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo
    1.94 +    // kModuloInt = kModulo
    1.95 +    { kNoType, kNoType, kNoBias },  // kModuloScalar
    1.96 +    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply
    1.97 +    // kMultiplyInt = kMultiply
    1.98 +    { kNoType, kNoType, kNoBias },  // kMultiplyScalar
    1.99 +    { kNoType, kNoType, kNoBias },  // kParen
   1.100 +    { kInt, kInt, kNoBias }, // kShiftLeft
   1.101 +    { kInt, kInt, kNoBias }, // kShiftRight
   1.102 +    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract
   1.103 +    // kSubtractInt = kSubtract
   1.104 +    { kNoType, kNoType, kNoBias },  // kSubtractScalar
   1.105 +    { kInt, kInt, kNoBias } // kXor
   1.106 +};
   1.107 +
   1.108 +// Note that the real precedence for () [] is '2'
   1.109 +// but here, precedence means 'while an equal or smaller precedence than the current operator
   1.110 +// is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply
   1.111 +// is preformed, since the add precedence is not smaller than multiply.
   1.112 +// But, (3*4 does not process the '(', since brackets are greater than all other precedences
   1.113 +#define kBracketPrecedence 16
   1.114 +#define kIfElsePrecedence 15
   1.115 +
   1.116 +const signed char SkScriptEngine::gPrecedence[] = {
   1.117 +        -1, //  kUnassigned,
   1.118 +        6, // kAdd,
   1.119 +        // kAddInt = kAdd,
   1.120 +        6, // kAddScalar,
   1.121 +        6, // kAddString,   // string concat
   1.122 +        kBracketPrecedence, // kArrayOp,
   1.123 +        10, // kBitAnd,
   1.124 +        4, // kBitNot,
   1.125 +        12, // kBitOr,
   1.126 +        5, // kDivide,
   1.127 +        // kDivideInt = kDivide,
   1.128 +        5, // kDivideScalar,
   1.129 +        kIfElsePrecedence, // kElse,
   1.130 +        9, // kEqual,
   1.131 +        // kEqualInt = kEqual,
   1.132 +        9, // kEqualScalar,
   1.133 +        9, // kEqualString,
   1.134 +        -1, // kFlipOps,
   1.135 +        8, // kGreaterEqual,
   1.136 +        // kGreaterEqualInt = kGreaterEqual,
   1.137 +        8, // kGreaterEqualScalar,
   1.138 +        8, // kGreaterEqualString,
   1.139 +        kIfElsePrecedence, // kIf,
   1.140 +        13, // kLogicalAnd,
   1.141 +        4, // kLogicalNot,
   1.142 +        14, // kLogicalOr,
   1.143 +        4, // kMinus,
   1.144 +        // kMinusInt = kMinus,
   1.145 +        4, // kMinusScalar,
   1.146 +        5, // kModulo,
   1.147 +        // kModuloInt = kModulo,
   1.148 +        5, // kModuloScalar,
   1.149 +        5, // kMultiply,
   1.150 +        // kMultiplyInt = kMultiply,
   1.151 +        5, // kMultiplyScalar,
   1.152 +        kBracketPrecedence, // kParen,
   1.153 +        7, // kShiftLeft,
   1.154 +        7, // kShiftRight,  // signed
   1.155 +        6, // kSubtract,
   1.156 +        // kSubtractInt = kSubtract,
   1.157 +        6, // kSubtractScalar,
   1.158 +        11, // kXor
   1.159 +};
   1.160 +
   1.161 +static inline bool is_between(int c, int min, int max)
   1.162 +{
   1.163 +    return (unsigned)(c - min) <= (unsigned)(max - min);
   1.164 +}
   1.165 +
   1.166 +static inline bool is_ws(int c)
   1.167 +{
   1.168 +    return is_between(c, 1, 32);
   1.169 +}
   1.170 +
   1.171 +static int token_length(const char* start) {
   1.172 +    char ch = start[0];
   1.173 +    if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
   1.174 +        return -1;
   1.175 +    int length = 0;
   1.176 +    do
   1.177 +        ch = start[++length];
   1.178 +    while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
   1.179 +        ch == '_' || ch == '$');
   1.180 +    return length;
   1.181 +}
   1.182 +
   1.183 +SkScriptEngine::SkScriptEngine(SkOpType returnType) :
   1.184 +    fTokenLength(0), fReturnType(returnType), fError(kNoError)
   1.185 +{
   1.186 +    SkSuppress noInitialSuppress;
   1.187 +    noInitialSuppress.fOperator = kUnassigned;
   1.188 +    noInitialSuppress.fOpStackDepth = 0;
   1.189 +    noInitialSuppress.fSuppress = false;
   1.190 +    noInitialSuppress.fElse = 0;
   1.191 +    fSuppressStack.push(noInitialSuppress);
   1.192 +    *fOpStack.push() = kParen;
   1.193 +    fTrackArray.appendClear();
   1.194 +    fTrackString.appendClear();
   1.195 +}
   1.196 +
   1.197 +SkScriptEngine::~SkScriptEngine() {
   1.198 +    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
   1.199 +        delete *stringPtr;
   1.200 +    for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
   1.201 +        delete *arrayPtr;
   1.202 +}
   1.203 +
   1.204 +int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) {
   1.205 +    SkOp op = kUnassigned;
   1.206 +    bool reverseOperands = false;
   1.207 +    bool negateResult = false;
   1.208 +    int advance = 1;
   1.209 +    switch (ch) {
   1.210 +        case '+':
   1.211 +            // !!! ignoring unary plus as implemented here has the side effect of
   1.212 +            // suppressing errors like +"hi"
   1.213 +            if (lastPush == false)  // unary plus, don't push an operator
   1.214 +                goto returnAdv;
   1.215 +            op = kAdd;
   1.216 +            break;
   1.217 +        case '-':
   1.218 +            op = lastPush ? kSubtract : kMinus;
   1.219 +            break;
   1.220 +        case '*':
   1.221 +            op = kMultiply;
   1.222 +            break;
   1.223 +        case '/':
   1.224 +            op = kDivide;
   1.225 +            break;
   1.226 +        case '>':
   1.227 +            if (nextChar == '>') {
   1.228 +                op = kShiftRight;
   1.229 +                goto twoChar;
   1.230 +            }
   1.231 +            op = kGreaterEqual;
   1.232 +            if (nextChar == '=')
   1.233 +                goto twoChar;
   1.234 +            reverseOperands = negateResult = true;
   1.235 +            break;
   1.236 +        case '<':
   1.237 +            if (nextChar == '<') {
   1.238 +                op = kShiftLeft;
   1.239 +                goto twoChar;
   1.240 +            }
   1.241 +            op = kGreaterEqual;
   1.242 +            reverseOperands = nextChar == '=';
   1.243 +            negateResult = ! reverseOperands;
   1.244 +            advance += reverseOperands;
   1.245 +            break;
   1.246 +        case '=':
   1.247 +            if (nextChar == '=') {
   1.248 +                op = kEqual;
   1.249 +                goto twoChar;
   1.250 +            }
   1.251 +            break;
   1.252 +        case '!':
   1.253 +            if (nextChar == '=') {
   1.254 +                op = kEqual;
   1.255 +                negateResult = true;
   1.256 +twoChar:
   1.257 +                advance++;
   1.258 +                break;
   1.259 +            }
   1.260 +            op = kLogicalNot;
   1.261 +            break;
   1.262 +        case '?':
   1.263 +            op = kIf;
   1.264 +            break;
   1.265 +        case ':':
   1.266 +            op = kElse;
   1.267 +            break;
   1.268 +        case '^':
   1.269 +            op = kXor;
   1.270 +            break;
   1.271 +        case '(':
   1.272 +            *fOpStack.push() = kParen;  // push even if eval is suppressed
   1.273 +            goto returnAdv;
   1.274 +        case '&':
   1.275 +            SkASSERT(nextChar != '&');
   1.276 +            op = kBitAnd;
   1.277 +            break;
   1.278 +        case '|':
   1.279 +            SkASSERT(nextChar != '|');
   1.280 +            op = kBitOr;
   1.281 +            break;
   1.282 +        case '%':
   1.283 +            op = kModulo;
   1.284 +            break;
   1.285 +        case '~':
   1.286 +            op = kBitNot;
   1.287 +            break;
   1.288 +    }
   1.289 +    if (op == kUnassigned)
   1.290 +        return 0;
   1.291 +    if (fSuppressStack.top().fSuppress == false) {
   1.292 +        signed char precedence = gPrecedence[op];
   1.293 +        do {
   1.294 +            int idx = 0;
   1.295 +            SkOp compare;
   1.296 +            do {
   1.297 +                compare = fOpStack.index(idx);
   1.298 +                if ((compare & kArtificialOp) == 0)
   1.299 +                    break;
   1.300 +                idx++;
   1.301 +            } while (true);
   1.302 +            signed char topPrecedence = gPrecedence[compare];
   1.303 +            SkASSERT(topPrecedence != -1);
   1.304 +            if (topPrecedence > precedence || (topPrecedence == precedence &&
   1.305 +                    gOpAttributes[op].fLeftType == kNoType)) {
   1.306 +                break;
   1.307 +            }
   1.308 +            if (processOp() == false)
   1.309 +                return 0;   // error
   1.310 +        } while (true);
   1.311 +        if (negateResult)
   1.312 +            *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp);
   1.313 +        fOpStack.push(op);
   1.314 +        if (reverseOperands)
   1.315 +            *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp);
   1.316 +    }
   1.317 +returnAdv:
   1.318 +    return advance;
   1.319 +}
   1.320 +
   1.321 +void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) {
   1.322 +    UserCallBack callBack;
   1.323 +    callBack.fBoxCallBack = func;
   1.324 +    commonCallBack(kBox, callBack, userStorage);
   1.325 +}
   1.326 +
   1.327 +void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) {
   1.328 +    callBack.fCallBackType = type;
   1.329 +    callBack.fUserStorage = userStorage;
   1.330 +    *fUserCallBacks.prepend() = callBack;
   1.331 +}
   1.332 +
   1.333 +bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params,
   1.334 +        const SkFunctionParamType* paramTypes, int paramCount) {
   1.335 +    if (params.count() > paramCount) {
   1.336 +        fError = kTooManyParameters;
   1.337 +        return false;   // too many parameters passed
   1.338 +    }
   1.339 +    for (int index = 0; index < params.count(); index++) {
   1.340 +        if (convertTo((SkDisplayTypes) paramTypes[index], &params[index]) == false)
   1.341 +            return false;
   1.342 +    }
   1.343 +    return true;
   1.344 +}
   1.345 +
   1.346 +bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) {
   1.347 +    SkDisplayTypes type = value->fType;
   1.348 +    if (type == toType)
   1.349 +        return true;
   1.350 +    if (ToOpType(type) == kObject) {
   1.351 +#if 0   // !!! I want object->string to get string from displaystringtype, not id
   1.352 +        if (ToOpType(toType) == kString) {
   1.353 +            bool success = handleObjectToString(value->fOperand.fObject);
   1.354 +            if (success == false)
   1.355 +                return false;
   1.356 +            SkOpType type;
   1.357 +            fTypeStack.pop(&type);
   1.358 +            value->fType = ToDisplayType(type);
   1.359 +            fOperandStack.pop(&value->fOperand);
   1.360 +            return true;
   1.361 +        }
   1.362 +#endif
   1.363 +        if (handleUnbox(value) == false) {
   1.364 +            fError = kHandleUnboxFailed;
   1.365 +            return false;
   1.366 +        }
   1.367 +        return convertTo(toType, value);
   1.368 +    }
   1.369 +    return ConvertTo(this, toType, value);
   1.370 +}
   1.371 +
   1.372 +bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) {
   1.373 +    size_t fieldLength = token_length(++script);        // skip dot
   1.374 +    if (fieldLength == 0) {
   1.375 +        fError = kExpectedFieldName;
   1.376 +        return false;
   1.377 +    }
   1.378 +    const char* field = script;
   1.379 +    script += fieldLength;
   1.380 +    bool success = handleProperty(suppressed);
   1.381 +    if (success == false) {
   1.382 +        fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins
   1.383 +        return false;
   1.384 +    }
   1.385 +    return evaluateDotParam(script, suppressed, field, fieldLength);
   1.386 +}
   1.387 +
   1.388 +bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed,
   1.389 +        const char* field, size_t fieldLength) {
   1.390 +    void* object;
   1.391 +    if (suppressed)
   1.392 +        object = NULL;
   1.393 +    else {
   1.394 +        if (fTypeStack.top() != kObject) {
   1.395 +            fError = kDotOperatorExpectsObject;
   1.396 +            return false;
   1.397 +        }
   1.398 +        object = fOperandStack.top().fObject;
   1.399 +        fTypeStack.pop();
   1.400 +        fOperandStack.pop();
   1.401 +    }
   1.402 +    char ch; // see if it is a simple member or a function
   1.403 +    while (is_ws(ch = script[0]))
   1.404 +        script++;
   1.405 +    bool success = true;
   1.406 +    if (ch != '(') {
   1.407 +            if (suppressed == false) {
   1.408 +                if ((success = handleMember(field, fieldLength, object)) == false)
   1.409 +                    fError = kHandleMemberFailed;
   1.410 +            }
   1.411 +    } else {
   1.412 +        SkTDArray<SkScriptValue> params;
   1.413 +        *fBraceStack.push() = kFunctionBrace;
   1.414 +        success = functionParams(&script, params);
   1.415 +        if (success && suppressed == false &&
   1.416 +                (success = handleMemberFunction(field, fieldLength, object, params)) == false)
   1.417 +            fError = kHandleMemberFunctionFailed;
   1.418 +    }
   1.419 +    return success;
   1.420 +}
   1.421 +
   1.422 +bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) {
   1.423 +#ifdef SK_DEBUG
   1.424 +    const char** original = scriptPtr;
   1.425 +#endif
   1.426 +    bool success;
   1.427 +    const char* inner;
   1.428 +    if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
   1.429 +        *scriptPtr += sizeof("#script:") - 1;
   1.430 +        if (fReturnType == kNoType || fReturnType == kString) {
   1.431 +            success = innerScript(scriptPtr, value);
   1.432 +            if (success == false)
   1.433 +                goto end;
   1.434 +            inner = value->fOperand.fString->c_str();
   1.435 +            scriptPtr = &inner;
   1.436 +        }
   1.437 +    }
   1.438 +    {
   1.439 +        success = innerScript(scriptPtr, value);
   1.440 +        if (success == false)
   1.441 +            goto end;
   1.442 +        const char* script = *scriptPtr;
   1.443 +        char ch;
   1.444 +        while (is_ws(ch = script[0]))
   1.445 +            script++;
   1.446 +        if (ch != '\0') {
   1.447 +            // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
   1.448 +            fError = kPrematureEnd;
   1.449 +            success = false;
   1.450 +        }
   1.451 +    }
   1.452 +end:
   1.453 +#ifdef SK_DEBUG
   1.454 +    if (success == false) {
   1.455 +        SkDebugf("script failed: %s", *original);
   1.456 +        if (fError)
   1.457 +            SkDebugf(" %s", errorStrings[fError - 1]);
   1.458 +        SkDebugf("\n");
   1.459 +    }
   1.460 +#endif
   1.461 +    return success;
   1.462 +}
   1.463 +
   1.464 +void SkScriptEngine::forget(SkTypedArray* array) {
   1.465 +    if (array->getType() == SkType_String) {
   1.466 +        for (int index = 0; index < array->count(); index++) {
   1.467 +            SkString* string = (*array)[index].fString;
   1.468 +            int found = fTrackString.find(string);
   1.469 +            if (found >= 0)
   1.470 +                fTrackString.remove(found);
   1.471 +        }
   1.472 +        return;
   1.473 +    }
   1.474 +    if (array->getType() == SkType_Array) {
   1.475 +        for (int index = 0; index < array->count(); index++) {
   1.476 +            SkTypedArray* child = (*array)[index].fArray;
   1.477 +            forget(child);  // forgets children of child
   1.478 +            int found = fTrackArray.find(child);
   1.479 +            if (found >= 0)
   1.480 +                fTrackArray.remove(found);
   1.481 +        }
   1.482 +    }
   1.483 +}
   1.484 +
   1.485 +void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) {
   1.486 +    UserCallBack callBack;
   1.487 +    callBack.fFunctionCallBack = func;
   1.488 +    commonCallBack(kFunction, callBack, userStorage);
   1.489 +}
   1.490 +
   1.491 +bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) {
   1.492 +    (*scriptPtr)++; // skip open paren
   1.493 +    *fOpStack.push() = kParen;
   1.494 +    *fBraceStack.push() = kFunctionBrace;
   1.495 +    SkBool suppressed = fSuppressStack.top().fSuppress;
   1.496 +    do {
   1.497 +        SkScriptValue value;
   1.498 +        bool success = innerScript(scriptPtr, suppressed ? NULL : &value);
   1.499 +        if (success == false) {
   1.500 +            fError = kErrorInFunctionParameters;
   1.501 +            return false;
   1.502 +        }
   1.503 +        if (suppressed)
   1.504 +            continue;
   1.505 +        *params.append() = value;
   1.506 +    } while ((*scriptPtr)[-1] == ',');
   1.507 +    fBraceStack.pop();
   1.508 +    fOpStack.pop(); // pop paren
   1.509 +    (*scriptPtr)++; // advance beyond close paren
   1.510 +    return true;
   1.511 +}
   1.512 +
   1.513 +#ifdef SK_DEBUG
   1.514 +bool SkScriptEngine::getErrorString(SkString* str) const {
   1.515 +    if (fError)
   1.516 +        str->set(errorStrings[fError - 1]);
   1.517 +    return fError != 0;
   1.518 +}
   1.519 +#endif
   1.520 +
   1.521 +bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) {
   1.522 +    const char* script = *scriptPtr;
   1.523 +    char ch;
   1.524 +    bool lastPush = false;
   1.525 +    bool success = true;
   1.526 +    int opBalance = fOpStack.count();
   1.527 +    int baseBrace = fBraceStack.count();
   1.528 +    int suppressBalance = fSuppressStack.count();
   1.529 +    while ((ch = script[0]) != '\0') {
   1.530 +        if (is_ws(ch)) {
   1.531 +            script++;
   1.532 +            continue;
   1.533 +        }
   1.534 +        SkBool suppressed = fSuppressStack.top().fSuppress;
   1.535 +        SkOperand operand;
   1.536 +        const char* dotCheck;
   1.537 +        if (fBraceStack.count() > baseBrace) {
   1.538 +#if 0   // disable support for struct brace
   1.539 +            if (ch == ':') {
   1.540 +                SkASSERT(fTokenLength > 0);
   1.541 +                SkASSERT(fBraceStack.top() == kStructBrace);
   1.542 +                ++script;
   1.543 +                SkASSERT(fDisplayable);
   1.544 +                SkString token(fToken, fTokenLength);
   1.545 +                fTokenLength = 0;
   1.546 +                const char* tokenName = token.c_str();
   1.547 +                const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING;
   1.548 +                if (suppressed == false) {
   1.549 +                    SkDisplayTypes type = fInfo->getType();
   1.550 +                    tokenInfo = SkDisplayType::GetMember(type, &tokenName);
   1.551 +                    SkASSERT(tokenInfo);
   1.552 +                }
   1.553 +                SkScriptValue tokenValue;
   1.554 +                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
   1.555 +                SkASSERT(success);
   1.556 +                if (suppressed == false) {
   1.557 +                    if (tokenValue.fType == SkType_Displayable) {
   1.558 +                        SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType()));
   1.559 +                        fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable);
   1.560 +                    } else {
   1.561 +                        if (tokenValue.fType != tokenInfo->getType()) {
   1.562 +                            if (convertTo(tokenInfo->getType(), &tokenValue) == false)
   1.563 +                                return false;
   1.564 +                        }
   1.565 +                        tokenInfo->writeValue(fDisplayable, NULL, 0, 0,
   1.566 +                            (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset),
   1.567 +                            tokenInfo->getType(), tokenValue);
   1.568 +                    }
   1.569 +                }
   1.570 +                lastPush = false;
   1.571 +                continue;
   1.572 +            } else
   1.573 +#endif
   1.574 +            if (fBraceStack.top() == kArrayBrace) {
   1.575 +                SkScriptValue tokenValue;
   1.576 +                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
   1.577 +                if (success == false) {
   1.578 +                    fError = kErrorInArrrayIndex;
   1.579 +                    return false;
   1.580 +                }
   1.581 +                if (suppressed == false) {
   1.582 +#if 0 // no support for structures for now
   1.583 +                    if (tokenValue.fType == SkType_Structure) {
   1.584 +                        fArrayOffset += (int) fInfo->getSize(fDisplayable);
   1.585 +                    } else
   1.586 +#endif
   1.587 +                    {
   1.588 +                        SkDisplayTypes type = ToDisplayType(fReturnType);
   1.589 +                        if (fReturnType == kNoType) {
   1.590 +                            // !!! short sighted; in the future, allow each returned array component to carry
   1.591 +                            // its own type, and let caller do any needed conversions
   1.592 +                            if (value->fOperand.fArray->count() == 0)
   1.593 +                                value->fOperand.fArray->setType(type = tokenValue.fType);
   1.594 +                            else
   1.595 +                                type = value->fOperand.fArray->getType();
   1.596 +                        }
   1.597 +                        if (tokenValue.fType != type) {
   1.598 +                            if (convertTo(type, &tokenValue) == false)
   1.599 +                                return false;
   1.600 +                        }
   1.601 +                        *value->fOperand.fArray->append() = tokenValue.fOperand;
   1.602 +                    }
   1.603 +                }
   1.604 +                lastPush = false;
   1.605 +                continue;
   1.606 +            } else {
   1.607 +                if (token_length(script) == 0) {
   1.608 +                    fError = kExpectedToken;
   1.609 +                    return false;
   1.610 +                }
   1.611 +            }
   1.612 +        }
   1.613 +        if (lastPush != false && fTokenLength > 0) {
   1.614 +            if (ch == '(') {
   1.615 +                *fBraceStack.push() = kFunctionBrace;
   1.616 +                if (handleFunction(&script, SkToBool(suppressed)) == false)
   1.617 +                    return false;
   1.618 +                lastPush = true;
   1.619 +                continue;
   1.620 +            } else if (ch == '[') {
   1.621 +                if (handleProperty(SkToBool(suppressed)) == false)
   1.622 +                    return false;   // note: never triggered by standard animator plugins
   1.623 +                if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
   1.624 +                    return false;
   1.625 +                lastPush = true;
   1.626 +                continue;
   1.627 +            } else if (ch != '.') {
   1.628 +                if (handleProperty(SkToBool(suppressed)) == false)
   1.629 +                    return false;   // note: never triggered by standard animator plugins
   1.630 +                lastPush = true;
   1.631 +                continue;
   1.632 +            }
   1.633 +        }
   1.634 +        if (ch == '0' && (script[1] & ~0x20) == 'X') {
   1.635 +            if (lastPush != false) {
   1.636 +                fError = kExpectedOperator;
   1.637 +                return false;
   1.638 +            }
   1.639 +            script += 2;
   1.640 +            script = SkParse::FindHex(script, (uint32_t*)&operand.fS32);
   1.641 +            if (script == NULL) {
   1.642 +                fError = kExpectedHex;
   1.643 +                return false;
   1.644 +            }
   1.645 +            goto intCommon;
   1.646 +        }
   1.647 +        if (lastPush == false && ch == '.')
   1.648 +            goto scalarCommon;
   1.649 +        if (ch >= '0' && ch <= '9') {
   1.650 +            if (lastPush != false) {
   1.651 +                fError = kExpectedOperator;
   1.652 +                return false;
   1.653 +            }
   1.654 +            dotCheck = SkParse::FindS32(script, &operand.fS32);
   1.655 +            if (dotCheck[0] != '.') {
   1.656 +                script = dotCheck;
   1.657 +intCommon:
   1.658 +                if (suppressed == false)
   1.659 +                    *fTypeStack.push() = kInt;
   1.660 +            } else {
   1.661 +scalarCommon:
   1.662 +                script = SkParse::FindScalar(script, &operand.fScalar);
   1.663 +                if (suppressed == false)
   1.664 +                    *fTypeStack.push() = kScalar;
   1.665 +            }
   1.666 +            if (suppressed == false)
   1.667 +                fOperandStack.push(operand);
   1.668 +            lastPush = true;
   1.669 +            continue;
   1.670 +        }
   1.671 +        int length = token_length(script);
   1.672 +        if (length > 0) {
   1.673 +            if (lastPush != false) {
   1.674 +                fError = kExpectedOperator;
   1.675 +                return false;
   1.676 +            }
   1.677 +            fToken = script;
   1.678 +            fTokenLength = length;
   1.679 +            script += length;
   1.680 +            lastPush = true;
   1.681 +            continue;
   1.682 +        }
   1.683 +        char startQuote = ch;
   1.684 +        if (startQuote == '\'' || startQuote == '\"') {
   1.685 +            if (lastPush != false) {
   1.686 +                fError = kExpectedOperator;
   1.687 +                return false;
   1.688 +            }
   1.689 +            operand.fString = new SkString();
   1.690 +            track(operand.fString);
   1.691 +            ++script;
   1.692 +
   1.693 +            // <mrr> this is a lot of calls to append() one char at at time
   1.694 +            // how hard to preflight script so we know how much to grow fString by?
   1.695 +            do {
   1.696 +                if (script[0] == '\\')
   1.697 +                    ++script;
   1.698 +                operand.fString->append(script, 1);
   1.699 +                ++script;
   1.700 +                if (script[0] == '\0') {
   1.701 +                    fError = kUnterminatedString;
   1.702 +                    return false;
   1.703 +                }
   1.704 +            } while (script[0] != startQuote);
   1.705 +            ++script;
   1.706 +            if (suppressed == false) {
   1.707 +                *fTypeStack.push() = kString;
   1.708 +                fOperandStack.push(operand);
   1.709 +            }
   1.710 +            lastPush = true;
   1.711 +            continue;
   1.712 +        }
   1.713 +        ;
   1.714 +        if (ch ==  '.') {
   1.715 +            if (fTokenLength == 0) {
   1.716 +                SkScriptValue scriptValue;
   1.717 +                SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
   1.718 +                int tokenLength = token_length(++script);
   1.719 +                const char* token = script;
   1.720 +                script += tokenLength;
   1.721 +                if (suppressed == false) {
   1.722 +                    if (fTypeStack.count() == 0) {
   1.723 +                        fError = kExpectedTokenBeforeDotOperator;
   1.724 +                        return false;
   1.725 +                    }
   1.726 +                    SkOpType topType;
   1.727 +                    fTypeStack.pop(&topType);
   1.728 +                    fOperandStack.pop(&scriptValue.fOperand);
   1.729 +                    scriptValue.fType = ToDisplayType(topType);
   1.730 +                    handleBox(&scriptValue);
   1.731 +                }
   1.732 +                success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength);
   1.733 +                if (success == false)
   1.734 +                    return false;
   1.735 +                lastPush = true;
   1.736 +                continue;
   1.737 +            }
   1.738 +            // get next token, and evaluate immediately
   1.739 +            success = evaluateDot(script, SkToBool(suppressed));
   1.740 +            if (success == false)
   1.741 +                return false;
   1.742 +            lastPush = true;
   1.743 +            continue;
   1.744 +        }
   1.745 +        if (ch == '[') {
   1.746 +            if (lastPush == false) {
   1.747 +                script++;
   1.748 +                *fBraceStack.push() = kArrayBrace;
   1.749 +                if (suppressed)
   1.750 +                    continue;
   1.751 +                operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType));
   1.752 +                track(value->fOperand.fArray);
   1.753 +                *fTypeStack.push() = (SkOpType) kArray;
   1.754 +                fOperandStack.push(operand);
   1.755 +                continue;
   1.756 +            }
   1.757 +            if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
   1.758 +                return false;
   1.759 +            lastPush = true;
   1.760 +            continue;
   1.761 +        }
   1.762 +#if 0 // structs not supported for now
   1.763 +        if (ch == '{') {
   1.764 +            if (lastPush == false) {
   1.765 +                script++;
   1.766 +                *fBraceStack.push() = kStructBrace;
   1.767 +                if (suppressed)
   1.768 +                    continue;
   1.769 +                operand.fS32 = 0;
   1.770 +                *fTypeStack.push() = (SkOpType) kStruct;
   1.771 +                fOperandStack.push(operand);
   1.772 +                continue;
   1.773 +            }
   1.774 +            SkASSERT(0); // braces in other contexts aren't supported yet
   1.775 +        }
   1.776 +#endif
   1.777 +        if (ch == ')' && fBraceStack.count() > 0) {
   1.778 +            SkBraceStyle braceStyle = fBraceStack.top();
   1.779 +            if (braceStyle == kFunctionBrace) {
   1.780 +                fBraceStack.pop();
   1.781 +                break;
   1.782 +            }
   1.783 +        }
   1.784 +        if (ch == ',' || ch == ']') {
   1.785 +            if (ch != ',') {
   1.786 +                SkBraceStyle match;
   1.787 +                fBraceStack.pop(&match);
   1.788 +                if (match != kArrayBrace) {
   1.789 +                    fError = kMismatchedArrayBrace;
   1.790 +                    return false;
   1.791 +                }
   1.792 +            }
   1.793 +            script++;
   1.794 +            // !!! see if brace or bracket is correct closer
   1.795 +            break;
   1.796 +        }
   1.797 +        char nextChar = script[1];
   1.798 +        int advance = logicalOp(ch, nextChar);
   1.799 +        if (advance < 0)     // error
   1.800 +            return false;
   1.801 +        if (advance == 0)
   1.802 +            advance = arithmeticOp(ch, nextChar, lastPush);
   1.803 +        if (advance == 0) // unknown token
   1.804 +            return false;
   1.805 +        if (advance > 0)
   1.806 +            script += advance;
   1.807 +        lastPush = ch == ']' || ch == ')';
   1.808 +    }
   1.809 +    bool suppressed = SkToBool(fSuppressStack.top().fSuppress);
   1.810 +    if (fTokenLength > 0) {
   1.811 +        success = handleProperty(suppressed);
   1.812 +        if (success == false)
   1.813 +            return false;   // note: never triggered by standard animator plugins
   1.814 +    }
   1.815 +    while (fOpStack.count() > opBalance) {   // leave open paren
   1.816 +        if ((fError = opError()) != kNoError)
   1.817 +            return false;
   1.818 +        if (processOp() == false)
   1.819 +            return false;
   1.820 +    }
   1.821 +    SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType;
   1.822 +    if (suppressed == false && topType != fReturnType &&
   1.823 +            topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value
   1.824 +        SkString* string = fOperandStack.top().fString;
   1.825 +        fToken = string->c_str();
   1.826 +        fTokenLength = string->size();
   1.827 +        fOperandStack.pop();
   1.828 +        fTypeStack.pop();
   1.829 +        success = handleProperty(SkToBool(fSuppressStack.top().fSuppress));
   1.830 +        if (success == false) { // if it couldn't convert, return string (error?)
   1.831 +            SkOperand operand;
   1.832 +            operand.fS32 = 0;
   1.833 +            *fTypeStack.push() = kString;
   1.834 +            operand.fString = string;
   1.835 +            fOperandStack.push(operand);
   1.836 +        }
   1.837 +    }
   1.838 +    if (value) {
   1.839 +        if (fOperandStack.count() == 0)
   1.840 +            return false;
   1.841 +        SkASSERT(fOperandStack.count() >= 1);
   1.842 +        SkASSERT(fTypeStack.count() >= 1);
   1.843 +        fOperandStack.pop(&value->fOperand);
   1.844 +        SkOpType type;
   1.845 +        fTypeStack.pop(&type);
   1.846 +        value->fType = ToDisplayType(type);
   1.847 +//      SkASSERT(value->fType != SkType_Unknown);
   1.848 +        if (topType != fReturnType && topType == kObject && fReturnType != kNoType) {
   1.849 +            if (convertTo(ToDisplayType(fReturnType), value) == false)
   1.850 +                return false;
   1.851 +        }
   1.852 +    }
   1.853 +    while (fSuppressStack.count() > suppressBalance)
   1.854 +        fSuppressStack.pop();
   1.855 +    *scriptPtr = script;
   1.856 +    return true; // no error
   1.857 +}
   1.858 +
   1.859 +void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) {
   1.860 +    UserCallBack callBack;
   1.861 +    callBack.fMemberCallBack = member;
   1.862 +    commonCallBack(kMember, callBack, userStorage);
   1.863 +}
   1.864 +
   1.865 +void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) {
   1.866 +    UserCallBack callBack;
   1.867 +    callBack.fMemberFunctionCallBack = func;
   1.868 +    commonCallBack(kMemberFunction, callBack, userStorage);
   1.869 +}
   1.870 +
   1.871 +#if 0
   1.872 +void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) {
   1.873 +    UserCallBack callBack;
   1.874 +    callBack.fObjectToStringCallBack = func;
   1.875 +    commonCallBack(kObjectToString, callBack, userStorage);
   1.876 +}
   1.877 +#endif
   1.878 +
   1.879 +bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) {
   1.880 +    SkScriptValue scriptValue;
   1.881 +    (*scriptPtr)++;
   1.882 +    *fOpStack.push() = kParen;
   1.883 +    *fBraceStack.push() = kArrayBrace;
   1.884 +    SkOpType saveType = fReturnType;
   1.885 +    fReturnType = kInt;
   1.886 +    bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL);
   1.887 +    if (success == false)
   1.888 +        return false;
   1.889 +    fReturnType = saveType;
   1.890 +    if (suppressed == false) {
   1.891 +        if (convertTo(SkType_Int, &scriptValue) == false)
   1.892 +            return false;
   1.893 +        int index = scriptValue.fOperand.fS32;
   1.894 +        SkScriptValue scriptValue;
   1.895 +        SkOpType type;
   1.896 +        fTypeStack.pop(&type);
   1.897 +        fOperandStack.pop(&scriptValue.fOperand);
   1.898 +        scriptValue.fType = ToDisplayType(type);
   1.899 +        if (type == kObject) {
   1.900 +            success = handleUnbox(&scriptValue);
   1.901 +            if (success == false)
   1.902 +                return false;
   1.903 +            if (ToOpType(scriptValue.fType) != kArray) {
   1.904 +                fError = kExpectedArray;
   1.905 +                return false;
   1.906 +            }
   1.907 +        }
   1.908 +        *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType();
   1.909 +//      SkASSERT(index >= 0);
   1.910 +        if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
   1.911 +            fError = kArrayIndexOutOfBounds;
   1.912 +            return false;
   1.913 +        }
   1.914 +        scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
   1.915 +        fOperandStack.push(scriptValue.fOperand);
   1.916 +    }
   1.917 +    fOpStack.pop(); // pop paren
   1.918 +    return success;
   1.919 +}
   1.920 +
   1.921 +bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) {
   1.922 +    bool success = true;
   1.923 +    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1.924 +        if (callBack->fCallBackType != kBox)
   1.925 +            continue;
   1.926 +        success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue);
   1.927 +        if (success) {
   1.928 +            fOperandStack.push(scriptValue->fOperand);
   1.929 +            *fTypeStack.push() = ToOpType(scriptValue->fType);
   1.930 +            goto done;
   1.931 +        }
   1.932 +    }
   1.933 +done:
   1.934 +    return success;
   1.935 +}
   1.936 +
   1.937 +bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) {
   1.938 +    SkScriptValue callbackResult;
   1.939 +    SkTDArray<SkScriptValue> params;
   1.940 +    SkString functionName(fToken, fTokenLength);
   1.941 +    fTokenLength = 0;
   1.942 +    bool success = functionParams(scriptPtr, params);
   1.943 +    if (success == false)
   1.944 +        goto done;
   1.945 +    if (suppressed == true)
   1.946 +        return true;
   1.947 +    {
   1.948 +        for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1.949 +            if (callBack->fCallBackType != kFunction)
   1.950 +                continue;
   1.951 +            success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params,
   1.952 +                callBack->fUserStorage, &callbackResult);
   1.953 +            if (success) {
   1.954 +                fOperandStack.push(callbackResult.fOperand);
   1.955 +                *fTypeStack.push() = ToOpType(callbackResult.fType);
   1.956 +                goto done;
   1.957 +            }
   1.958 +        }
   1.959 +    }
   1.960 +    fError = kNoFunctionHandlerFound;
   1.961 +    return false;
   1.962 +done:
   1.963 +    return success;
   1.964 +}
   1.965 +
   1.966 +bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) {
   1.967 +    SkScriptValue callbackResult;
   1.968 +    bool success = true;
   1.969 +    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1.970 +        if (callBack->fCallBackType != kMember)
   1.971 +            continue;
   1.972 +        success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult);
   1.973 +        if (success) {
   1.974 +            if (callbackResult.fType == SkType_String)
   1.975 +                track(callbackResult.fOperand.fString);
   1.976 +            fOperandStack.push(callbackResult.fOperand);
   1.977 +            *fTypeStack.push() = ToOpType(callbackResult.fType);
   1.978 +            goto done;
   1.979 +        }
   1.980 +    }
   1.981 +    return false;
   1.982 +done:
   1.983 +    return success;
   1.984 +}
   1.985 +
   1.986 +bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) {
   1.987 +    SkScriptValue callbackResult;
   1.988 +    bool success = true;
   1.989 +    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
   1.990 +        if (callBack->fCallBackType != kMemberFunction)
   1.991 +            continue;
   1.992 +        success = (*callBack->fMemberFunctionCallBack)(field, len, object, params,
   1.993 +            callBack->fUserStorage, &callbackResult);
   1.994 +        if (success) {
   1.995 +            if (callbackResult.fType == SkType_String)
   1.996 +                track(callbackResult.fOperand.fString);
   1.997 +            fOperandStack.push(callbackResult.fOperand);
   1.998 +            *fTypeStack.push() = ToOpType(callbackResult.fType);
   1.999 +            goto done;
  1.1000 +        }
  1.1001 +    }
  1.1002 +    return false;
  1.1003 +done:
  1.1004 +    return success;
  1.1005 +}
  1.1006 +
  1.1007 +#if 0
  1.1008 +bool SkScriptEngine::handleObjectToString(void* object) {
  1.1009 +    SkScriptValue callbackResult;
  1.1010 +    bool success = true;
  1.1011 +    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
  1.1012 +        if (callBack->fCallBackType != kObjectToString)
  1.1013 +            continue;
  1.1014 +        success = (*callBack->fObjectToStringCallBack)(object,
  1.1015 +            callBack->fUserStorage, &callbackResult);
  1.1016 +        if (success) {
  1.1017 +            if (callbackResult.fType == SkType_String)
  1.1018 +                track(callbackResult.fOperand.fString);
  1.1019 +            fOperandStack.push(callbackResult.fOperand);
  1.1020 +            *fTypeStack.push() = ToOpType(callbackResult.fType);
  1.1021 +            goto done;
  1.1022 +        }
  1.1023 +    }
  1.1024 +    return false;
  1.1025 +done:
  1.1026 +    return success;
  1.1027 +}
  1.1028 +#endif
  1.1029 +
  1.1030 +bool SkScriptEngine::handleProperty(bool suppressed) {
  1.1031 +    SkScriptValue callbackResult;
  1.1032 +    bool success = true;
  1.1033 +    if (suppressed)
  1.1034 +        goto done;
  1.1035 +    success = false; // note that with standard animator-script plugins, callback never returns false
  1.1036 +    {
  1.1037 +        for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
  1.1038 +            if (callBack->fCallBackType != kProperty)
  1.1039 +                continue;
  1.1040 +            success = (*callBack->fPropertyCallBack)(fToken, fTokenLength,
  1.1041 +                callBack->fUserStorage, &callbackResult);
  1.1042 +            if (success) {
  1.1043 +                if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) {
  1.1044 +                    callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
  1.1045 +                    track(callbackResult.fOperand.fString);
  1.1046 +                }
  1.1047 +                fOperandStack.push(callbackResult.fOperand);
  1.1048 +                *fTypeStack.push() = ToOpType(callbackResult.fType);
  1.1049 +                goto done;
  1.1050 +            }
  1.1051 +        }
  1.1052 +    }
  1.1053 +done:
  1.1054 +    fTokenLength = 0;
  1.1055 +    return success;
  1.1056 +}
  1.1057 +
  1.1058 +bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) {
  1.1059 +    bool success = true;
  1.1060 +    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
  1.1061 +        if (callBack->fCallBackType != kUnbox)
  1.1062 +            continue;
  1.1063 +        success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue);
  1.1064 +        if (success) {
  1.1065 +            if (scriptValue->fType == SkType_String)
  1.1066 +                track(scriptValue->fOperand.fString);
  1.1067 +            goto done;
  1.1068 +        }
  1.1069 +    }
  1.1070 +    return false;
  1.1071 +done:
  1.1072 +    return success;
  1.1073 +}
  1.1074 +
  1.1075 +// note that entire expression is treated as if it were enclosed in parens
  1.1076 +// an open paren is always the first thing in the op stack
  1.1077 +
  1.1078 +int SkScriptEngine::logicalOp(char ch, char nextChar) {
  1.1079 +    int advance = 1;
  1.1080 +    SkOp match;
  1.1081 +    signed char precedence;
  1.1082 +    switch (ch) {
  1.1083 +        case ')':
  1.1084 +            match = kParen;
  1.1085 +            break;
  1.1086 +        case ']':
  1.1087 +            match = kArrayOp;
  1.1088 +            break;
  1.1089 +        case '?':
  1.1090 +            match = kIf;
  1.1091 +            break;
  1.1092 +        case ':':
  1.1093 +            match = kElse;
  1.1094 +            break;
  1.1095 +        case '&':
  1.1096 +            if (nextChar != '&')
  1.1097 +                goto noMatch;
  1.1098 +            match = kLogicalAnd;
  1.1099 +            advance = 2;
  1.1100 +            break;
  1.1101 +        case '|':
  1.1102 +            if (nextChar != '|')
  1.1103 +                goto noMatch;
  1.1104 +            match = kLogicalOr;
  1.1105 +            advance = 2;
  1.1106 +            break;
  1.1107 +        default:
  1.1108 +noMatch:
  1.1109 +            return 0;
  1.1110 +    }
  1.1111 +    SkSuppress suppress;
  1.1112 +    precedence = gPrecedence[match];
  1.1113 +    if (fSuppressStack.top().fSuppress) {
  1.1114 +        if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) {
  1.1115 +            SkOp topOp = fOpStack.top();
  1.1116 +            if (gPrecedence[topOp] <= precedence)
  1.1117 +                fOpStack.pop();
  1.1118 +            goto goHome;
  1.1119 +        }
  1.1120 +        bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence;
  1.1121 +        if (changedPrecedence)
  1.1122 +            fSuppressStack.pop();
  1.1123 +        if (precedence == kIfElsePrecedence) {
  1.1124 +            if (match == kIf) {
  1.1125 +                if (changedPrecedence)
  1.1126 +                    fOpStack.pop();
  1.1127 +                else
  1.1128 +                    *fOpStack.push() = kIf;
  1.1129 +            } else {
  1.1130 +                if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) {
  1.1131 +                    goto flipSuppress;
  1.1132 +                }
  1.1133 +                fOpStack.pop();
  1.1134 +            }
  1.1135 +        }
  1.1136 +        if (changedPrecedence == false)
  1.1137 +            goto goHome;
  1.1138 +    }
  1.1139 +    while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) {
  1.1140 +        if (processOp() == false)
  1.1141 +            return false;
  1.1142 +    }
  1.1143 +    if (fSuppressStack.top().fOpStackDepth > fOpStack.count())
  1.1144 +        fSuppressStack.pop();
  1.1145 +    switch (match) {
  1.1146 +        case kParen:
  1.1147 +        case kArrayOp:
  1.1148 +            if (fOpStack.count() <= 1 || fOpStack.top() != match) {
  1.1149 +                fError = kMismatchedBrackets;
  1.1150 +                return -1;
  1.1151 +            }
  1.1152 +            if (match == kParen)
  1.1153 +                fOpStack.pop();
  1.1154 +            else {
  1.1155 +                SkOpType indexType;
  1.1156 +                fTypeStack.pop(&indexType);
  1.1157 +                if (indexType != kInt && indexType != kScalar) {
  1.1158 +                    fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually)
  1.1159 +                    return -1;
  1.1160 +                }
  1.1161 +                SkOperand indexOperand;
  1.1162 +                fOperandStack.pop(&indexOperand);
  1.1163 +                int index = indexType == kScalar ? SkScalarFloorToInt(indexOperand.fScalar) :
  1.1164 +                    indexOperand.fS32;
  1.1165 +                SkOpType arrayType;
  1.1166 +                fTypeStack.pop(&arrayType);
  1.1167 +                if ((unsigned)arrayType != (unsigned)kArray) {
  1.1168 +                    fError = kExpectedArray;
  1.1169 +                    return -1;
  1.1170 +                }
  1.1171 +                SkOperand arrayOperand;
  1.1172 +                fOperandStack.pop(&arrayOperand);
  1.1173 +                SkTypedArray* array = arrayOperand.fArray;
  1.1174 +                SkOperand operand;
  1.1175 +                if (array->getIndex(index, &operand) == false) {
  1.1176 +                    fError = kIndexOutOfRange;
  1.1177 +                    return -1;
  1.1178 +                }
  1.1179 +                SkOpType resultType = array->getOpType();
  1.1180 +                fTypeStack.push(resultType);
  1.1181 +                fOperandStack.push(operand);
  1.1182 +            }
  1.1183 +            break;
  1.1184 +        case kIf: {
  1.1185 +            SkScriptValue ifValue;
  1.1186 +            SkOpType ifType;
  1.1187 +            fTypeStack.pop(&ifType);
  1.1188 +            ifValue.fType = ToDisplayType(ifType);
  1.1189 +            fOperandStack.pop(&ifValue.fOperand);
  1.1190 +            if (convertTo(SkType_Int, &ifValue) == false)
  1.1191 +                return -1;
  1.1192 +            if (ifValue.fType != SkType_Int) {
  1.1193 +                fError = kExpectedIntForConditionOperator;
  1.1194 +                return -1;
  1.1195 +            }
  1.1196 +            suppress.fSuppress = ifValue.fOperand.fS32 == 0;
  1.1197 +            suppress.fOperator = kIf;
  1.1198 +            suppress.fOpStackDepth = fOpStack.count();
  1.1199 +            suppress.fElse = false;
  1.1200 +            fSuppressStack.push(suppress);
  1.1201 +            // if left is true, do only up to colon
  1.1202 +            // if left is false, do only after colon
  1.1203 +            } break;
  1.1204 +        case kElse:
  1.1205 +flipSuppress:
  1.1206 +            if (fSuppressStack.top().fElse)
  1.1207 +                fSuppressStack.pop();
  1.1208 +            fSuppressStack.top().fElse = true;
  1.1209 +            fSuppressStack.top().fSuppress ^= true;
  1.1210 +            // flip last do / don't do consideration from last '?'
  1.1211 +            break;
  1.1212 +        case kLogicalAnd:
  1.1213 +        case kLogicalOr: {
  1.1214 +            if (fTypeStack.top() != kInt) {
  1.1215 +                fError = kExpectedBooleanExpression;
  1.1216 +                return -1;
  1.1217 +            }
  1.1218 +            int32_t topInt = fOperandStack.top().fS32;
  1.1219 +            if (fOpStack.top() != kLogicalAnd)
  1.1220 +                *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or'
  1.1221 +            if (match == kLogicalOr ? topInt != 0 : topInt == 0) {
  1.1222 +                suppress.fSuppress = true;
  1.1223 +                suppress.fOperator = match;
  1.1224 +                suppress.fOpStackDepth = fOpStack.count();
  1.1225 +                suppress.fElse = false;
  1.1226 +                fSuppressStack.push(suppress);
  1.1227 +            } else {
  1.1228 +                fTypeStack.pop();
  1.1229 +                fOperandStack.pop();
  1.1230 +            }
  1.1231 +        }   break;
  1.1232 +        default:
  1.1233 +            SkASSERT(0);
  1.1234 +    }
  1.1235 +goHome:
  1.1236 +    return advance;
  1.1237 +}
  1.1238 +
  1.1239 +SkScriptEngine::Error SkScriptEngine::opError() {
  1.1240 +    int opCount = fOpStack.count();
  1.1241 +    int operandCount = fOperandStack.count();
  1.1242 +    if (opCount == 0) {
  1.1243 +        if (operandCount != 1)
  1.1244 +            return kExpectedOperator;
  1.1245 +        return kNoError;
  1.1246 +    }
  1.1247 +    SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp);
  1.1248 +    const SkOperatorAttributes* attributes = &gOpAttributes[op];
  1.1249 +    if (attributes->fLeftType != kNoType && operandCount < 2)
  1.1250 +        return kExpectedValue;
  1.1251 +    if (attributes->fLeftType == kNoType && operandCount < 1)
  1.1252 +        return kExpectedValue;
  1.1253 +    return kNoError;
  1.1254 +}
  1.1255 +
  1.1256 +bool SkScriptEngine::processOp() {
  1.1257 +    SkOp op;
  1.1258 +    fOpStack.pop(&op);
  1.1259 +    op = (SkOp) (op & ~kArtificialOp);
  1.1260 +    const SkOperatorAttributes* attributes = &gOpAttributes[op];
  1.1261 +    SkOpType type2;
  1.1262 +    fTypeStack.pop(&type2);
  1.1263 +    SkOpType type1 = type2;
  1.1264 +    SkOperand operand2;
  1.1265 +    fOperandStack.pop(&operand2);
  1.1266 +    SkOperand operand1 = operand2; // !!! not really needed, suppresses warning
  1.1267 +    if (attributes->fLeftType != kNoType) {
  1.1268 +        fTypeStack.pop(&type1);
  1.1269 +        fOperandStack.pop(&operand1);
  1.1270 +        if (op == kFlipOps) {
  1.1271 +            SkTSwap(type1, type2);
  1.1272 +            SkTSwap(operand1, operand2);
  1.1273 +            fOpStack.pop(&op);
  1.1274 +            op = (SkOp) (op & ~kArtificialOp);
  1.1275 +            attributes = &gOpAttributes[op];
  1.1276 +        }
  1.1277 +        if (type1 == kObject && (type1 & attributes->fLeftType) == 0) {
  1.1278 +            SkScriptValue val;
  1.1279 +            val.fType = ToDisplayType(type1);
  1.1280 +            val.fOperand = operand1;
  1.1281 +            bool success = handleUnbox(&val);
  1.1282 +            if (success == false)
  1.1283 +                return false;
  1.1284 +            type1 = ToOpType(val.fType);
  1.1285 +            operand1 = val.fOperand;
  1.1286 +        }
  1.1287 +    }
  1.1288 +    if (type2 == kObject && (type2 & attributes->fLeftType) == 0) {
  1.1289 +        SkScriptValue val;
  1.1290 +        val.fType = ToDisplayType(type2);
  1.1291 +        val.fOperand = operand2;
  1.1292 +        bool success = handleUnbox(&val);
  1.1293 +        if (success == false)
  1.1294 +            return false;
  1.1295 +        type2 = ToOpType(val.fType);
  1.1296 +        operand2 = val.fOperand;
  1.1297 +    }
  1.1298 +    if (attributes->fLeftType != kNoType) {
  1.1299 +        if (type1 != type2) {
  1.1300 +            if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) {
  1.1301 +                if (type1 == kInt || type1 == kScalar) {
  1.1302 +                    convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float);
  1.1303 +                    type1 = kString;
  1.1304 +                }
  1.1305 +                if (type2 == kInt || type2 == kScalar) {
  1.1306 +                    convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float);
  1.1307 +                    type2 = kString;
  1.1308 +                }
  1.1309 +            } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) {
  1.1310 +                if (type1 == kInt) {
  1.1311 +                    operand1.fScalar = IntToScalar(operand1.fS32);
  1.1312 +                    type1 = kScalar;
  1.1313 +                }
  1.1314 +                if (type2 == kInt) {
  1.1315 +                    operand2.fScalar = IntToScalar(operand2.fS32);
  1.1316 +                     type2 = kScalar;
  1.1317 +                }
  1.1318 +            }
  1.1319 +        }
  1.1320 +        if ((type1 & attributes->fLeftType) == 0 || type1 != type2) {
  1.1321 +            if (type1 == kString) {
  1.1322 +                const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar);
  1.1323 +                if (result == NULL) {
  1.1324 +                    fError = kExpectedNumber;
  1.1325 +                    return false;
  1.1326 +                }
  1.1327 +                type1 = kScalar;
  1.1328 +            }
  1.1329 +            if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) {
  1.1330 +                operand1.fS32 = SkScalarFloorToInt(operand1.fScalar);
  1.1331 +                type1 = kInt;
  1.1332 +            }
  1.1333 +        }
  1.1334 +    }
  1.1335 +    if ((type2 & attributes->fRightType) == 0 || type1 != type2) {
  1.1336 +        if (type2 == kString) {
  1.1337 +            const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar);
  1.1338 +            if (result == NULL) {
  1.1339 +                fError = kExpectedNumber;
  1.1340 +                return false;
  1.1341 +            }
  1.1342 +            type2 = kScalar;
  1.1343 +        }
  1.1344 +        if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) {
  1.1345 +            operand2.fS32 = SkScalarFloorToInt(operand2.fScalar);
  1.1346 +            type2 = kInt;
  1.1347 +        }
  1.1348 +    }
  1.1349 +    if (type2 == kScalar)
  1.1350 +        op = (SkOp) (op + 1);
  1.1351 +    else if (type2 == kString)
  1.1352 +        op = (SkOp) (op + 2);
  1.1353 +    switch(op) {
  1.1354 +        case kAddInt:
  1.1355 +            operand2.fS32 += operand1.fS32;
  1.1356 +            break;
  1.1357 +        case kAddScalar:
  1.1358 +            operand2.fScalar += operand1.fScalar;
  1.1359 +            break;
  1.1360 +        case kAddString:
  1.1361 +            if (fTrackString.find(operand1.fString) < 0) {
  1.1362 +                operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString));
  1.1363 +                track(operand1.fString);
  1.1364 +            }
  1.1365 +            operand1.fString->append(*operand2.fString);
  1.1366 +            operand2 = operand1;
  1.1367 +            break;
  1.1368 +        case kBitAnd:
  1.1369 +            operand2.fS32 &= operand1.fS32;
  1.1370 +            break;
  1.1371 +        case kBitNot:
  1.1372 +            operand2.fS32 = ~operand2.fS32;
  1.1373 +            break;
  1.1374 +        case kBitOr:
  1.1375 +            operand2.fS32 |= operand1.fS32;
  1.1376 +            break;
  1.1377 +        case kDivideInt:
  1.1378 +            if (operand2.fS32 == 0) {
  1.1379 +                operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
  1.1380 +                break;
  1.1381 +            } else {
  1.1382 +                int32_t original = operand2.fS32;
  1.1383 +                operand2.fS32 = operand1.fS32 / operand2.fS32;
  1.1384 +                if (original * operand2.fS32 == operand1.fS32)
  1.1385 +                    break;    // integer divide was good enough
  1.1386 +                operand2.fS32 = original;
  1.1387 +                type2 = kScalar;
  1.1388 +            }
  1.1389 +        case kDivideScalar:
  1.1390 +            if (operand2.fScalar == 0)
  1.1391 +                operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
  1.1392 +            else
  1.1393 +                operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar);
  1.1394 +            break;
  1.1395 +        case kEqualInt:
  1.1396 +            operand2.fS32 = operand1.fS32 == operand2.fS32;
  1.1397 +            break;
  1.1398 +        case kEqualScalar:
  1.1399 +            operand2.fS32 = operand1.fScalar == operand2.fScalar;
  1.1400 +            type2 = kInt;
  1.1401 +            break;
  1.1402 +        case kEqualString:
  1.1403 +            operand2.fS32 = *operand1.fString == *operand2.fString;
  1.1404 +            type2 = kInt;
  1.1405 +            break;
  1.1406 +        case kGreaterEqualInt:
  1.1407 +            operand2.fS32 = operand1.fS32 >= operand2.fS32;
  1.1408 +            break;
  1.1409 +        case kGreaterEqualScalar:
  1.1410 +            operand2.fS32 = operand1.fScalar >= operand2.fScalar;
  1.1411 +            type2 = kInt;
  1.1412 +            break;
  1.1413 +        case kGreaterEqualString:
  1.1414 +            operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0;
  1.1415 +            type2 = kInt;
  1.1416 +            break;
  1.1417 +        case kLogicalAnd:
  1.1418 +            operand2.fS32 = !! operand2.fS32;   // really, ToBool
  1.1419 +            break;
  1.1420 +        case kLogicalNot:
  1.1421 +            operand2.fS32 = ! operand2.fS32;
  1.1422 +            break;
  1.1423 +        case kLogicalOr:
  1.1424 +            SkASSERT(0);    // should have already been processed
  1.1425 +            break;
  1.1426 +        case kMinusInt:
  1.1427 +            operand2.fS32 = -operand2.fS32;
  1.1428 +            break;
  1.1429 +        case kMinusScalar:
  1.1430 +            operand2.fScalar = -operand2.fScalar;
  1.1431 +            break;
  1.1432 +        case kModuloInt:
  1.1433 +            operand2.fS32 = operand1.fS32 % operand2.fS32;
  1.1434 +            break;
  1.1435 +        case kModuloScalar:
  1.1436 +            operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar);
  1.1437 +            break;
  1.1438 +        case kMultiplyInt:
  1.1439 +            operand2.fS32 *= operand1.fS32;
  1.1440 +            break;
  1.1441 +        case kMultiplyScalar:
  1.1442 +            operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar);
  1.1443 +            break;
  1.1444 +        case kShiftLeft:
  1.1445 +            operand2.fS32 = operand1.fS32 << operand2.fS32;
  1.1446 +            break;
  1.1447 +        case kShiftRight:
  1.1448 +            operand2.fS32 = operand1.fS32 >> operand2.fS32;
  1.1449 +            break;
  1.1450 +        case kSubtractInt:
  1.1451 +            operand2.fS32 = operand1.fS32 - operand2.fS32;
  1.1452 +            break;
  1.1453 +        case kSubtractScalar:
  1.1454 +            operand2.fScalar = operand1.fScalar - operand2.fScalar;
  1.1455 +            break;
  1.1456 +        case kXor:
  1.1457 +            operand2.fS32 ^= operand1.fS32;
  1.1458 +            break;
  1.1459 +        default:
  1.1460 +            SkASSERT(0);
  1.1461 +    }
  1.1462 +    fTypeStack.push(type2);
  1.1463 +    fOperandStack.push(operand2);
  1.1464 +    return true;
  1.1465 +}
  1.1466 +
  1.1467 +void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) {
  1.1468 +    UserCallBack callBack;
  1.1469 +    callBack.fPropertyCallBack = prop;
  1.1470 +    commonCallBack(kProperty, callBack, userStorage);
  1.1471 +}
  1.1472 +
  1.1473 +void SkScriptEngine::track(SkTypedArray* array) {
  1.1474 +    SkASSERT(fTrackArray.find(array) < 0);
  1.1475 +    *(fTrackArray.end() - 1) = array;
  1.1476 +    fTrackArray.appendClear();
  1.1477 +}
  1.1478 +
  1.1479 +void SkScriptEngine::track(SkString* string) {
  1.1480 +    SkASSERT(fTrackString.find(string) < 0);
  1.1481 +    *(fTrackString.end() - 1) = string;
  1.1482 +    fTrackString.appendClear();
  1.1483 +}
  1.1484 +
  1.1485 +void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) {
  1.1486 +    UserCallBack callBack;
  1.1487 +    callBack.fUnboxCallBack = func;
  1.1488 +    commonCallBack(kUnbox, callBack, userStorage);
  1.1489 +}
  1.1490 +
  1.1491 +bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) {
  1.1492 +    SkASSERT(value);
  1.1493 +    if (SkDisplayType::IsEnum(NULL /* fMaker */, toType))
  1.1494 +        toType = SkType_Int;
  1.1495 +    if (toType == SkType_Point || toType == SkType_3D_Point)
  1.1496 +        toType = SkType_Float;
  1.1497 +    if (toType == SkType_Drawable)
  1.1498 +        toType = SkType_Displayable;
  1.1499 +    SkDisplayTypes type = value->fType;
  1.1500 +    if (type == toType)
  1.1501 +        return true;
  1.1502 +    SkOperand& operand = value->fOperand;
  1.1503 +    bool success = true;
  1.1504 +    switch (toType) {
  1.1505 +        case SkType_Int:
  1.1506 +            if (type == SkType_Boolean)
  1.1507 +                break;
  1.1508 +            if (type == SkType_Float)
  1.1509 +                operand.fS32 = SkScalarFloorToInt(operand.fScalar);
  1.1510 +            else {
  1.1511 +                if (type != SkType_String) {
  1.1512 +                    success = false;
  1.1513 +                    break; // error
  1.1514 +                }
  1.1515 +                success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
  1.1516 +            }
  1.1517 +            break;
  1.1518 +        case SkType_Float:
  1.1519 +            if (type == SkType_Int) {
  1.1520 +                if (operand.fS32 == SK_NaN32)
  1.1521 +                    operand.fScalar = SK_ScalarNaN;
  1.1522 +                else if (SkAbs32(operand.fS32) == SK_MaxS32)
  1.1523 +                    operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
  1.1524 +                else
  1.1525 +                    operand.fScalar = SkIntToScalar(operand.fS32);
  1.1526 +            } else {
  1.1527 +                if (type != SkType_String) {
  1.1528 +                    success = false;
  1.1529 +                    break; // error
  1.1530 +                }
  1.1531 +                success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
  1.1532 +            }
  1.1533 +            break;
  1.1534 +        case SkType_String: {
  1.1535 +            SkString* strPtr = new SkString();
  1.1536 +            SkASSERT(engine);
  1.1537 +            engine->track(strPtr);
  1.1538 +            if (type == SkType_Int) {
  1.1539 +                strPtr->appendS32(operand.fS32);
  1.1540 +            } else if (type == SkType_Displayable) {
  1.1541 +                SkASSERT(0); // must call through instance version instead of static version
  1.1542 +            } else {
  1.1543 +                if (type != SkType_Float) {
  1.1544 +                    success = false;
  1.1545 +                    break;
  1.1546 +                }
  1.1547 +                strPtr->appendScalar(operand.fScalar);
  1.1548 +            }
  1.1549 +            operand.fString = strPtr;
  1.1550 +            } break;
  1.1551 +        case SkType_Array: {
  1.1552 +            SkTypedArray* array = new SkTypedArray(type);
  1.1553 +            *array->append() = operand;
  1.1554 +            engine->track(array);
  1.1555 +            operand.fArray = array;
  1.1556 +            } break;
  1.1557 +        default:
  1.1558 +            SkASSERT(0);
  1.1559 +    }
  1.1560 +    value->fType = toType;
  1.1561 +    if (success == false)
  1.1562 +        engine->fError = kTypeConversionFailed;
  1.1563 +    return success;
  1.1564 +}
  1.1565 +
  1.1566 +SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
  1.1567 +    SkScalar scalar;
  1.1568 +    if (s32 == SK_NaN32)
  1.1569 +        scalar = SK_ScalarNaN;
  1.1570 +    else if (SkAbs32(s32) == SK_MaxS32)
  1.1571 +        scalar = SkSign32(s32) * SK_ScalarMax;
  1.1572 +    else
  1.1573 +        scalar = SkIntToScalar(s32);
  1.1574 +    return scalar;
  1.1575 +}
  1.1576 +
  1.1577 +SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) {
  1.1578 +    int val = type;
  1.1579 +    switch (val) {
  1.1580 +        case kNoType:
  1.1581 +            return SkType_Unknown;
  1.1582 +        case kInt:
  1.1583 +            return SkType_Int;
  1.1584 +        case kScalar:
  1.1585 +            return SkType_Float;
  1.1586 +        case kString:
  1.1587 +            return SkType_String;
  1.1588 +        case kArray:
  1.1589 +            return SkType_Array;
  1.1590 +        case kObject:
  1.1591 +            return SkType_Displayable;
  1.1592 +//      case kStruct:
  1.1593 +//          return SkType_Structure;
  1.1594 +        default:
  1.1595 +            SkASSERT(0);
  1.1596 +            return SkType_Unknown;
  1.1597 +    }
  1.1598 +}
  1.1599 +
  1.1600 +SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) {
  1.1601 +    if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
  1.1602 +        return (SkOpType) kObject;
  1.1603 +    if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
  1.1604 +        return kInt;
  1.1605 +    switch (type) {
  1.1606 +        case SkType_ARGB:
  1.1607 +        case SkType_MSec:
  1.1608 +        case SkType_Int:
  1.1609 +            return kInt;
  1.1610 +        case SkType_Float:
  1.1611 +        case SkType_Point:
  1.1612 +        case SkType_3D_Point:
  1.1613 +            return kScalar;
  1.1614 +        case SkType_Base64:
  1.1615 +        case SkType_DynamicString:
  1.1616 +        case SkType_String:
  1.1617 +            return kString;
  1.1618 +        case SkType_Array:
  1.1619 +            return (SkOpType) kArray;
  1.1620 +        case SkType_Unknown:
  1.1621 +            return kNoType;
  1.1622 +        default:
  1.1623 +            SkASSERT(0);
  1.1624 +            return kNoType;
  1.1625 +    }
  1.1626 +}
  1.1627 +
  1.1628 +bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) {
  1.1629 +    switch (value.fType) {
  1.1630 +        case kInt:
  1.1631 +            string->reset();
  1.1632 +            string->appendS32(value.fOperand.fS32);
  1.1633 +            break;
  1.1634 +        case kScalar:
  1.1635 +            string->reset();
  1.1636 +            string->appendScalar(value.fOperand.fScalar);
  1.1637 +            break;
  1.1638 +        case kString:
  1.1639 +            string->set(*value.fOperand.fString);
  1.1640 +            break;
  1.1641 +        default:
  1.1642 +            SkASSERT(0);
  1.1643 +            return false;
  1.1644 +    }
  1.1645 +    return true; // no error
  1.1646 +}
  1.1647 +
  1.1648 +#ifdef SK_SUPPORT_UNITTEST
  1.1649 +
  1.1650 +#include "SkFloatingPoint.h"
  1.1651 +
  1.1652 +#define DEF_SCALAR_ANSWER   0
  1.1653 +#define DEF_STRING_ANSWER   NULL
  1.1654 +
  1.1655 +#define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
  1.1656 +    #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER }
  1.1657 +    #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER }
  1.1658 +#define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
  1.1659 +#define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
  1.1660 +
  1.1661 +static const SkScriptNAnswer scriptTests[]  = {
  1.1662 +    testInt(1>1/2),
  1.1663 +    testInt((6+7)*8),
  1.1664 +    testInt(0&&1?2:3),
  1.1665 +    testInt(3*(4+5)),
  1.1666 +    testScalar(1.0+2.0),
  1.1667 +    testScalar(1.0+5),
  1.1668 +    testScalar(3.0-1.0),
  1.1669 +    testScalar(6-1.0),
  1.1670 +    testScalar(- -5.5- -1.5),
  1.1671 +    testScalar(2.5*6.),
  1.1672 +    testScalar(0.5*4),
  1.1673 +    testScalar(4.5/.5),
  1.1674 +    testScalar(9.5/19),
  1.1675 +    testRemainder(9.5, 0.5),
  1.1676 +    testRemainder(9.,2),
  1.1677 +    testRemainder(9,2.5),
  1.1678 +    testRemainder(-9,2.5),
  1.1679 +    testTrue(-9==-9.0),
  1.1680 +    testTrue(-9.==-4.0-5),
  1.1681 +    testTrue(-9.*1==-4-5),
  1.1682 +    testFalse(-9!=-9.0),
  1.1683 +    testFalse(-9.!=-4.0-5),
  1.1684 +    testFalse(-9.*1!=-4-5),
  1.1685 +    testInt(0x123),
  1.1686 +    testInt(0XABC),
  1.1687 +    testInt(0xdeadBEEF),
  1.1688 +    {   "'123'+\"456\"", SkType_String, 0, 0, "123456" },
  1.1689 +    {   "123+\"456\"", SkType_String, 0, 0, "123456" },
  1.1690 +    {   "'123'+456", SkType_String, 0, 0, "123456" },
  1.1691 +    {   "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
  1.1692 +    {   "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
  1.1693 +    {   "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
  1.1694 +    {   "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
  1.1695 +    {   "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
  1.1696 +    {   "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
  1.1697 +    testInt(123),
  1.1698 +    testInt(-345),
  1.1699 +    testInt(+678),
  1.1700 +    testInt(1+2+3),
  1.1701 +    testInt(3*4+5),
  1.1702 +    testInt(6+7*8),
  1.1703 +    testInt(-1-2-8/4),
  1.1704 +    testInt(-9%4),
  1.1705 +    testInt(9%-4),
  1.1706 +    testInt(-9%-4),
  1.1707 +    testInt(123|978),
  1.1708 +    testInt(123&978),
  1.1709 +    testInt(123^978),
  1.1710 +    testInt(2<<4),
  1.1711 +    testInt(99>>3),
  1.1712 +    testInt(~55),
  1.1713 +    testInt(~~55),
  1.1714 +    testInt(!55),
  1.1715 +    testInt(!!55),
  1.1716 +    // both int
  1.1717 +    testInt(2<2),
  1.1718 +    testInt(2<11),
  1.1719 +    testInt(20<11),
  1.1720 +    testInt(2<=2),
  1.1721 +    testInt(2<=11),
  1.1722 +    testInt(20<=11),
  1.1723 +    testInt(2>2),
  1.1724 +    testInt(2>11),
  1.1725 +    testInt(20>11),
  1.1726 +    testInt(2>=2),
  1.1727 +    testInt(2>=11),
  1.1728 +    testInt(20>=11),
  1.1729 +    testInt(2==2),
  1.1730 +    testInt(2==11),
  1.1731 +    testInt(20==11),
  1.1732 +    testInt(2!=2),
  1.1733 +    testInt(2!=11),
  1.1734 +    testInt(20!=11),
  1.1735 +    // left int, right scalar
  1.1736 +    testInt(2<2.),
  1.1737 +    testInt(2<11.),
  1.1738 +    testInt(20<11.),
  1.1739 +    testInt(2<=2.),
  1.1740 +    testInt(2<=11.),
  1.1741 +    testInt(20<=11.),
  1.1742 +    testInt(2>2.),
  1.1743 +    testInt(2>11.),
  1.1744 +    testInt(20>11.),
  1.1745 +    testInt(2>=2.),
  1.1746 +    testInt(2>=11.),
  1.1747 +    testInt(20>=11.),
  1.1748 +    testInt(2==2.),
  1.1749 +    testInt(2==11.),
  1.1750 +    testInt(20==11.),
  1.1751 +    testInt(2!=2.),
  1.1752 +    testInt(2!=11.),
  1.1753 +    testInt(20!=11.),
  1.1754 +    // left scalar, right int
  1.1755 +        testInt(2.<2),
  1.1756 +    testInt(2.<11),
  1.1757 +    testInt(20.<11),
  1.1758 +    testInt(2.<=2),
  1.1759 +    testInt(2.<=11),
  1.1760 +    testInt(20.<=11),
  1.1761 +    testInt(2.>2),
  1.1762 +    testInt(2.>11),
  1.1763 +    testInt(20.>11),
  1.1764 +    testInt(2.>=2),
  1.1765 +    testInt(2.>=11),
  1.1766 +    testInt(20.>=11),
  1.1767 +    testInt(2.==2),
  1.1768 +    testInt(2.==11),
  1.1769 +    testInt(20.==11),
  1.1770 +    testInt(2.!=2),
  1.1771 +    testInt(2.!=11),
  1.1772 +    testInt(20.!=11),
  1.1773 +    // both scalar
  1.1774 +    testInt(2.<11.),
  1.1775 +    testInt(20.<11.),
  1.1776 +    testInt(2.<=2.),
  1.1777 +    testInt(2.<=11.),
  1.1778 +    testInt(20.<=11.),
  1.1779 +    testInt(2.>2.),
  1.1780 +    testInt(2.>11.),
  1.1781 +    testInt(20.>11.),
  1.1782 +    testInt(2.>=2.),
  1.1783 +    testInt(2.>=11.),
  1.1784 +    testInt(20.>=11.),
  1.1785 +    testInt(2.==2.),
  1.1786 +    testInt(2.==11.),
  1.1787 +    testInt(20.==11.),
  1.1788 +    testInt(2.!=2.),
  1.1789 +    testInt(2.!=11.),
  1.1790 +    testInt(20.!=11.),
  1.1791 +    // int, string (string is int)
  1.1792 +    testFalse(2<'2'),
  1.1793 +    testTrue(2<'11'),
  1.1794 +    testFalse(20<'11'),
  1.1795 +    testTrue(2<='2'),
  1.1796 +    testTrue(2<='11'),
  1.1797 +    testFalse(20<='11'),
  1.1798 +    testFalse(2>'2'),
  1.1799 +    testFalse(2>'11'),
  1.1800 +    testTrue(20>'11'),
  1.1801 +    testTrue(2>='2'),
  1.1802 +    testFalse(2>='11'),
  1.1803 +    testTrue(20>='11'),
  1.1804 +    testTrue(2=='2'),
  1.1805 +    testFalse(2=='11'),
  1.1806 +    testFalse(2!='2'),
  1.1807 +    testTrue(2!='11'),
  1.1808 +    // int, string (string is scalar)
  1.1809 +    testFalse(2<'2.'),
  1.1810 +    testTrue(2<'11.'),
  1.1811 +    testFalse(20<'11.'),
  1.1812 +    testTrue(2=='2.'),
  1.1813 +    testFalse(2=='11.'),
  1.1814 +    // scalar, string
  1.1815 +    testFalse(2.<'2.'),
  1.1816 +    testTrue(2.<'11.'),
  1.1817 +    testFalse(20.<'11.'),
  1.1818 +    testTrue(2.=='2.'),
  1.1819 +    testFalse(2.=='11.'),
  1.1820 +    // string, int
  1.1821 +    testFalse('2'<2),
  1.1822 +    testTrue('2'<11),
  1.1823 +    testFalse('20'<11),
  1.1824 +    testTrue('2'==2),
  1.1825 +    testFalse('2'==11),
  1.1826 +    // string, scalar
  1.1827 +    testFalse('2'<2.),
  1.1828 +    testTrue('2'<11.),
  1.1829 +    testFalse('20'<11.),
  1.1830 +    testTrue('2'==2.),
  1.1831 +    testFalse('2'==11.),
  1.1832 +    // string, string
  1.1833 +    testFalse('2'<'2'),
  1.1834 +    testFalse('2'<'11'),
  1.1835 +    testFalse('20'<'11'),
  1.1836 +    testTrue('2'=='2'),
  1.1837 +    testFalse('2'=='11'),
  1.1838 +    // logic
  1.1839 +    testInt(1?2:3),
  1.1840 +    testInt(0?2:3),
  1.1841 +    testInt((1&&2)||3),
  1.1842 +    testInt((1&&0)||3),
  1.1843 +    testInt((1&&0)||0),
  1.1844 +    testInt(1||(0&&3)),
  1.1845 +    testInt(0||(0&&3)),
  1.1846 +    testInt(0||(1&&3)),
  1.1847 +    testInt(1?(2?3:4):5),
  1.1848 +    testInt(0?(2?3:4):5),
  1.1849 +    testInt(1?(0?3:4):5),
  1.1850 +    testInt(0?(0?3:4):5),
  1.1851 +    testInt(1?2?3:4:5),
  1.1852 +    testInt(0?2?3:4:5),
  1.1853 +    testInt(1?0?3:4:5),
  1.1854 +    testInt(0?0?3:4:5),
  1.1855 +
  1.1856 +    testInt(1?2:(3?4:5)),
  1.1857 +    testInt(0?2:(3?4:5)),
  1.1858 +    testInt(1?0:(3?4:5)),
  1.1859 +    testInt(0?0:(3?4:5)),
  1.1860 +    testInt(1?2:3?4:5),
  1.1861 +    testInt(0?2:3?4:5),
  1.1862 +    testInt(1?0:3?4:5),
  1.1863 +    testInt(0?0:3?4:5)
  1.1864 +    , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER }
  1.1865 +};
  1.1866 +
  1.1867 +#define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
  1.1868 +
  1.1869 +void SkScriptEngine::UnitTest() {
  1.1870 +    for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
  1.1871 +        SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType));
  1.1872 +        SkScriptValue value;
  1.1873 +        const char* script = scriptTests[index].fScript;
  1.1874 +        SkASSERT(engine.evaluateScript(&script, &value) == true);
  1.1875 +        SkASSERT(value.fType == scriptTests[index].fType);
  1.1876 +        SkScalar error;
  1.1877 +        switch (value.fType) {
  1.1878 +            case SkType_Int:
  1.1879 +                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
  1.1880 +                break;
  1.1881 +            case SkType_Float:
  1.1882 +                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
  1.1883 +                SkASSERT(error < SK_Scalar1 / 10000);
  1.1884 +                break;
  1.1885 +            case SkType_String:
  1.1886 +                SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
  1.1887 +                break;
  1.1888 +            default:
  1.1889 +                SkASSERT(0);
  1.1890 +        }
  1.1891 +    }
  1.1892 +}
  1.1893 +#endif

mercurial