| Index: skia/animator/SkScriptTokenizer.cpp
|
| ===================================================================
|
| --- skia/animator/SkScriptTokenizer.cpp (revision 16859)
|
| +++ skia/animator/SkScriptTokenizer.cpp (working copy)
|
| @@ -1,1514 +0,0 @@
|
| -#include "SkScript2.h"
|
| -#include "SkFloatingPoint.h"
|
| -#include "SkMath.h"
|
| -#include "SkParse.h"
|
| -#include "SkScriptCallBack.h"
|
| -#include "SkScriptRuntime.h"
|
| -#include "SkString.h"
|
| -#include "SkOpArray.h"
|
| -
|
| -const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = {
|
| -{ SkOperand2::kNoType },
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
|
| - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString }, // kAdd
|
| -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitAnd
|
| -{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kBitNot
|
| -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitOr
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
|
| - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kDivide
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
|
| - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber,
|
| - kResultIsBoolean }, // kEqual
|
| -{ SkOperand2::kS32 }, // kFlipOps
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
|
| - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsNumber,
|
| - kResultIsBoolean }, // kGreaterEqual
|
| -{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalAnd (really, ToBool)
|
| -{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalNot
|
| -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kLogicalOr
|
| -{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMinus
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
|
| - SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias }, // kModulo
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
|
| - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMultiply
|
| -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftLeft
|
| -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftRight
|
| -{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
|
| - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kSubtract
|
| -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias } // kXor
|
| -};
|
| -
|
| -#define kBracketPrecedence 16
|
| -#define kIfElsePrecedence 15
|
| -
|
| -const signed char SkScriptEngine2::gPrecedence[] = {
|
| - 17, // kUnassigned,
|
| - 6, // kAdd,
|
| - 10, // kBitAnd,
|
| - 4, // kBitNot,
|
| - 12, // kBitOr,
|
| - 5, // kDivide,
|
| - 9, // kEqual,
|
| - -1, // kFlipOps,
|
| - 8, // kGreaterEqual,
|
| - 13, // kLogicalAnd,
|
| - 4, // kLogicalNot,
|
| - 14, // kLogicalOr,
|
| - 4, // kMinus,
|
| - 5, // kModulo,
|
| - 5, // kMultiply,
|
| - 7, // kShiftLeft,
|
| - 7, // kShiftRight, // signed
|
| - 6, // kSubtract,
|
| - 11, // kXor
|
| - kBracketPrecedence, // kArrayOp
|
| - kIfElsePrecedence, // kElse
|
| - kIfElsePrecedence, // kIf
|
| - kBracketPrecedence, // kParen
|
| -};
|
| -
|
| -const SkScriptEngine2::TypeOp SkScriptEngine2::gTokens[] = {
|
| - kNop, // unassigned
|
| - kAddInt, // kAdd,
|
| - kBitAndInt, // kBitAnd,
|
| - kBitNotInt, // kBitNot,
|
| - kBitOrInt, // kBitOr,
|
| - kDivideInt, // kDivide,
|
| - kEqualInt, // kEqual,
|
| - kFlipOpsOp, // kFlipOps,
|
| - kGreaterEqualInt, // kGreaterEqual,
|
| - kLogicalAndInt, // kLogicalAnd,
|
| - kLogicalNotInt, // kLogicalNot,
|
| - kLogicalOrInt, // kLogicalOr,
|
| - kMinusInt, // kMinus,
|
| - kModuloInt, // kModulo,
|
| - kMultiplyInt, // kMultiply,
|
| - kShiftLeftInt, // kShiftLeft,
|
| - kShiftRightInt, // kShiftRight, // signed
|
| - kSubtractInt, // kSubtract,
|
| - kXorInt // kXor
|
| -};
|
| -
|
| -static inline bool is_between(int c, int min, int max)
|
| -{
|
| - return (unsigned)(c - min) <= (unsigned)(max - min);
|
| -}
|
| -
|
| -static inline bool is_ws(int c)
|
| -{
|
| - return is_between(c, 1, 32);
|
| -}
|
| -
|
| -static int token_length(const char* start) {
|
| - char ch = start[0];
|
| - if (! is_between(ch, 'a' , 'z') && ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
|
| - return -1;
|
| - int length = 0;
|
| - do
|
| - ch = start[++length];
|
| - while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
|
| - ch == '_' || ch == '$');
|
| - return length;
|
| -}
|
| -
|
| -SkScriptEngine2::SkScriptEngine2(SkOperand2::OpType returnType) : fActiveStream(&fStream),
|
| -fTokenLength(0), fReturnType(returnType), fError(kNoError),
|
| -fAccumulatorType(SkOperand2::kNoType),
|
| -fBranchPopAllowed(true), fConstExpression(true), fOperandInUse(false)
|
| -{
|
| - Branch branch(kUnassigned, 0, 0);
|
| - fBranchStack.push(branch);
|
| - *fOpStack.push() = (Op) kParen;
|
| -}
|
| -
|
| -SkScriptEngine2::~SkScriptEngine2() {
|
| - for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
|
| - delete *stringPtr;
|
| - for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
|
| - delete *arrayPtr;
|
| -}
|
| -
|
| -void SkScriptEngine2::addToken(SkScriptEngine2::TypeOp op) {
|
| - int limit = fBranchStack.count() - 1;
|
| - for (int index = 0; index < limit; index++) {
|
| - Branch& branch = fBranchStack.index(index);
|
| - if (branch.fPrimed == Branch::kIsPrimed)
|
| - resolveBranch(branch);
|
| - }
|
| - if (fBranchPopAllowed) {
|
| - while (fBranchStack.top().fDone == Branch::kIsDone)
|
| - fBranchStack.pop();
|
| - }
|
| - unsigned char charOp = (unsigned char) op;
|
| - fActiveStream->write(&charOp, sizeof(charOp));
|
| -}
|
| -
|
| -void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg,
|
| - SkOperand2::OpType toType, SkScriptEngine2::TypeOp op) {
|
| - if (value->fIsConstant == SkScriptValue2::kConstant && convertTo(toType, value))
|
| - return;
|
| - addTokenValue(*value, reg);
|
| - addToken(op);
|
| - value->fIsWritten = SkScriptValue2::kWritten;
|
| - value->fType = toType;
|
| -}
|
| -
|
| -void SkScriptEngine2::addTokenInt(int integer) {
|
| - fActiveStream->write(&integer, sizeof(integer));
|
| -}
|
| -
|
| -void SkScriptEngine2::addTokenScalar(SkScalar scalar) {
|
| - fActiveStream->write(&scalar, sizeof(scalar));
|
| -}
|
| -
|
| -void SkScriptEngine2::addTokenString(const SkString& string) {
|
| - int size = string.size();
|
| - addTokenInt(size);
|
| - fActiveStream->write(string.c_str(), size);
|
| -}
|
| -
|
| -void SkScriptEngine2::addTokenValue(const SkScriptValue2& value, AddTokenRegister reg) {
|
| - if (value.isConstant() == false) {
|
| - if (reg == kAccumulator) {
|
| - if (fAccumulatorType == SkOperand2::kNoType)
|
| - addToken(kAccumulatorPop);
|
| - } else {
|
| - ; // !!! incomplete?
|
| - }
|
| - return;
|
| - }
|
| - if (reg == kAccumulator && fAccumulatorType != SkOperand2::kNoType)
|
| - addToken(kAccumulatorPush);
|
| - switch (value.fType) {
|
| - case SkOperand2::kS32:
|
| - addToken(reg == kAccumulator ? kIntegerAccumulator : kIntegerOperand);
|
| - addTokenInt(value.fOperand.fS32);
|
| - if (reg == kAccumulator)
|
| - fAccumulatorType = SkOperand2::kS32;
|
| - else
|
| - fOperandInUse = true;
|
| - break;
|
| - case SkOperand2::kScalar:
|
| - addToken(reg == kAccumulator ? kScalarAccumulator : kScalarOperand);
|
| - addTokenScalar(value.fOperand.fScalar);
|
| - if (reg == kAccumulator)
|
| - fAccumulatorType = SkOperand2::kScalar;
|
| - else
|
| - fOperandInUse = true;
|
| - break;
|
| - case SkOperand2::kString:
|
| - addToken(reg == kAccumulator ? kStringAccumulator : kStringOperand);
|
| - addTokenString(*value.fOperand.fString);
|
| - if (reg == kAccumulator)
|
| - fAccumulatorType = SkOperand2::kString;
|
| - else
|
| - fOperandInUse = true;
|
| - break;
|
| - default:
|
| - SkASSERT(0); //!!! not implemented yet
|
| - }
|
| -}
|
| -
|
| -int SkScriptEngine2::arithmeticOp(char ch, char nextChar, bool lastPush) {
|
| - Op op = kUnassigned;
|
| - bool reverseOperands = false;
|
| - bool negateResult = false;
|
| - int advance = 1;
|
| - switch (ch) {
|
| - case '+':
|
| - // !!! ignoring unary plus as implemented here has the side effect of
|
| - // suppressing errors like +"hi"
|
| - if (lastPush == false) // unary plus, don't push an operator
|
| - goto returnAdv;
|
| - op = kAdd;
|
| - break;
|
| - case '-':
|
| - op = lastPush ? kSubtract : kMinus;
|
| - break;
|
| - case '*':
|
| - op = kMultiply;
|
| - break;
|
| - case '/':
|
| - op = kDivide;
|
| - break;
|
| - case '>':
|
| - if (nextChar == '>') {
|
| - op = kShiftRight;
|
| - goto twoChar;
|
| - }
|
| - op = kGreaterEqual;
|
| - if (nextChar == '=')
|
| - goto twoChar;
|
| - reverseOperands = negateResult = true;
|
| - break;
|
| - case '<':
|
| - if (nextChar == '<') {
|
| - op = kShiftLeft;
|
| - goto twoChar;
|
| - }
|
| - op = kGreaterEqual;
|
| - reverseOperands = nextChar == '=';
|
| - negateResult = ! reverseOperands;
|
| - advance += reverseOperands;
|
| - break;
|
| - case '=':
|
| - if (nextChar == '=') {
|
| - op = kEqual;
|
| - goto twoChar;
|
| - }
|
| - break;
|
| - case '!':
|
| - if (nextChar == '=') {
|
| - op = kEqual;
|
| - negateResult = true;
|
| -twoChar:
|
| - advance++;
|
| - break;
|
| - }
|
| - op = kLogicalNot;
|
| - break;
|
| - case '?':
|
| - op =(Op) kIf;
|
| - break;
|
| - case ':':
|
| - op = (Op) kElse;
|
| - break;
|
| - case '^':
|
| - op = kXor;
|
| - break;
|
| - case '(':
|
| - *fOpStack.push() = (Op) kParen;
|
| - goto returnAdv;
|
| - case '&':
|
| - SkASSERT(nextChar != '&');
|
| - op = kBitAnd;
|
| - break;
|
| - case '|':
|
| - SkASSERT(nextChar != '|');
|
| - op = kBitOr;
|
| - break;
|
| - case '%':
|
| - op = kModulo;
|
| - break;
|
| - case '~':
|
| - op = kBitNot;
|
| - break;
|
| - }
|
| - if (op == kUnassigned)
|
| - return 0;
|
| - signed char precedence = gPrecedence[op];
|
| - do {
|
| - int idx = 0;
|
| - Op compare;
|
| - do {
|
| - compare = fOpStack.index(idx);
|
| - if ((compare & kArtificialOp) == 0)
|
| - break;
|
| - idx++;
|
| - } while (true);
|
| - signed char topPrecedence = gPrecedence[compare];
|
| - SkASSERT(topPrecedence != -1);
|
| - if (topPrecedence > precedence || topPrecedence == precedence &&
|
| - gOpAttributes[op].fLeftType == SkOperand2::kNoType) {
|
| - break;
|
| - }
|
| - processOp();
|
| - } while (true);
|
| - if (negateResult)
|
| - *fOpStack.push() = (Op) (kLogicalNot | kArtificialOp);
|
| - fOpStack.push(op);
|
| - if (reverseOperands)
|
| - *fOpStack.push() = (Op) (kFlipOps | kArtificialOp);
|
| -returnAdv:
|
| - return advance;
|
| -}
|
| -
|
| -bool SkScriptEngine2::convertParams(SkTDArray<SkScriptValue2>* params,
|
| - const SkOperand2::OpType* paramTypes, int paramCount) {
|
| - int count = params->count();
|
| - if (count > paramCount) {
|
| - SkASSERT(0);
|
| - return false; // too many parameters passed
|
| - }
|
| - for (int index = 0; index < count; index++)
|
| - convertTo(paramTypes[index], &(*params)[index]);
|
| - return true;
|
| -}
|
| -
|
| -bool SkScriptEngine2::convertTo(SkOperand2::OpType toType, SkScriptValue2* value ) {
|
| - SkOperand2::OpType type = value->fType;
|
| - if (type == toType)
|
| - return true;
|
| - if (type == SkOperand2::kObject) {
|
| - if (handleUnbox(value) == false)
|
| - return false;
|
| - return convertTo(toType, value);
|
| - }
|
| - return ConvertTo(this, toType, value);
|
| -}
|
| -
|
| -bool SkScriptEngine2::evaluateDot(const char*& script) {
|
| - size_t fieldLength = token_length(++script); // skip dot
|
| - SkASSERT(fieldLength > 0); // !!! add error handling
|
| - const char* field = script;
|
| - script += fieldLength;
|
| - bool success = handleProperty();
|
| - if (success == false) {
|
| - fError = kCouldNotFindReferencedID;
|
| - goto error;
|
| - }
|
| - return evaluateDotParam(script, field, fieldLength);
|
| -error:
|
| - return false;
|
| -}
|
| -
|
| -bool SkScriptEngine2::evaluateDotParam(const char*& script, const char* field, size_t fieldLength) {
|
| - SkScriptValue2& top = fValueStack.top();
|
| - if (top.fType != SkOperand2::kObject)
|
| - return false;
|
| - void* object = top.fOperand.fObject;
|
| - fValueStack.pop();
|
| - char ch; // see if it is a simple member or a function
|
| - while (is_ws(ch = script[0]))
|
| - script++;
|
| - bool success = true;
|
| - if (ch != '(')
|
| - success = handleMember(field, fieldLength, object);
|
| - else {
|
| - SkTDArray<SkScriptValue2> params;
|
| - *fBraceStack.push() = kFunctionBrace;
|
| - success = functionParams(&script, ¶ms);
|
| - if (success)
|
| - success = handleMemberFunction(field, fieldLength, object, ¶ms);
|
| - }
|
| - return success;
|
| -}
|
| -
|
| -bool SkScriptEngine2::evaluateScript(const char** scriptPtr, SkScriptValue2* value) {
|
| - // fArrayOffset = 0; // no support for structures for now
|
| - bool success;
|
| - const char* inner;
|
| - if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
|
| - *scriptPtr += sizeof("#script:") - 1;
|
| - if (fReturnType == SkOperand2::kNoType || fReturnType == SkOperand2::kString) {
|
| - success = innerScript(scriptPtr, value);
|
| - SkASSERT(success);
|
| - inner = value->fOperand.fString->c_str();
|
| - scriptPtr = &inner;
|
| - }
|
| - }
|
| - success = innerScript(scriptPtr, value);
|
| - const char* script = *scriptPtr;
|
| - char ch;
|
| - while (is_ws(ch = script[0]))
|
| - script++;
|
| - if (ch != '\0') {
|
| - // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
|
| - return false;
|
| - }
|
| - return success;
|
| -}
|
| -
|
| -void SkScriptEngine2::forget(SkOpArray* array) {
|
| - if (array->getType() == SkOperand2::kString) {
|
| - for (int index = 0; index < array->count(); index++) {
|
| - SkString* string = (*array)[index].fString;
|
| - int found = fTrackString.find(string);
|
| - if (found >= 0)
|
| - fTrackString.remove(found);
|
| - }
|
| - return;
|
| - }
|
| - if (array->getType() == SkOperand2::kArray) {
|
| - for (int index = 0; index < array->count(); index++) {
|
| - SkOpArray* child = (*array)[index].fArray;
|
| - forget(child); // forgets children of child
|
| - int found = fTrackArray.find(child);
|
| - if (found >= 0)
|
| - fTrackArray.remove(found);
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool SkScriptEngine2::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params) {
|
| - (*scriptPtr)++; // skip open paren
|
| - *fOpStack.push() = (Op) kParen;
|
| - *fBraceStack.push() = kFunctionBrace;
|
| - do {
|
| - SkScriptValue2 value;
|
| - bool success = innerScript(scriptPtr, &value);
|
| - SkASSERT(success);
|
| - if (success == false)
|
| - return false;
|
| - *params->append() = value;
|
| - } while ((*scriptPtr)[-1] == ',');
|
| - fBraceStack.pop();
|
| - fOpStack.pop(); // pop paren
|
| - (*scriptPtr)++; // advance beyond close paren
|
| - return true;
|
| -}
|
| -
|
| -size_t SkScriptEngine2::getTokenOffset() {
|
| - return fActiveStream->getOffset();
|
| -}
|
| -
|
| -SkOperand2::OpType SkScriptEngine2::getUnboxType(SkOperand2 scriptValue) {
|
| - for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
|
| - if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
|
| - continue;
|
| - return (*callBack)->getReturnType(0, &scriptValue);
|
| - }
|
| - return SkOperand2::kObject;
|
| -}
|
| -
|
| -bool SkScriptEngine2::innerScript(const char** scriptPtr, SkScriptValue2* value) {
|
| - const char* script = *scriptPtr;
|
| - char ch;
|
| - bool lastPush = false;
|
| - bool success = true;
|
| - int opBalance = fOpStack.count();
|
| - int baseBrace = fBraceStack.count();
|
| - int branchBalance = fBranchStack.count();
|
| - while ((ch = script[0]) != '\0') {
|
| - if (is_ws(ch)) {
|
| - script++;
|
| - continue;
|
| - }
|
| - SkScriptValue2 operand;
|
| - const char* dotCheck;
|
| - if (fBraceStack.count() > baseBrace) {
|
| - if (fBraceStack.top() == kArrayBrace) {
|
| - SkScriptValue2 tokenValue;
|
| - success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace
|
| - SkASSERT(success);
|
| - {
|
| - SkOperand2::OpType type = fReturnType;
|
| - if (fReturnType == SkOperand2::kNoType) {
|
| - // !!! short sighted; in the future, allow each returned array component to carry
|
| - // its own type, and let caller do any needed conversions
|
| - if (value->fOperand.fArray->count() == 0)
|
| - value->fOperand.fArray->setType(type = tokenValue.fType);
|
| - else
|
| - type = value->fOperand.fArray->getType();
|
| - }
|
| - if (tokenValue.fType != type)
|
| - convertTo(type, &tokenValue);
|
| - *value->fOperand.fArray->append() = tokenValue.fOperand;
|
| - }
|
| - lastPush = false;
|
| - continue;
|
| - } else
|
| - SkASSERT(token_length(script) > 0);
|
| - }
|
| - if (lastPush != false && fTokenLength > 0) {
|
| - if (ch == '(') {
|
| - *fBraceStack.push() = kFunctionBrace;
|
| - SkString functionName(fToken, fTokenLength);
|
| -
|
| - if (handleFunction(&script) == false)
|
| - return false;
|
| - lastPush = true;
|
| - continue;
|
| - } else if (ch == '[') {
|
| - if (handleProperty() == false) {
|
| - SkASSERT(0);
|
| - return false;
|
| - }
|
| - if (handleArrayIndexer(&script) == false)
|
| - return false;
|
| - lastPush = true;
|
| - continue;
|
| - } else if (ch != '.') {
|
| - if (handleProperty() == false) {
|
| - SkASSERT(0);
|
| - return false;
|
| - }
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| - }
|
| - if (ch == '0' && (script[1] & ~0x20) == 'X') {
|
| - SkASSERT(lastPush == false);
|
| - script += 2;
|
| - script = SkParse::FindHex(script, (uint32_t*) &operand.fOperand.fS32);
|
| - SkASSERT(script);
|
| - goto intCommon;
|
| - }
|
| - if (lastPush == false && ch == '.')
|
| - goto scalarCommon;
|
| - if (ch >= '0' && ch <= '9') {
|
| - SkASSERT(lastPush == false);
|
| - dotCheck = SkParse::FindS32(script, &operand.fOperand.fS32);
|
| - if (dotCheck[0] != '.') {
|
| - script = dotCheck;
|
| -intCommon:
|
| - operand.fType = SkOperand2::kS32;
|
| - } else {
|
| -scalarCommon:
|
| - script = SkParse::FindScalar(script, &operand.fOperand.fScalar);
|
| - operand.fType = SkOperand2::kScalar;
|
| - }
|
| - operand.fIsConstant = SkScriptValue2::kConstant;
|
| - fValueStack.push(operand);
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| - int length = token_length(script);
|
| - if (length > 0) {
|
| - SkASSERT(lastPush == false);
|
| - fToken = script;
|
| - fTokenLength = length;
|
| - script += length;
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| - char startQuote = ch;
|
| - if (startQuote == '\'' || startQuote == '\"') {
|
| - SkASSERT(lastPush == false);
|
| - operand.fOperand.fString = new SkString();
|
| - ++script;
|
| - const char* stringStart = script;
|
| - do { // measure string
|
| - if (script[0] == '\\')
|
| - ++script;
|
| - ++script;
|
| - SkASSERT(script[0]); // !!! throw an error
|
| - } while (script[0] != startQuote);
|
| - operand.fOperand.fString->set(stringStart, script - stringStart);
|
| - script = stringStart;
|
| - char* stringWrite = operand.fOperand.fString->writable_str();
|
| - do { // copy string
|
| - if (script[0] == '\\')
|
| - ++script;
|
| - *stringWrite++ = script[0];
|
| - ++script;
|
| - SkASSERT(script[0]); // !!! throw an error
|
| - } while (script[0] != startQuote);
|
| - ++script;
|
| - track(operand.fOperand.fString);
|
| - operand.fType = SkOperand2::kString;
|
| - operand.fIsConstant = SkScriptValue2::kConstant;
|
| - fValueStack.push(operand);
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| - if (ch == '.') {
|
| - if (fTokenLength == 0) {
|
| - SkScriptValue2 scriptValue;
|
| - SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
|
| - int tokenLength = token_length(++script);
|
| - const char* token = script;
|
| - script += tokenLength;
|
| - SkASSERT(fValueStack.count() > 0); // !!! add error handling
|
| - SkScriptValue2 top;
|
| - fValueStack.pop(&top);
|
| -
|
| - addTokenInt(top.fType);
|
| - addToken(kBoxToken);
|
| - top.fType = SkOperand2::kObject;
|
| - top.fIsConstant = SkScriptValue2::kVariable;
|
| - fConstExpression = false;
|
| - fValueStack.push(top);
|
| - success = evaluateDotParam(script, token, tokenLength);
|
| - SkASSERT(success);
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| - // get next token, and evaluate immediately
|
| - success = evaluateDot(script);
|
| - if (success == false) {
|
| - // SkASSERT(0);
|
| - return false;
|
| - }
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| - if (ch == '[') {
|
| - if (lastPush == false) {
|
| - script++;
|
| - *fBraceStack.push() = kArrayBrace;
|
| - operand.fOperand.fArray = value->fOperand.fArray = new SkOpArray(fReturnType);
|
| - track(value->fOperand.fArray);
|
| -
|
| - operand.fType = SkOperand2::kArray;
|
| - operand.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(operand);
|
| - continue;
|
| - }
|
| - if (handleArrayIndexer(&script) == false)
|
| - return false;
|
| - lastPush = true;
|
| - continue;
|
| - }
|
| -#if 0 // structs not supported for now
|
| - if (ch == '{') {
|
| - if (lastPush == false) {
|
| - script++;
|
| - *fBraceStack.push() = kStructBrace;
|
| - operand.fS32 = 0;
|
| - *fTypeStack.push() = (SkOpType) kStruct;
|
| - fOperandStack.push(operand);
|
| - continue;
|
| - }
|
| - SkASSERT(0); // braces in other contexts aren't supported yet
|
| - }
|
| -#endif
|
| - if (ch == ')' && fBraceStack.count() > 0) {
|
| - BraceStyle braceStyle = fBraceStack.top();
|
| - if (braceStyle == kFunctionBrace) {
|
| - fBraceStack.pop();
|
| - break;
|
| - }
|
| - }
|
| - if (ch == ',' || ch == ']') {
|
| - if (ch != ',') {
|
| - BraceStyle match;
|
| - fBraceStack.pop(&match);
|
| - SkASSERT(match == kArrayBrace);
|
| - }
|
| - script++;
|
| - // !!! see if brace or bracket is correct closer
|
| - break;
|
| - }
|
| - char nextChar = script[1];
|
| - int advance = logicalOp(ch, nextChar);
|
| - if (advance == 0)
|
| - advance = arithmeticOp(ch, nextChar, lastPush);
|
| - if (advance == 0) // unknown token
|
| - return false;
|
| - if (advance > 0)
|
| - script += advance;
|
| - lastPush = ch == ']' || ch == ')';
|
| - }
|
| - if (fTokenLength > 0) {
|
| - success = handleProperty();
|
| - SkASSERT(success);
|
| - }
|
| - int branchIndex = 0;
|
| - branchBalance = fBranchStack.count() - branchBalance;
|
| - fBranchPopAllowed = false;
|
| - while (branchIndex < branchBalance) {
|
| - Branch& branch = fBranchStack.index(branchIndex++);
|
| - if (branch.fPrimed == Branch::kIsPrimed)
|
| - break;
|
| - Op branchOp = branch.fOperator;
|
| - SkOperand2::OpType lastType = fValueStack.top().fType;
|
| - addTokenValue(fValueStack.top(), kAccumulator);
|
| - fValueStack.pop();
|
| - if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
|
| - if (branch.fOperator == kLogicalAnd)
|
| - branch.prime();
|
| - addToken(kToBool);
|
| - } else {
|
| - resolveBranch(branch);
|
| - SkScriptValue2 operand;
|
| - operand.fType = lastType;
|
| - // !!! note that many branching expressions could be constant
|
| - // today, we always evaluate branches as returning variables
|
| - operand.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(operand);
|
| - }
|
| - if (branch.fDone == Branch::kIsNotDone)
|
| - branch.prime();
|
| - }
|
| - fBranchPopAllowed = true;
|
| - while (fBranchStack.top().fDone == Branch::kIsDone)
|
| - fBranchStack.pop();
|
| - while (fOpStack.count() > opBalance) { // leave open paren
|
| - if (processOp() == false)
|
| - return false;
|
| - }
|
| - SkOperand2::OpType topType = fValueStack.count() > 0 ? fValueStack.top().fType : SkOperand2::kNoType;
|
| - if (topType != fReturnType &&
|
| - topType == SkOperand2::kString && fReturnType != SkOperand2::kNoType) { // if result is a string, give handle property a chance to convert it to the property value
|
| - SkString* string = fValueStack.top().fOperand.fString;
|
| - fToken = string->c_str();
|
| - fTokenLength = string->size();
|
| - fValueStack.pop();
|
| - success = handleProperty();
|
| - if (success == false) { // if it couldn't convert, return string (error?)
|
| - SkScriptValue2 operand;
|
| - operand.fType = SkOperand2::kString;
|
| - operand.fOperand.fString = string;
|
| - operand.fIsConstant = SkScriptValue2::kVariable; // !!! ?
|
| - fValueStack.push(operand);
|
| - }
|
| - }
|
| - if (fStream.getOffset() > 0) {
|
| - addToken(kEnd);
|
| -#ifdef SK_DEBUG
|
| - decompile((const unsigned char*)fStream.getStream(), fStream.getOffset());
|
| -#endif
|
| - SkScriptRuntime runtime(fCallBackArray);
|
| - runtime.executeTokens((unsigned char*) fStream.getStream());
|
| - SkScriptValue2 value1;
|
| - runtime.getResult(&value1.fOperand);
|
| - value1.fType = fReturnType;
|
| - fValueStack.push(value1);
|
| - }
|
| - if (value) {
|
| - if (fValueStack.count() == 0)
|
| - return false;
|
| - fValueStack.pop(value);
|
| - if (value->fType != fReturnType && value->fType == SkOperand2::kObject &&
|
| - fReturnType != SkOperand2::kNoType)
|
| - convertTo(fReturnType, value);
|
| - }
|
| - // if (fBranchStack.top().fOpStackDepth > fOpStack.count())
|
| - // resolveBranch();
|
| - *scriptPtr = script;
|
| - return true; // no error
|
| -}
|
| -
|
| -bool SkScriptEngine2::handleArrayIndexer(const char** scriptPtr) {
|
| - SkScriptValue2 scriptValue;
|
| - (*scriptPtr)++;
|
| - *fOpStack.push() = (Op) kParen;
|
| - *fBraceStack.push() = kArrayBrace;
|
| - SkOperand2::OpType saveType = fReturnType;
|
| - fReturnType = SkOperand2::kS32;
|
| - bool success = innerScript(scriptPtr, &scriptValue);
|
| - fReturnType = saveType;
|
| - SkASSERT(success);
|
| - success = convertTo(SkOperand2::kS32, &scriptValue);
|
| - SkASSERT(success);
|
| - int index = scriptValue.fOperand.fS32;
|
| - fValueStack.pop(&scriptValue);
|
| - if (scriptValue.fType == SkOperand2::kObject) {
|
| - success = handleUnbox(&scriptValue);
|
| - SkASSERT(success);
|
| - SkASSERT(scriptValue.fType == SkOperand2::kArray);
|
| - }
|
| - scriptValue.fType = scriptValue.fOperand.fArray->getType();
|
| - // SkASSERT(index >= 0);
|
| - if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
|
| - fError = kArrayIndexOutOfBounds;
|
| - return false;
|
| - }
|
| - scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
|
| - scriptValue.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(scriptValue);
|
| - fOpStack.pop(); // pop paren
|
| - return success;
|
| -}
|
| -
|
| -bool SkScriptEngine2::handleFunction(const char** scriptPtr) {
|
| - const char* functionName = fToken;
|
| - size_t functionNameLen = fTokenLength;
|
| - fTokenLength = 0;
|
| - SkTDArray<SkScriptValue2> params;
|
| - bool success = functionParams(scriptPtr, ¶ms);
|
| - if (success == false)
|
| - goto done;
|
| - {
|
| - for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
|
| - if ((*callBack)->getType() != SkScriptCallBack::kFunction)
|
| - continue;
|
| - SkScriptValue2 callbackResult;
|
| - success = (*callBack)->getReference(functionName, functionNameLen, &callbackResult);
|
| - if (success) {
|
| - callbackResult.fType = (*callBack)->getReturnType(callbackResult.fOperand.fReference, NULL);
|
| - callbackResult.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(callbackResult);
|
| - goto done;
|
| - }
|
| - }
|
| - }
|
| - return false;
|
| -done:
|
| - fOpStack.pop();
|
| - return success;
|
| -}
|
| -
|
| -bool SkScriptEngine2::handleMember(const char* field, size_t len, void* object) {
|
| - bool success = true;
|
| - for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
|
| - if ((*callBack)->getType() != SkScriptCallBack::kMember)
|
| - continue;
|
| - SkScriptValue2 callbackResult;
|
| - success = (*callBack)->getReference(field, len, &callbackResult);
|
| - if (success) {
|
| - if (callbackResult.fType == SkOperand2::kString)
|
| - track(callbackResult.fOperand.fString);
|
| - callbackResult.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(callbackResult);
|
| - goto done;
|
| - }
|
| - }
|
| - return false;
|
| -done:
|
| - return success;
|
| -}
|
| -
|
| -bool SkScriptEngine2::handleMemberFunction(const char* field, size_t len, void* object,
|
| - SkTDArray<SkScriptValue2>* params) {
|
| - bool success = true;
|
| - for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
|
| - if ((*callBack)->getType() != SkScriptCallBack::kMemberFunction)
|
| - continue;
|
| - SkScriptValue2 callbackResult;
|
| - success = (*callBack)->getReference(field, len, &callbackResult);
|
| - if (success) {
|
| - if (callbackResult.fType == SkOperand2::kString)
|
| - track(callbackResult.fOperand.fString);
|
| - callbackResult.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(callbackResult);
|
| - goto done;
|
| - }
|
| - }
|
| - return false;
|
| -done:
|
| - return success;
|
| -}
|
| -
|
| -bool SkScriptEngine2::handleProperty() {
|
| - bool success = true;
|
| - for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
|
| - if ((*callBack)->getType() != SkScriptCallBack::kProperty)
|
| - continue;
|
| - SkScriptValue2 callbackResult;
|
| - success = (*callBack)->getReference(fToken, fTokenLength, &callbackResult);
|
| - if (success) {
|
| - if (callbackResult.fType == SkOperand2::kString && callbackResult.fOperand.fString == NULL) {
|
| - callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
|
| - track(callbackResult.fOperand.fString);
|
| - }
|
| - callbackResult.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(callbackResult);
|
| - goto done;
|
| - }
|
| - }
|
| -done:
|
| - fTokenLength = 0;
|
| - return success;
|
| -}
|
| -
|
| -bool SkScriptEngine2::handleUnbox(SkScriptValue2* scriptValue) {
|
| - bool success = true;
|
| - for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
|
| - if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
|
| - continue;
|
| - SkScriptCallBackConvert* callBackConvert = (SkScriptCallBackConvert*) *callBack;
|
| - success = callBackConvert->convert(scriptValue->fType, &scriptValue->fOperand);
|
| - if (success) {
|
| - if (scriptValue->fType == SkOperand2::kString)
|
| - track(scriptValue->fOperand.fString);
|
| - goto done;
|
| - }
|
| - }
|
| - return false;
|
| -done:
|
| - return success;
|
| -}
|
| -
|
| -// note that entire expression is treated as if it were enclosed in parens
|
| -// an open paren is always the first thing in the op stack
|
| -
|
| -int SkScriptEngine2::logicalOp(char ch, char nextChar) {
|
| - int advance = 1;
|
| - Op op;
|
| - signed char precedence;
|
| - switch (ch) {
|
| - case ')':
|
| - op = (Op) kParen;
|
| - break;
|
| - case ']':
|
| - op = (Op) kArrayOp;
|
| - break;
|
| - case '?':
|
| - op = (Op) kIf;
|
| - break;
|
| - case ':':
|
| - op = (Op) kElse;
|
| - break;
|
| - case '&':
|
| - if (nextChar != '&')
|
| - goto noMatch;
|
| - op = kLogicalAnd;
|
| - advance = 2;
|
| - break;
|
| - case '|':
|
| - if (nextChar != '|')
|
| - goto noMatch;
|
| - op = kLogicalOr;
|
| - advance = 2;
|
| - break;
|
| - default:
|
| - noMatch:
|
| - return 0;
|
| - }
|
| - precedence = gPrecedence[op];
|
| - int branchIndex = 0;
|
| - fBranchPopAllowed = false;
|
| - do {
|
| - while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence)
|
| - processOp();
|
| - Branch& branch = fBranchStack.index(branchIndex++);
|
| - Op branchOp = branch.fOperator;
|
| - if (gPrecedence[branchOp] >= precedence)
|
| - break;
|
| - addTokenValue(fValueStack.top(), kAccumulator);
|
| - fValueStack.pop();
|
| - if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
|
| - if (branch.fOperator == kLogicalAnd)
|
| - branch.prime();
|
| - addToken(kToBool);
|
| - } else
|
| - resolveBranch(branch);
|
| - if (branch.fDone == Branch::kIsNotDone)
|
| - branch.prime();
|
| - } while (true);
|
| - fBranchPopAllowed = true;
|
| - while (fBranchStack.top().fDone == Branch::kIsDone)
|
| - fBranchStack.pop();
|
| - processLogicalOp(op);
|
| - return advance;
|
| -}
|
| -
|
| -void SkScriptEngine2::processLogicalOp(Op op) {
|
| - switch (op) {
|
| - case kParen:
|
| - case kArrayOp:
|
| - SkASSERT(fOpStack.count() > 1 && fOpStack.top() == op); // !!! add error handling
|
| - if (op == kParen)
|
| - fOpStack.pop();
|
| - else {
|
| - SkScriptValue2 value;
|
| - fValueStack.pop(&value);
|
| - SkASSERT(value.fType == SkOperand2::kS32 || value.fType == SkOperand2::kScalar); // !!! add error handling (although, could permit strings eventually)
|
| - int index = value.fType == SkOperand2::kScalar ? SkScalarFloor(value.fOperand.fScalar) :
|
| - value.fOperand.fS32;
|
| - SkScriptValue2 arrayValue;
|
| - fValueStack.pop(&arrayValue);
|
| - SkASSERT(arrayValue.fType == SkOperand2::kArray); // !!! add error handling
|
| - SkOpArray* array = arrayValue.fOperand.fArray;
|
| - SkOperand2 operand;
|
| - bool success = array->getIndex(index, &operand);
|
| - SkASSERT(success); // !!! add error handling
|
| - SkScriptValue2 resultValue;
|
| - resultValue.fType = array->getType();
|
| - resultValue.fOperand = operand;
|
| - resultValue.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(resultValue);
|
| - }
|
| - break;
|
| - case kIf: {
|
| - if (fAccumulatorType == SkOperand2::kNoType) {
|
| - addTokenValue(fValueStack.top(), kAccumulator);
|
| - fValueStack.pop();
|
| - }
|
| - SkASSERT(fAccumulatorType != SkOperand2::kString); // !!! add error handling
|
| - addToken(kIfOp);
|
| - Branch branch(op, fOpStack.count(), getTokenOffset());
|
| - *fBranchStack.push() = branch;
|
| - addTokenInt(0); // placeholder for future branch
|
| - fAccumulatorType = SkOperand2::kNoType;
|
| - } break;
|
| - case kElse: {
|
| - addTokenValue(fValueStack.top(), kAccumulator);
|
| - fValueStack.pop();
|
| - addToken(kElseOp);
|
| - size_t newOffset = getTokenOffset();
|
| - addTokenInt(0); // placeholder for future branch
|
| - Branch& branch = fBranchStack.top();
|
| - resolveBranch(branch);
|
| - branch.fOperator = op;
|
| - branch.fDone = Branch::kIsNotDone;
|
| - SkASSERT(branch.fOpStackDepth == fOpStack.count());
|
| - branch.fOffset = newOffset;
|
| - fAccumulatorType = SkOperand2::kNoType;
|
| - } break;
|
| - case kLogicalAnd:
|
| - case kLogicalOr: {
|
| - Branch& oldTop = fBranchStack.top();
|
| - Branch::Primed wasPrime = oldTop.fPrimed;
|
| - Branch::Done wasDone = oldTop.fDone;
|
| - oldTop.fPrimed = Branch::kIsNotPrimed;
|
| - oldTop.fDone = Branch::kIsNotDone;
|
| - if (fAccumulatorType == SkOperand2::kNoType) {
|
| - SkASSERT(fValueStack.top().fType == SkOperand2::kS32); // !!! add error handling, and conversion to int?
|
| - addTokenValue(fValueStack.top(), kAccumulator);
|
| - fValueStack.pop();
|
| - } else
|
| - SkASSERT(fAccumulatorType == SkOperand2::kS32);
|
| - // if 'and', write beq goto opcode after end of predicate (after to bool)
|
| - // if 'or', write bne goto to bool
|
| - addToken(op == kLogicalAnd ? kLogicalAndInt : kLogicalOrInt);
|
| - Branch branch(op, fOpStack.count(), getTokenOffset());
|
| - addTokenInt(0); // placeholder for future branch
|
| - oldTop.fPrimed = wasPrime;
|
| - oldTop.fDone = wasDone;
|
| - *fBranchStack.push() = branch;
|
| - fAccumulatorType = SkOperand2::kNoType;
|
| - } break;
|
| - default:
|
| - SkASSERT(0);
|
| - }
|
| -}
|
| -
|
| -bool SkScriptEngine2::processOp() {
|
| - Op op;
|
| - fOpStack.pop(&op);
|
| - op = (Op) (op & ~kArtificialOp);
|
| - const OperatorAttributes* attributes = &gOpAttributes[op];
|
| - SkScriptValue2 value1 = { 0 };
|
| - SkScriptValue2 value2;
|
| - fValueStack.pop(&value2);
|
| - value2.fIsWritten = SkScriptValue2::kUnwritten;
|
| - // SkScriptEngine2::SkTypeOp convert1[3];
|
| - // SkScriptEngine2::SkTypeOp convert2[3];
|
| - // SkScriptEngine2::SkTypeOp* convert2Ptr = convert2;
|
| - bool constantOperands = value2.fIsConstant == SkScriptValue2::kConstant;
|
| - if (attributes->fLeftType != SkOperand2::kNoType) {
|
| - fValueStack.pop(&value1);
|
| - constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant;
|
| - value1.fIsWritten = SkScriptValue2::kUnwritten;
|
| - if (op == kFlipOps) {
|
| - SkTSwap(value1, value2);
|
| - fOpStack.pop(&op);
|
| - op = (Op) (op & ~kArtificialOp);
|
| - attributes = &gOpAttributes[op];
|
| - if (constantOperands == false)
|
| - addToken(kFlipOpsOp);
|
| - }
|
| - if (value1.fType == SkOperand2::kObject && (value1.fType & attributes->fLeftType) == 0) {
|
| - value1.fType = getUnboxType(value1.fOperand);
|
| - addToken(kUnboxToken);
|
| - }
|
| - }
|
| - if (value2.fType == SkOperand2::kObject && (value2.fType & attributes->fLeftType) == 0) {
|
| - value1.fType = getUnboxType(value2.fOperand);
|
| - addToken(kUnboxToken2);
|
| - }
|
| - if (attributes->fLeftType != SkOperand2::kNoType) {
|
| - if (value1.fType != value2.fType) {
|
| - if ((attributes->fLeftType & SkOperand2::kString) && attributes->fBias & kTowardsString &&
|
| - ((value1.fType | value2.fType) & SkOperand2::kString)) {
|
| - if (value1.fType == SkOperand2::kS32 || value1.fType == SkOperand2::kScalar) {
|
| - addTokenConst(&value1, kAccumulator, SkOperand2::kString,
|
| - value1.fType == SkOperand2::kS32 ? kIntToString : kScalarToString);
|
| - }
|
| - if (value2.fType == SkOperand2::kS32 || value2.fType == SkOperand2::kScalar) {
|
| - addTokenConst(&value2, kOperand, SkOperand2::kString,
|
| - value2.fType == SkOperand2::kS32 ? kIntToString2 : kScalarToString2);
|
| - }
|
| - } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) &
|
| - SkOperand2::kScalar)) {
|
| - if (value1.fType == SkOperand2::kS32)
|
| - addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kIntToScalar);
|
| - if (value2.fType == SkOperand2::kS32)
|
| - addTokenConst(&value2, kOperand, SkOperand2::kScalar, kIntToScalar2);
|
| - }
|
| - }
|
| - if ((value1.fType & attributes->fLeftType) == 0 || value1.fType != value2.fType) {
|
| - if (value1.fType == SkOperand2::kString)
|
| - addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kStringToScalar);
|
| - if (value1.fType == SkOperand2::kScalar && (attributes->fLeftType == SkOperand2::kS32 ||
|
| - value2.fType == SkOperand2::kS32))
|
| - addTokenConst(&value1, kAccumulator, SkOperand2::kS32, kScalarToInt);
|
| - }
|
| - }
|
| - AddTokenRegister rhRegister = attributes->fLeftType != SkOperand2::kNoType ?
|
| - kOperand : kAccumulator;
|
| - if ((value2.fType & attributes->fRightType) == 0 || value1.fType != value2.fType) {
|
| - if (value2.fType == SkOperand2::kString)
|
| - addTokenConst(&value2, rhRegister, SkOperand2::kScalar, kStringToScalar2);
|
| - if (value2.fType == SkOperand2::kScalar && (attributes->fRightType == SkOperand2::kS32 ||
|
| - value1.fType == SkOperand2::kS32))
|
| - addTokenConst(&value2, rhRegister, SkOperand2::kS32, kScalarToInt2);
|
| - }
|
| - TypeOp typeOp = gTokens[op];
|
| - if (value2.fType == SkOperand2::kScalar)
|
| - typeOp = (TypeOp) (typeOp + 1);
|
| - else if (value2.fType == SkOperand2::kString)
|
| - typeOp = (TypeOp) (typeOp + 2);
|
| - SkDynamicMemoryWStream stream;
|
| - SkOperand2::OpType saveType;
|
| - SkBool saveOperand;
|
| - if (constantOperands) {
|
| - fActiveStream = &stream;
|
| - saveType = fAccumulatorType;
|
| - saveOperand = fOperandInUse;
|
| - fAccumulatorType = SkOperand2::kNoType;
|
| - fOperandInUse = false;
|
| - }
|
| - if (attributes->fLeftType != SkOperand2::kNoType) { // two operands
|
| - if (value1.fIsWritten == SkScriptValue2::kUnwritten)
|
| - addTokenValue(value1, kAccumulator);
|
| - }
|
| - if (value2.fIsWritten == SkScriptValue2::kUnwritten)
|
| - addTokenValue(value2, rhRegister);
|
| - addToken(typeOp);
|
| - if (constantOperands) {
|
| - addToken(kEnd);
|
| -#ifdef SK_DEBUG
|
| - decompile((const unsigned char*) stream.getStream(), stream.getOffset());
|
| -#endif
|
| - SkScriptRuntime runtime(fCallBackArray);
|
| - runtime.executeTokens((unsigned char*) stream.getStream());
|
| - runtime.getResult(&value1.fOperand);
|
| - if (attributes->fResultIsBoolean == kResultIsBoolean)
|
| - value1.fType = SkOperand2::kS32;
|
| - else if (attributes->fLeftType == SkOperand2::kNoType) // unary operand
|
| - value1.fType = value2.fType;
|
| - fValueStack.push(value1);
|
| - if (value1.fType == SkOperand2::kString)
|
| - runtime.untrack(value1.fOperand.fString);
|
| - else if (value1.fType == SkOperand2::kArray)
|
| - runtime.untrack(value1.fOperand.fArray);
|
| - fActiveStream = &fStream;
|
| - fAccumulatorType = saveType;
|
| - fOperandInUse = saveOperand;
|
| - return true;
|
| - }
|
| - value2.fIsConstant = SkScriptValue2::kVariable;
|
| - fValueStack.push(value2);
|
| - return true;
|
| -}
|
| -
|
| -void SkScriptEngine2::Branch::resolve(SkDynamicMemoryWStream* stream, size_t off) {
|
| - SkASSERT(fDone == kIsNotDone);
|
| - fPrimed = kIsNotPrimed;
|
| - fDone = kIsDone;
|
| - SkASSERT(off > fOffset + sizeof(size_t));
|
| - size_t offset = off - fOffset - sizeof(offset);
|
| - stream->write(&offset, fOffset, sizeof(offset));
|
| -}
|
| -
|
| -void SkScriptEngine2::resolveBranch(SkScriptEngine2::Branch& branch) {
|
| - branch.resolve(fActiveStream, getTokenOffset());
|
| -}
|
| -
|
| -bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toType, SkScriptValue2* value ) {
|
| - SkASSERT(value);
|
| - SkOperand2::OpType type = value->fType;
|
| - if (type == toType)
|
| - return true;
|
| - SkOperand2& operand = value->fOperand;
|
| - bool success = true;
|
| - switch (toType) {
|
| - case SkOperand2::kS32:
|
| - if (type == SkOperand2::kScalar)
|
| - operand.fS32 = SkScalarFloor(operand.fScalar);
|
| - else {
|
| - SkASSERT(type == SkOperand2::kString);
|
| - success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
|
| - }
|
| - break;
|
| - case SkOperand2::kScalar:
|
| - if (type == SkOperand2::kS32)
|
| - operand.fScalar = IntToScalar(operand.fS32);
|
| - else {
|
| - SkASSERT(type == SkOperand2::kString);
|
| - success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
|
| - }
|
| - break;
|
| - case SkOperand2::kString: {
|
| - SkString* strPtr = new SkString();
|
| - SkASSERT(engine);
|
| - engine->track(strPtr);
|
| - if (type == SkOperand2::kS32)
|
| - strPtr->appendS32(operand.fS32);
|
| - else {
|
| - SkASSERT(type == SkOperand2::kScalar);
|
| - strPtr->appendScalar(operand.fScalar);
|
| - }
|
| - operand.fString = strPtr;
|
| - } break;
|
| - case SkOperand2::kArray: {
|
| - SkOpArray* array = new SkOpArray(type);
|
| - *array->append() = operand;
|
| - engine->track(array);
|
| - operand.fArray = array;
|
| - } break;
|
| - default:
|
| - SkASSERT(0);
|
| - }
|
| - value->fType = toType;
|
| - return success;
|
| -}
|
| -
|
| -SkScalar SkScriptEngine2::IntToScalar(int32_t s32) {
|
| - SkScalar scalar;
|
| - if (s32 == SK_NaN32)
|
| - scalar = SK_ScalarNaN;
|
| - else if (SkAbs32(s32) == SK_MaxS32)
|
| - scalar = SkSign32(s32) * SK_ScalarMax;
|
| - else
|
| - scalar = SkIntToScalar(s32);
|
| - return scalar;
|
| -}
|
| -
|
| -bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* string) {
|
| - switch (value.fType) {
|
| - case SkOperand2::kS32:
|
| - string->reset();
|
| - string->appendS32(value.fOperand.fS32);
|
| - break;
|
| - case SkOperand2::kScalar:
|
| - string->reset();
|
| - string->appendScalar(value.fOperand.fScalar);
|
| - break;
|
| - case SkOperand2::kString:
|
| - string->set(*value.fOperand.fString);
|
| - break;
|
| - default:
|
| - SkASSERT(0);
|
| - return false;
|
| - }
|
| - return true; // no error
|
| -}
|
| -
|
| -#ifdef SK_DEBUG
|
| -
|
| -#define testInt(expression) { #expression, SkOperand2::kS32, expression }
|
| -#ifdef SK_SCALAR_IS_FLOAT
|
| -#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression }
|
| -#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2) }
|
| -#else
|
| -#ifdef SK_CAN_USE_FLOAT
|
| -#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f) }
|
| -#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2) * 65536.0f) }
|
| -#endif
|
| -#endif
|
| -#define testTrue(expression) { #expression, SkOperand2::kS32, 1 }
|
| -#define testFalse(expression) { #expression, SkOperand2::kS32, 0 }
|
| -
|
| -#if !defined(SK_BUILD_FOR_BREW)
|
| -static const SkScriptNAnswer2 scriptTests[] = {
|
| - testInt(1||0&&3),
|
| -#ifdef SK_CAN_USE_FLOAT
|
| - testScalar(- -5.5- -1.5),
|
| - testScalar(1.0+5),
|
| -#endif
|
| - testInt((6+7)*8),
|
| - testInt(3*(4+5)),
|
| -#ifdef SK_CAN_USE_FLOAT
|
| - testScalar(1.0+2.0),
|
| - testScalar(3.0-1.0),
|
| - testScalar(6-1.0),
|
| - testScalar(2.5*6.),
|
| - testScalar(0.5*4),
|
| - testScalar(4.5/.5),
|
| - testScalar(9.5/19),
|
| - testRemainder(9.5, 0.5),
|
| - testRemainder(9.,2),
|
| - testRemainder(9,2.5),
|
| - testRemainder(-9,2.5),
|
| - testTrue(-9==-9.0),
|
| - testTrue(-9.==-4.0-5),
|
| - testTrue(-9.*1==-4-5),
|
| - testFalse(-9!=-9.0),
|
| - testFalse(-9.!=-4.0-5),
|
| - testFalse(-9.*1!=-4-5),
|
| -#endif
|
| - testInt(0x123),
|
| - testInt(0XABC),
|
| - testInt(0xdeadBEEF),
|
| - { "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" },
|
| - { "123+\"456\"", SkOperand2::kString, 0, 0, "123456" },
|
| - { "'123'+456", SkOperand2::kString, 0, 0, "123456" },
|
| - { "'123'|\"456\"", SkOperand2::kS32, 123|456 },
|
| - { "123|\"456\"", SkOperand2::kS32, 123|456 },
|
| - { "'123'|456", SkOperand2::kS32, 123|456 },
|
| - { "'2'<11", SkOperand2::kS32, 1 },
|
| - { "2<'11'", SkOperand2::kS32, 1 },
|
| - { "'2'<'11'", SkOperand2::kS32, 0 },
|
| - testInt(123),
|
| - testInt(-345),
|
| - testInt(+678),
|
| - testInt(1+2+3),
|
| - testInt(3*4+5),
|
| - testInt(6+7*8),
|
| - testInt(-1-2-8/4),
|
| - testInt(-9%4),
|
| - testInt(9%-4),
|
| - testInt(-9%-4),
|
| - testInt(123|978),
|
| - testInt(123&978),
|
| - testInt(123^978),
|
| - testInt(2<<4),
|
| - testInt(99>>3),
|
| - testInt(~55),
|
| - testInt(~~55),
|
| - testInt(!55),
|
| - testInt(!!55),
|
| - // both int
|
| - testInt(2<2),
|
| - testInt(2<11),
|
| - testInt(20<11),
|
| - testInt(2<=2),
|
| - testInt(2<=11),
|
| - testInt(20<=11),
|
| - testInt(2>2),
|
| - testInt(2>11),
|
| - testInt(20>11),
|
| - testInt(2>=2),
|
| - testInt(2>=11),
|
| - testInt(20>=11),
|
| - testInt(2==2),
|
| - testInt(2==11),
|
| - testInt(20==11),
|
| - testInt(2!=2),
|
| - testInt(2!=11),
|
| - testInt(20!=11),
|
| -#ifdef SK_CAN_USE_FLOAT
|
| - // left int, right scalar
|
| - testInt(2<2.),
|
| - testInt(2<11.),
|
| - testInt(20<11.),
|
| - testInt(2<=2.),
|
| - testInt(2<=11.),
|
| - testInt(20<=11.),
|
| - testInt(2>2.),
|
| - testInt(2>11.),
|
| - testInt(20>11.),
|
| - testInt(2>=2.),
|
| - testInt(2>=11.),
|
| - testInt(20>=11.),
|
| - testInt(2==2.),
|
| - testInt(2==11.),
|
| - testInt(20==11.),
|
| - testInt(2!=2.),
|
| - testInt(2!=11.),
|
| - testInt(20!=11.),
|
| - // left scalar, right int
|
| - testInt(2.<2),
|
| - testInt(2.<11),
|
| - testInt(20.<11),
|
| - testInt(2.<=2),
|
| - testInt(2.<=11),
|
| - testInt(20.<=11),
|
| - testInt(2.>2),
|
| - testInt(2.>11),
|
| - testInt(20.>11),
|
| - testInt(2.>=2),
|
| - testInt(2.>=11),
|
| - testInt(20.>=11),
|
| - testInt(2.==2),
|
| - testInt(2.==11),
|
| - testInt(20.==11),
|
| - testInt(2.!=2),
|
| - testInt(2.!=11),
|
| - testInt(20.!=11),
|
| - // both scalar
|
| - testInt(2.<11.),
|
| - testInt(20.<11.),
|
| - testInt(2.<=2.),
|
| - testInt(2.<=11.),
|
| - testInt(20.<=11.),
|
| - testInt(2.>2.),
|
| - testInt(2.>11.),
|
| - testInt(20.>11.),
|
| - testInt(2.>=2.),
|
| - testInt(2.>=11.),
|
| - testInt(20.>=11.),
|
| - testInt(2.==2.),
|
| - testInt(2.==11.),
|
| - testInt(20.==11.),
|
| - testInt(2.!=2.),
|
| - testInt(2.!=11.),
|
| - testInt(20.!=11.),
|
| -#endif
|
| - // int, string (string is int)
|
| - testFalse(2<'2'),
|
| - testTrue(2<'11'),
|
| - testFalse(20<'11'),
|
| - testTrue(2<='2'),
|
| - testTrue(2<='11'),
|
| - testFalse(20<='11'),
|
| - testFalse(2>'2'),
|
| - testFalse(2>'11'),
|
| - testTrue(20>'11'),
|
| - testTrue(2>='2'),
|
| - testFalse(2>='11'),
|
| - testTrue(20>='11'),
|
| - testTrue(2=='2'),
|
| - testFalse(2=='11'),
|
| - testFalse(2!='2'),
|
| - testTrue(2!='11'),
|
| - // int, string (string is scalar)
|
| - testFalse(2<'2.'),
|
| - testTrue(2<'11.'),
|
| - testFalse(20<'11.'),
|
| - testTrue(2=='2.'),
|
| - testFalse(2=='11.'),
|
| -#ifdef SK_CAN_USE_FLOAT
|
| - // scalar, string
|
| - testFalse(2.<'2.'),
|
| - testTrue(2.<'11.'),
|
| - testFalse(20.<'11.'),
|
| - testTrue(2.=='2.'),
|
| - testFalse(2.=='11.'),
|
| - // string, int
|
| - testFalse('2'<2),
|
| - testTrue('2'<11),
|
| - testFalse('20'<11),
|
| - testTrue('2'==2),
|
| - testFalse('2'==11),
|
| - // string, scalar
|
| - testFalse('2'<2.),
|
| - testTrue('2'<11.),
|
| - testFalse('20'<11.),
|
| - testTrue('2'==2.),
|
| - testFalse('2'==11.),
|
| -#endif
|
| - // string, string
|
| - testFalse('2'<'2'),
|
| - testFalse('2'<'11'),
|
| - testFalse('20'<'11'),
|
| - testTrue('2'=='2'),
|
| - testFalse('2'=='11'),
|
| - // logic
|
| - testInt(1?2:3),
|
| - testInt(0?2:3),
|
| - testInt(1&&2||3),
|
| - testInt(1&&0||3),
|
| - testInt(1&&0||0),
|
| - testInt(1||0&&3),
|
| - testInt(0||0&&3),
|
| - testInt(0||1&&3),
|
| - testInt(0&&1?2:3)
|
| -#ifdef SK_CAN_USE_FLOAT
|
| - , { "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2 }
|
| -#endif
|
| -};
|
| -#endif // build for brew
|
| -
|
| -#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests)
|
| -
|
| -void SkScriptEngine2::UnitTest() {
|
| -#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST)
|
| - ValidateDecompileTable();
|
| - for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
|
| - SkScriptEngine2 engine(scriptTests[index].fType);
|
| - SkScriptValue2 value;
|
| - const char* script = scriptTests[index].fScript;
|
| - const char* scriptPtr = script;
|
| - SkASSERT(engine.evaluateScript(&scriptPtr, &value) == true);
|
| - SkASSERT(value.fType == scriptTests[index].fType);
|
| - SkScalar error;
|
| - switch (value.fType) {
|
| - case SkOperand2::kS32:
|
| - if (value.fOperand.fS32 != scriptTests[index].fIntAnswer)
|
| - SkDEBUGF(("script '%s' == value %d != expected answer %d\n", script, value.fOperand.fS32, scriptTests[index].fIntAnswer));
|
| - SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
|
| - break;
|
| - case SkOperand2::kScalar:
|
| - error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
|
| -#ifdef SK_CAN_USE_FLOAT
|
| - if (error >= SK_Scalar1 / 10000)
|
| - SkDEBUGF(("script '%s' == value %g != expected answer %g\n", script, value.fOperand.fScalar / (1.0f * SK_Scalar1), scriptTests[index].fScalarAnswer / (1.0f * SK_Scalar1)));
|
| -#endif
|
| - SkASSERT(error < SK_Scalar1 / 10000);
|
| - break;
|
| - case SkOperand2::kString:
|
| - SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
|
| - break;
|
| - default:
|
| - SkASSERT(0);
|
| - }
|
| - }
|
| -#endif
|
| -}
|
| -#endif
|
|
|