Index: skia/animator/SkScript.cpp |
=================================================================== |
--- skia/animator/SkScript.cpp (revision 16859) |
+++ skia/animator/SkScript.cpp (working copy) |
@@ -1,1918 +0,0 @@ |
-/* libs/graphics/animator/SkScript.cpp |
-** |
-** Copyright 2006, The Android Open Source Project |
-** |
-** Licensed under the Apache License, Version 2.0 (the "License"); |
-** you may not use this file except in compliance with the License. |
-** You may obtain a copy of the License at |
-** |
-** http://www.apache.org/licenses/LICENSE-2.0 |
-** |
-** Unless required by applicable law or agreed to in writing, software |
-** distributed under the License is distributed on an "AS IS" BASIS, |
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-** See the License for the specific language governing permissions and |
-** limitations under the License. |
-*/ |
- |
-#include "SkScript.h" |
-#include "SkMath.h" |
-#include "SkParse.h" |
-#include "SkString.h" |
-#include "SkTypedArray.h" |
- |
-/* things to do |
- ? re-enable support for struct literals (e.g., for initializing points or rects) |
- {x:1, y:2} |
- ? use standard XML / script notation like document.getElementById("canvas"); |
- finish support for typed arrays |
- ? allow indexing arrays by string |
- this could map to the 'name' attribute of a given child of an array |
- ? allow multiple types in the array |
- remove SkDisplayType.h // from SkOperand.h |
- merge type and operand arrays into scriptvalue array |
-*/ |
- |
-#ifdef SK_DEBUG |
-static const char* errorStrings[] = { |
- "array index of out bounds", // kArrayIndexOutOfBounds |
- "could not find reference id", // kCouldNotFindReferencedID |
- "dot operator expects object", // kDotOperatorExpectsObject |
- "error in array index", // kErrorInArrrayIndex |
- "error in function parameters", // kErrorInFunctionParameters |
- "expected array", // kExpectedArray |
- "expected boolean expression", // kExpectedBooleanExpression |
- "expected field name", // kExpectedFieldName |
- "expected hex", // kExpectedHex |
- "expected int for condition operator", // kExpectedIntForConditionOperator |
- "expected number", // kExpectedNumber |
- "expected number for array index", // kExpectedNumberForArrayIndex |
- "expected operator", // kExpectedOperator |
- "expected token", // kExpectedToken |
- "expected token before dot operator", // kExpectedTokenBeforeDotOperator |
- "expected value", // kExpectedValue |
- "handle member failed", // kHandleMemberFailed |
- "handle member function failed", // kHandleMemberFunctionFailed |
- "handle unbox failed", // kHandleUnboxFailed |
- "index out of range", // kIndexOutOfRange |
- "mismatched array brace", // kMismatchedArrayBrace |
- "mismatched brackets", // kMismatchedBrackets |
- "no function handler found", // kNoFunctionHandlerFound |
- "premature end", // kPrematureEnd |
- "too many parameters", // kTooManyParameters |
- "type conversion failed", // kTypeConversionFailed |
- "unterminated string" // kUnterminatedString |
-}; |
-#endif |
- |
-const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = { |
- { kNoType, kNoType, kNoBias }, // kUnassigned, |
- { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd |
- // kAddInt = kAdd, |
- { kNoType, kNoType, kNoBias }, // kAddScalar, |
- { kNoType, kNoType, kNoBias }, // kAddString, |
- { kNoType, kNoType, kNoBias }, // kArrayOp, |
- { kInt, kInt, kNoBias }, // kBitAnd |
- { kNoType, kInt, kNoBias }, // kBitNot |
- { kInt, kInt, kNoBias }, // kBitOr |
- { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide |
- // kDivideInt = kDivide |
- { kNoType, kNoType, kNoBias }, // kDivideScalar |
- { kNoType, kNoType, kNoBias }, // kElse |
- { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual |
- // kEqualInt = kEqual |
- { kNoType, kNoType, kNoBias }, // kEqualScalar |
- { kNoType, kNoType, kNoBias }, // kEqualString |
- { kInt, kNoType, kNoBias }, // kFlipOps |
- { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual |
- // kGreaterEqualInt = kGreaterEqual |
- { kNoType, kNoType, kNoBias }, // kGreaterEqualScalar |
- { kNoType, kNoType, kNoBias }, // kGreaterEqualString |
- { kNoType, kNoType, kNoBias }, // kIf |
- { kNoType, kInt, kNoBias }, // kLogicalAnd (really, ToBool) |
- { kNoType, kInt, kNoBias }, // kLogicalNot |
- { kInt, kInt, kNoBias }, // kLogicalOr |
- { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus |
- // kMinusInt = kMinus |
- { kNoType, kNoType, kNoBias }, // kMinusScalar |
- { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo |
- // kModuloInt = kModulo |
- { kNoType, kNoType, kNoBias }, // kModuloScalar |
- { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply |
- // kMultiplyInt = kMultiply |
- { kNoType, kNoType, kNoBias }, // kMultiplyScalar |
- { kNoType, kNoType, kNoBias }, // kParen |
- { kInt, kInt, kNoBias }, // kShiftLeft |
- { kInt, kInt, kNoBias }, // kShiftRight |
- { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract |
- // kSubtractInt = kSubtract |
- { kNoType, kNoType, kNoBias }, // kSubtractScalar |
- { kInt, kInt, kNoBias } // kXor |
-}; |
- |
-// Note that the real precedence for () [] is '2' |
-// but here, precedence means 'while an equal or smaller precedence than the current operator |
-// is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply |
-// is preformed, since the add precedence is not smaller than multiply. |
-// But, (3*4 does not process the '(', since brackets are greater than all other precedences |
-#define kBracketPrecedence 16 |
-#define kIfElsePrecedence 15 |
- |
-const signed char SkScriptEngine::gPrecedence[] = { |
- -1, // kUnassigned, |
- 6, // kAdd, |
- // kAddInt = kAdd, |
- 6, // kAddScalar, |
- 6, // kAddString, // string concat |
- kBracketPrecedence, // kArrayOp, |
- 10, // kBitAnd, |
- 4, // kBitNot, |
- 12, // kBitOr, |
- 5, // kDivide, |
- // kDivideInt = kDivide, |
- 5, // kDivideScalar, |
- kIfElsePrecedence, // kElse, |
- 9, // kEqual, |
- // kEqualInt = kEqual, |
- 9, // kEqualScalar, |
- 9, // kEqualString, |
- -1, // kFlipOps, |
- 8, // kGreaterEqual, |
- // kGreaterEqualInt = kGreaterEqual, |
- 8, // kGreaterEqualScalar, |
- 8, // kGreaterEqualString, |
- kIfElsePrecedence, // kIf, |
- 13, // kLogicalAnd, |
- 4, // kLogicalNot, |
- 14, // kLogicalOr, |
- 4, // kMinus, |
- // kMinusInt = kMinus, |
- 4, // kMinusScalar, |
- 5, // kModulo, |
- // kModuloInt = kModulo, |
- 5, // kModuloScalar, |
- 5, // kMultiply, |
- // kMultiplyInt = kMultiply, |
- 5, // kMultiplyScalar, |
- kBracketPrecedence, // kParen, |
- 7, // kShiftLeft, |
- 7, // kShiftRight, // signed |
- 6, // kSubtract, |
- // kSubtractInt = kSubtract, |
- 6, // kSubtractScalar, |
- 11, // 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; |
-} |
- |
-SkScriptEngine::SkScriptEngine(SkOpType returnType) : |
- fTokenLength(0), fReturnType(returnType), fError(kNoError) |
-{ |
- SkSuppress noInitialSuppress; |
- noInitialSuppress.fOperator = kUnassigned; |
- noInitialSuppress.fOpStackDepth = 0; |
- noInitialSuppress.fSuppress = false; |
- fSuppressStack.push(noInitialSuppress); |
- *fOpStack.push() = kParen; |
- fTrackArray.appendClear(); |
- fTrackString.appendClear(); |
-} |
- |
-SkScriptEngine::~SkScriptEngine() { |
- for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++) |
- delete *stringPtr; |
- for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++) |
- delete *arrayPtr; |
-} |
- |
-int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) { |
- SkOp 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 = kIf; |
- break; |
- case ':': |
- op = kElse; |
- break; |
- case '^': |
- op = kXor; |
- break; |
- case '(': |
- *fOpStack.push() = kParen; // push even if eval is suppressed |
- 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; |
- if (fSuppressStack.top().fSuppress == false) { |
- signed char precedence = gPrecedence[op]; |
- do { |
- int idx = 0; |
- SkOp 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 == kNoType) { |
- break; |
- } |
- if (processOp() == false) |
- return 0; // error |
- } while (true); |
- if (negateResult) |
- *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp); |
- fOpStack.push(op); |
- if (reverseOperands) |
- *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp); |
- } |
-returnAdv: |
- return advance; |
-} |
- |
-void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) { |
- UserCallBack callBack; |
- callBack.fBoxCallBack = func; |
- commonCallBack(kBox, callBack, userStorage); |
-} |
- |
-void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) { |
- callBack.fCallBackType = type; |
- callBack.fUserStorage = userStorage; |
- *fUserCallBacks.prepend() = callBack; |
-} |
- |
-bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params, |
- const SkFunctionParamType* paramTypes, int paramCount) { |
- if (params.count() > paramCount) { |
- fError = kTooManyParameters; |
- return false; // too many parameters passed |
- } |
- for (int index = 0; index < params.count(); index++) { |
- if (convertTo((SkDisplayTypes) paramTypes[index], ¶ms[index]) == false) |
- return false; |
- } |
- return true; |
-} |
- |
-bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) { |
- SkDisplayTypes type = value->fType; |
- if (type == toType) |
- return true; |
- if (ToOpType(type) == kObject) { |
-#if 0 // !!! I want object->string to get string from displaystringtype, not id |
- if (ToOpType(toType) == kString) { |
- bool success = handleObjectToString(value->fOperand.fObject); |
- if (success == false) |
- return false; |
- SkOpType type; |
- fTypeStack.pop(&type); |
- value->fType = ToDisplayType(type); |
- fOperandStack.pop(&value->fOperand); |
- return true; |
- } |
-#endif |
- if (handleUnbox(value) == false) { |
- fError = kHandleUnboxFailed; |
- return false; |
- } |
- return convertTo(toType, value); |
- } |
- return ConvertTo(this, toType, value); |
-} |
- |
-bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) { |
- size_t fieldLength = token_length(++script); // skip dot |
- if (fieldLength == 0) { |
- fError = kExpectedFieldName; |
- return false; |
- } |
- const char* field = script; |
- script += fieldLength; |
- bool success = handleProperty(suppressed); |
- if (success == false) { |
- fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins |
- return false; |
- } |
- return evaluateDotParam(script, suppressed, field, fieldLength); |
-} |
- |
-bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed, |
- const char* field, size_t fieldLength) { |
- void* object; |
- if (suppressed) |
- object = NULL; |
- else { |
- if (fTypeStack.top() != kObject) { |
- fError = kDotOperatorExpectsObject; |
- return false; |
- } |
- object = fOperandStack.top().fObject; |
- fTypeStack.pop(); |
- fOperandStack.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 != '(') { |
- if (suppressed == false) { |
- if ((success = handleMember(field, fieldLength, object)) == false) |
- fError = kHandleMemberFailed; |
- } |
- } else { |
- SkTDArray<SkScriptValue> params; |
- *fBraceStack.push() = kFunctionBrace; |
- success = functionParams(&script, params); |
- if (success && suppressed == false && |
- (success = handleMemberFunction(field, fieldLength, object, params)) == false) |
- fError = kHandleMemberFunctionFailed; |
- } |
- return success; |
-} |
- |
-bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) { |
-#ifdef SK_DEBUG |
- const char** original = scriptPtr; |
-#endif |
- bool success; |
- const char* inner; |
- if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) { |
- *scriptPtr += sizeof("#script:") - 1; |
- if (fReturnType == kNoType || fReturnType == kString) { |
- success = innerScript(scriptPtr, value); |
- if (success == false) |
- goto end; |
- inner = value->fOperand.fString->c_str(); |
- scriptPtr = &inner; |
- } |
- } |
- { |
- success = innerScript(scriptPtr, value); |
- if (success == false) |
- goto end; |
- 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]" |
- fError = kPrematureEnd; |
- success = false; |
- } |
- } |
-end: |
-#ifdef SK_DEBUG |
- if (success == false) { |
- SkDebugf("script failed: %s", *original); |
- if (fError) |
- SkDebugf(" %s", errorStrings[fError - 1]); |
- SkDebugf("\n"); |
- } |
-#endif |
- return success; |
-} |
- |
-void SkScriptEngine::forget(SkTypedArray* array) { |
- if (array->getType() == SkType_String) { |
- 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() == SkType_Array) { |
- for (int index = 0; index < array->count(); index++) { |
- SkTypedArray* child = (*array)[index].fArray; |
- forget(child); // forgets children of child |
- int found = fTrackArray.find(child); |
- if (found >= 0) |
- fTrackArray.remove(found); |
- } |
- } |
-} |
- |
-void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) { |
- UserCallBack callBack; |
- callBack.fFunctionCallBack = func; |
- commonCallBack(kFunction, callBack, userStorage); |
-} |
- |
-bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) { |
- (*scriptPtr)++; // skip open paren |
- *fOpStack.push() = kParen; |
- *fBraceStack.push() = kFunctionBrace; |
- SkBool suppressed = fSuppressStack.top().fSuppress; |
- do { |
- SkScriptValue value; |
- bool success = innerScript(scriptPtr, suppressed ? NULL : &value); |
- if (success == false) { |
- fError = kErrorInFunctionParameters; |
- return false; |
- } |
- if (suppressed) |
- continue; |
- *params.append() = value; |
- } while ((*scriptPtr)[-1] == ','); |
- fBraceStack.pop(); |
- fOpStack.pop(); // pop paren |
- (*scriptPtr)++; // advance beyond close paren |
- return true; |
-} |
- |
-#ifdef SK_DEBUG |
-bool SkScriptEngine::getErrorString(SkString* str) const { |
- if (fError) |
- str->set(errorStrings[fError - 1]); |
- return fError != 0; |
-} |
-#endif |
- |
-bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) { |
- const char* script = *scriptPtr; |
- char ch; |
- bool lastPush = false; |
- bool success = true; |
- int opBalance = fOpStack.count(); |
- int baseBrace = fBraceStack.count(); |
- int suppressBalance = fSuppressStack.count(); |
- while ((ch = script[0]) != '\0') { |
- if (is_ws(ch)) { |
- script++; |
- continue; |
- } |
- SkBool suppressed = fSuppressStack.top().fSuppress; |
- SkOperand operand; |
- const char* dotCheck; |
- if (fBraceStack.count() > baseBrace) { |
-#if 0 // disable support for struct brace |
- if (ch == ':') { |
- SkASSERT(fTokenLength > 0); |
- SkASSERT(fBraceStack.top() == kStructBrace); |
- ++script; |
- SkASSERT(fDisplayable); |
- SkString token(fToken, fTokenLength); |
- fTokenLength = 0; |
- const char* tokenName = token.c_str(); |
- const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING; |
- if (suppressed == false) { |
- SkDisplayTypes type = fInfo->getType(); |
- tokenInfo = SkDisplayType::GetMember(type, &tokenName); |
- SkASSERT(tokenInfo); |
- } |
- SkScriptValue tokenValue; |
- success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace |
- SkASSERT(success); |
- if (suppressed == false) { |
- if (tokenValue.fType == SkType_Displayable) { |
- SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType())); |
- fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable); |
- } else { |
- if (tokenValue.fType != tokenInfo->getType()) { |
- if (convertTo(tokenInfo->getType(), &tokenValue) == false) |
- return false; |
- } |
- tokenInfo->writeValue(fDisplayable, NULL, 0, 0, |
- (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset), |
- tokenInfo->getType(), tokenValue); |
- } |
- } |
- lastPush = false; |
- continue; |
- } else |
-#endif |
- if (fBraceStack.top() == kArrayBrace) { |
- SkScriptValue tokenValue; |
- success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace |
- if (success == false) { |
- fError = kErrorInArrrayIndex; |
- return false; |
- } |
- if (suppressed == false) { |
-#if 0 // no support for structures for now |
- if (tokenValue.fType == SkType_Structure) { |
- fArrayOffset += (int) fInfo->getSize(fDisplayable); |
- } else |
-#endif |
- { |
- SkDisplayTypes type = ToDisplayType(fReturnType); |
- if (fReturnType == 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) { |
- if (convertTo(type, &tokenValue) == false) |
- return false; |
- } |
- *value->fOperand.fArray->append() = tokenValue.fOperand; |
- } |
- } |
- lastPush = false; |
- continue; |
- } else { |
- if (token_length(script) == 0) { |
- fError = kExpectedToken; |
- return false; |
- } |
- } |
- } |
- if (lastPush != false && fTokenLength > 0) { |
- if (ch == '(') { |
- *fBraceStack.push() = kFunctionBrace; |
- if (handleFunction(&script, SkToBool(suppressed)) == false) |
- return false; |
- lastPush = true; |
- continue; |
- } else if (ch == '[') { |
- if (handleProperty(SkToBool(suppressed)) == false) |
- return false; // note: never triggered by standard animator plugins |
- if (handleArrayIndexer(&script, SkToBool(suppressed)) == false) |
- return false; |
- lastPush = true; |
- continue; |
- } else if (ch != '.') { |
- if (handleProperty(SkToBool(suppressed)) == false) |
- return false; // note: never triggered by standard animator plugins |
- lastPush = true; |
- continue; |
- } |
- } |
- if (ch == '0' && (script[1] & ~0x20) == 'X') { |
- if (lastPush != false) { |
- fError = kExpectedOperator; |
- return false; |
- } |
- script += 2; |
- script = SkParse::FindHex(script, (uint32_t*)&operand.fS32); |
- if (script == NULL) { |
- fError = kExpectedHex; |
- return false; |
- } |
- goto intCommon; |
- } |
- if (lastPush == false && ch == '.') |
- goto scalarCommon; |
- if (ch >= '0' && ch <= '9') { |
- if (lastPush != false) { |
- fError = kExpectedOperator; |
- return false; |
- } |
- dotCheck = SkParse::FindS32(script, &operand.fS32); |
- if (dotCheck[0] != '.') { |
- script = dotCheck; |
-intCommon: |
- if (suppressed == false) |
- *fTypeStack.push() = kInt; |
- } else { |
-scalarCommon: |
- script = SkParse::FindScalar(script, &operand.fScalar); |
- if (suppressed == false) |
- *fTypeStack.push() = kScalar; |
- } |
- if (suppressed == false) |
- fOperandStack.push(operand); |
- lastPush = true; |
- continue; |
- } |
- int length = token_length(script); |
- if (length > 0) { |
- if (lastPush != false) { |
- fError = kExpectedOperator; |
- return false; |
- } |
- fToken = script; |
- fTokenLength = length; |
- script += length; |
- lastPush = true; |
- continue; |
- } |
- char startQuote = ch; |
- if (startQuote == '\'' || startQuote == '\"') { |
- if (lastPush != false) { |
- fError = kExpectedOperator; |
- return false; |
- } |
- operand.fString = new SkString(); |
- track(operand.fString); |
- ++script; |
- |
- // <mrr> this is a lot of calls to append() one char at at time |
- // how hard to preflight script so we know how much to grow fString by? |
- do { |
- if (script[0] == '\\') |
- ++script; |
- operand.fString->append(script, 1); |
- ++script; |
- if (script[0] == '\0') { |
- fError = kUnterminatedString; |
- return false; |
- } |
- } while (script[0] != startQuote); |
- ++script; |
- if (suppressed == false) { |
- *fTypeStack.push() = kString; |
- fOperandStack.push(operand); |
- } |
- lastPush = true; |
- continue; |
- } |
- ; |
- if (ch == '.') { |
- if (fTokenLength == 0) { |
- SkScriptValue scriptValue; |
- SkDEBUGCODE(scriptValue.fOperand.fObject = NULL); |
- int tokenLength = token_length(++script); |
- const char* token = script; |
- script += tokenLength; |
- if (suppressed == false) { |
- if (fTypeStack.count() == 0) { |
- fError = kExpectedTokenBeforeDotOperator; |
- return false; |
- } |
- SkOpType topType; |
- fTypeStack.pop(&topType); |
- fOperandStack.pop(&scriptValue.fOperand); |
- scriptValue.fType = ToDisplayType(topType); |
- handleBox(&scriptValue); |
- } |
- success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength); |
- if (success == false) |
- return false; |
- lastPush = true; |
- continue; |
- } |
- // get next token, and evaluate immediately |
- success = evaluateDot(script, SkToBool(suppressed)); |
- if (success == false) |
- return false; |
- lastPush = true; |
- continue; |
- } |
- if (ch == '[') { |
- if (lastPush == false) { |
- script++; |
- *fBraceStack.push() = kArrayBrace; |
- if (suppressed) |
- continue; |
- operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType)); |
- track(value->fOperand.fArray); |
- *fTypeStack.push() = (SkOpType) kArray; |
- fOperandStack.push(operand); |
- continue; |
- } |
- if (handleArrayIndexer(&script, SkToBool(suppressed)) == false) |
- return false; |
- lastPush = true; |
- continue; |
- } |
-#if 0 // structs not supported for now |
- if (ch == '{') { |
- if (lastPush == false) { |
- script++; |
- *fBraceStack.push() = kStructBrace; |
- if (suppressed) |
- continue; |
- 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) { |
- SkBraceStyle braceStyle = fBraceStack.top(); |
- if (braceStyle == kFunctionBrace) { |
- fBraceStack.pop(); |
- break; |
- } |
- } |
- if (ch == ',' || ch == ']') { |
- if (ch != ',') { |
- SkBraceStyle match; |
- fBraceStack.pop(&match); |
- if (match != kArrayBrace) { |
- fError = kMismatchedArrayBrace; |
- return false; |
- } |
- } |
- script++; |
- // !!! see if brace or bracket is correct closer |
- break; |
- } |
- char nextChar = script[1]; |
- int advance = logicalOp(ch, nextChar); |
- if (advance < 0) // error |
- return false; |
- if (advance == 0) |
- advance = arithmeticOp(ch, nextChar, lastPush); |
- if (advance == 0) // unknown token |
- return false; |
- if (advance > 0) |
- script += advance; |
- lastPush = ch == ']' || ch == ')'; |
- } |
- bool suppressed = SkToBool(fSuppressStack.top().fSuppress); |
- if (fTokenLength > 0) { |
- success = handleProperty(suppressed); |
- if (success == false) |
- return false; // note: never triggered by standard animator plugins |
- } |
- while (fOpStack.count() > opBalance) { // leave open paren |
- if ((fError = opError()) != kNoError) |
- return false; |
- if (processOp() == false) |
- return false; |
- } |
- SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType; |
- if (suppressed == false && topType != fReturnType && |
- topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value |
- SkString* string = fOperandStack.top().fString; |
- fToken = string->c_str(); |
- fTokenLength = string->size(); |
- fOperandStack.pop(); |
- fTypeStack.pop(); |
- success = handleProperty(SkToBool(fSuppressStack.top().fSuppress)); |
- if (success == false) { // if it couldn't convert, return string (error?) |
- SkOperand operand; |
- operand.fS32 = 0; |
- *fTypeStack.push() = kString; |
- operand.fString = string; |
- fOperandStack.push(operand); |
- } |
- } |
- if (value) { |
- if (fOperandStack.count() == 0) |
- return false; |
- SkASSERT(fOperandStack.count() >= 1); |
- SkASSERT(fTypeStack.count() >= 1); |
- fOperandStack.pop(&value->fOperand); |
- SkOpType type; |
- fTypeStack.pop(&type); |
- value->fType = ToDisplayType(type); |
-// SkASSERT(value->fType != SkType_Unknown); |
- if (topType != fReturnType && topType == kObject && fReturnType != kNoType) { |
- if (convertTo(ToDisplayType(fReturnType), value) == false) |
- return false; |
- } |
- } |
- while (fSuppressStack.count() > suppressBalance) |
- fSuppressStack.pop(); |
- *scriptPtr = script; |
- return true; // no error |
-} |
- |
-void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) { |
- UserCallBack callBack; |
- callBack.fMemberCallBack = member; |
- commonCallBack(kMember, callBack, userStorage); |
-} |
- |
-void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) { |
- UserCallBack callBack; |
- callBack.fMemberFunctionCallBack = func; |
- commonCallBack(kMemberFunction, callBack, userStorage); |
-} |
- |
-#if 0 |
-void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) { |
- UserCallBack callBack; |
- callBack.fObjectToStringCallBack = func; |
- commonCallBack(kObjectToString, callBack, userStorage); |
-} |
-#endif |
- |
-bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) { |
- SkScriptValue scriptValue; |
- (*scriptPtr)++; |
- *fOpStack.push() = kParen; |
- *fBraceStack.push() = kArrayBrace; |
- SkOpType saveType = fReturnType; |
- fReturnType = kInt; |
- bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL); |
- if (success == false) |
- return false; |
- fReturnType = saveType; |
- if (suppressed == false) { |
- if (convertTo(SkType_Int, &scriptValue) == false) |
- return false; |
- int index = scriptValue.fOperand.fS32; |
- SkScriptValue scriptValue; |
- SkOpType type; |
- fTypeStack.pop(&type); |
- fOperandStack.pop(&scriptValue.fOperand); |
- scriptValue.fType = ToDisplayType(type); |
- if (type == kObject) { |
- success = handleUnbox(&scriptValue); |
- if (success == false) |
- return false; |
- if (ToOpType(scriptValue.fType) != kArray) { |
- fError = kExpectedArray; |
- return false; |
- } |
- } |
- *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType(); |
-// SkASSERT(index >= 0); |
- if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) { |
- fError = kArrayIndexOutOfBounds; |
- return false; |
- } |
- scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index]; |
- fOperandStack.push(scriptValue.fOperand); |
- } |
- fOpStack.pop(); // pop paren |
- return success; |
-} |
- |
-bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) { |
- bool success = true; |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kBox) |
- continue; |
- success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue); |
- if (success) { |
- fOperandStack.push(scriptValue->fOperand); |
- *fTypeStack.push() = ToOpType(scriptValue->fType); |
- goto done; |
- } |
- } |
-done: |
- return success; |
-} |
- |
-bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) { |
- SkScriptValue callbackResult; |
- SkTDArray<SkScriptValue> params; |
- SkString functionName(fToken, fTokenLength); |
- fTokenLength = 0; |
- bool success = functionParams(scriptPtr, params); |
- if (success == false) |
- goto done; |
- if (suppressed == true) |
- return true; |
- { |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kFunction) |
- continue; |
- success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params, |
- callBack->fUserStorage, &callbackResult); |
- if (success) { |
- fOperandStack.push(callbackResult.fOperand); |
- *fTypeStack.push() = ToOpType(callbackResult.fType); |
- goto done; |
- } |
- } |
- } |
- fError = kNoFunctionHandlerFound; |
- return false; |
-done: |
- return success; |
-} |
- |
-bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) { |
- SkScriptValue callbackResult; |
- bool success = true; |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kMember) |
- continue; |
- success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult); |
- if (success) { |
- if (callbackResult.fType == SkType_String) |
- track(callbackResult.fOperand.fString); |
- fOperandStack.push(callbackResult.fOperand); |
- *fTypeStack.push() = ToOpType(callbackResult.fType); |
- goto done; |
- } |
- } |
- return false; |
-done: |
- return success; |
-} |
- |
-bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) { |
- SkScriptValue callbackResult; |
- bool success = true; |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kMemberFunction) |
- continue; |
- success = (*callBack->fMemberFunctionCallBack)(field, len, object, params, |
- callBack->fUserStorage, &callbackResult); |
- if (success) { |
- if (callbackResult.fType == SkType_String) |
- track(callbackResult.fOperand.fString); |
- fOperandStack.push(callbackResult.fOperand); |
- *fTypeStack.push() = ToOpType(callbackResult.fType); |
- goto done; |
- } |
- } |
- return false; |
-done: |
- return success; |
-} |
- |
-#if 0 |
-bool SkScriptEngine::handleObjectToString(void* object) { |
- SkScriptValue callbackResult; |
- bool success = true; |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kObjectToString) |
- continue; |
- success = (*callBack->fObjectToStringCallBack)(object, |
- callBack->fUserStorage, &callbackResult); |
- if (success) { |
- if (callbackResult.fType == SkType_String) |
- track(callbackResult.fOperand.fString); |
- fOperandStack.push(callbackResult.fOperand); |
- *fTypeStack.push() = ToOpType(callbackResult.fType); |
- goto done; |
- } |
- } |
- return false; |
-done: |
- return success; |
-} |
-#endif |
- |
-bool SkScriptEngine::handleProperty(bool suppressed) { |
- SkScriptValue callbackResult; |
- bool success = true; |
- if (suppressed) |
- goto done; |
- success = false; // note that with standard animator-script plugins, callback never returns false |
- { |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kProperty) |
- continue; |
- success = (*callBack->fPropertyCallBack)(fToken, fTokenLength, |
- callBack->fUserStorage, &callbackResult); |
- if (success) { |
- if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) { |
- callbackResult.fOperand.fString = new SkString(fToken, fTokenLength); |
- track(callbackResult.fOperand.fString); |
- } |
- fOperandStack.push(callbackResult.fOperand); |
- *fTypeStack.push() = ToOpType(callbackResult.fType); |
- goto done; |
- } |
- } |
- } |
-done: |
- fTokenLength = 0; |
- return success; |
-} |
- |
-bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) { |
- bool success = true; |
- for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) { |
- if (callBack->fCallBackType != kUnbox) |
- continue; |
- success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue); |
- if (success) { |
- if (scriptValue->fType == SkType_String) |
- 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 SkScriptEngine::logicalOp(char ch, char nextChar) { |
- int advance = 1; |
- SkOp match; |
- signed char precedence; |
- switch (ch) { |
- case ')': |
- match = kParen; |
- break; |
- case ']': |
- match = kArrayOp; |
- break; |
- case '?': |
- match = kIf; |
- break; |
- case ':': |
- match = kElse; |
- break; |
- case '&': |
- if (nextChar != '&') |
- goto noMatch; |
- match = kLogicalAnd; |
- advance = 2; |
- break; |
- case '|': |
- if (nextChar != '|') |
- goto noMatch; |
- match = kLogicalOr; |
- advance = 2; |
- break; |
- default: |
-noMatch: |
- return 0; |
- } |
- SkSuppress suppress; |
- precedence = gPrecedence[match]; |
- if (fSuppressStack.top().fSuppress) { |
- if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) { |
- SkOp topOp = fOpStack.top(); |
- if (gPrecedence[topOp] <= precedence) |
- fOpStack.pop(); |
- goto goHome; |
- } |
- bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence; |
- if (changedPrecedence) |
- fSuppressStack.pop(); |
- if (precedence == kIfElsePrecedence) { |
- if (match == kIf) { |
- if (changedPrecedence) |
- fOpStack.pop(); |
- else |
- *fOpStack.push() = kIf; |
- } else { |
- if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) { |
- goto flipSuppress; |
- } |
- fOpStack.pop(); |
- } |
- } |
- if (changedPrecedence == false) |
- goto goHome; |
- } |
- while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) { |
- if (processOp() == false) |
- return false; |
- } |
- if (fSuppressStack.top().fOpStackDepth > fOpStack.count()) |
- fSuppressStack.pop(); |
- switch (match) { |
- case kParen: |
- case kArrayOp: |
- if (fOpStack.count() <= 1 || fOpStack.top() != match) { |
- fError = kMismatchedBrackets; |
- return -1; |
- } |
- if (match == kParen) |
- fOpStack.pop(); |
- else { |
- SkOpType indexType; |
- fTypeStack.pop(&indexType); |
- if (indexType != kInt && indexType != kScalar) { |
- fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually) |
- return -1; |
- } |
- SkOperand indexOperand; |
- fOperandStack.pop(&indexOperand); |
- int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) : |
- indexOperand.fS32; |
- SkOpType arrayType; |
- fTypeStack.pop(&arrayType); |
- if ((unsigned)arrayType != (unsigned)kArray) { |
- fError = kExpectedArray; |
- return -1; |
- } |
- SkOperand arrayOperand; |
- fOperandStack.pop(&arrayOperand); |
- SkTypedArray* array = arrayOperand.fArray; |
- SkOperand operand; |
- if (array->getIndex(index, &operand) == false) { |
- fError = kIndexOutOfRange; |
- return -1; |
- } |
- SkOpType resultType = array->getOpType(); |
- fTypeStack.push(resultType); |
- fOperandStack.push(operand); |
- } |
- break; |
- case kIf: { |
- SkScriptValue ifValue; |
- SkOpType ifType; |
- fTypeStack.pop(&ifType); |
- ifValue.fType = ToDisplayType(ifType); |
- fOperandStack.pop(&ifValue.fOperand); |
- if (convertTo(SkType_Int, &ifValue) == false) |
- return -1; |
- if (ifValue.fType != SkType_Int) { |
- fError = kExpectedIntForConditionOperator; |
- return -1; |
- } |
- suppress.fSuppress = ifValue.fOperand.fS32 == 0; |
- suppress.fOperator = kIf; |
- suppress.fOpStackDepth = fOpStack.count(); |
- suppress.fElse = false; |
- fSuppressStack.push(suppress); |
- // if left is true, do only up to colon |
- // if left is false, do only after colon |
- } break; |
- case kElse: |
-flipSuppress: |
- if (fSuppressStack.top().fElse == true) |
- fSuppressStack.pop(); |
- fSuppressStack.top().fElse = true; |
- fSuppressStack.top().fSuppress ^= true; |
- // flip last do / don't do consideration from last '?' |
- break; |
- case kLogicalAnd: |
- case kLogicalOr: { |
- if (fTypeStack.top() != kInt) { |
- fError = kExpectedBooleanExpression; |
- return -1; |
- } |
- int32_t topInt = fOperandStack.top().fS32; |
- if (fOpStack.top() != kLogicalAnd) |
- *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or' |
- if (match == kLogicalOr ? topInt != 0 : topInt == 0) { |
- suppress.fSuppress = true; |
- suppress.fOperator = match; |
- suppress.fOpStackDepth = fOpStack.count(); |
- fSuppressStack.push(suppress); |
- } else { |
- fTypeStack.pop(); |
- fOperandStack.pop(); |
- } |
- } break; |
- default: |
- SkASSERT(0); |
- } |
-goHome: |
- return advance; |
-} |
- |
-SkScriptEngine::Error SkScriptEngine::opError() { |
- int opCount = fOpStack.count(); |
- int operandCount = fOperandStack.count(); |
- if (opCount == 0) { |
- if (operandCount != 1) |
- return kExpectedOperator; |
- return kNoError; |
- } |
- SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp); |
- const SkOperatorAttributes* attributes = &gOpAttributes[op]; |
- if (attributes->fLeftType != kNoType && operandCount < 2) |
- return kExpectedValue; |
- if (attributes->fLeftType == kNoType && operandCount < 1) |
- return kExpectedValue; |
- return kNoError; |
-} |
- |
-bool SkScriptEngine::processOp() { |
- SkOp op; |
- fOpStack.pop(&op); |
- op = (SkOp) (op & ~kArtificialOp); |
- const SkOperatorAttributes* attributes = &gOpAttributes[op]; |
- SkOpType type2; |
- fTypeStack.pop(&type2); |
- SkOpType type1 = type2; |
- SkOperand operand2; |
- fOperandStack.pop(&operand2); |
- SkOperand operand1 = operand2; // !!! not really needed, suppresses warning |
- if (attributes->fLeftType != kNoType) { |
- fTypeStack.pop(&type1); |
- fOperandStack.pop(&operand1); |
- if (op == kFlipOps) { |
- SkTSwap(type1, type2); |
- SkTSwap(operand1, operand2); |
- fOpStack.pop(&op); |
- op = (SkOp) (op & ~kArtificialOp); |
- attributes = &gOpAttributes[op]; |
- } |
- if (type1 == kObject && (type1 & attributes->fLeftType) == 0) { |
- SkScriptValue val; |
- val.fType = ToDisplayType(type1); |
- val.fOperand = operand1; |
- bool success = handleUnbox(&val); |
- if (success == false) |
- return false; |
- type1 = ToOpType(val.fType); |
- operand1 = val.fOperand; |
- } |
- } |
- if (type2 == kObject && (type2 & attributes->fLeftType) == 0) { |
- SkScriptValue val; |
- val.fType = ToDisplayType(type2); |
- val.fOperand = operand2; |
- bool success = handleUnbox(&val); |
- if (success == false) |
- return false; |
- type2 = ToOpType(val.fType); |
- operand2 = val.fOperand; |
- } |
- if (attributes->fLeftType != kNoType) { |
- if (type1 != type2) { |
- if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) { |
- if (type1 == kInt || type1 == kScalar) { |
- convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float); |
- type1 = kString; |
- } |
- if (type2 == kInt || type2 == kScalar) { |
- convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float); |
- type2 = kString; |
- } |
- } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) { |
- if (type1 == kInt) { |
- operand1.fScalar = IntToScalar(operand1.fS32); |
- type1 = kScalar; |
- } |
- if (type2 == kInt) { |
- operand2.fScalar = IntToScalar(operand2.fS32); |
- type2 = kScalar; |
- } |
- } |
- } |
- if ((type1 & attributes->fLeftType) == 0 || type1 != type2) { |
- if (type1 == kString) { |
- const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar); |
- if (result == NULL) { |
- fError = kExpectedNumber; |
- return false; |
- } |
- type1 = kScalar; |
- } |
- if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) { |
- operand1.fS32 = SkScalarFloor(operand1.fScalar); |
- type1 = kInt; |
- } |
- } |
- } |
- if ((type2 & attributes->fRightType) == 0 || type1 != type2) { |
- if (type2 == kString) { |
- const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar); |
- if (result == NULL) { |
- fError = kExpectedNumber; |
- return false; |
- } |
- type2 = kScalar; |
- } |
- if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) { |
- operand2.fS32 = SkScalarFloor(operand2.fScalar); |
- type2 = kInt; |
- } |
- } |
- if (type2 == kScalar) |
- op = (SkOp) (op + 1); |
- else if (type2 == kString) |
- op = (SkOp) (op + 2); |
- switch(op) { |
- case kAddInt: |
- operand2.fS32 += operand1.fS32; |
- break; |
- case kAddScalar: |
- operand2.fScalar += operand1.fScalar; |
- break; |
- case kAddString: |
- if (fTrackString.find(operand1.fString) < 0) { |
- operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString)); |
- track(operand1.fString); |
- } |
- operand1.fString->append(*operand2.fString); |
- operand2 = operand1; |
- break; |
- case kBitAnd: |
- operand2.fS32 &= operand1.fS32; |
- break; |
- case kBitNot: |
- operand2.fS32 = ~operand2.fS32; |
- break; |
- case kBitOr: |
- operand2.fS32 |= operand1.fS32; |
- break; |
- case kDivideInt: |
- if (operand2.fS32 == 0) { |
- operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32; |
- break; |
- } else { |
- int32_t original = operand2.fS32; |
- operand2.fS32 = operand1.fS32 / operand2.fS32; |
- if (original * operand2.fS32 == operand1.fS32) |
- break; // integer divide was good enough |
- operand2.fS32 = original; |
- type2 = kScalar; |
- } |
- case kDivideScalar: |
- if (operand2.fScalar == 0) |
- operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax; |
- else |
- operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar); |
- break; |
- case kEqualInt: |
- operand2.fS32 = operand1.fS32 == operand2.fS32; |
- break; |
- case kEqualScalar: |
- operand2.fS32 = operand1.fScalar == operand2.fScalar; |
- type2 = kInt; |
- break; |
- case kEqualString: |
- operand2.fS32 = *operand1.fString == *operand2.fString; |
- type2 = kInt; |
- break; |
- case kGreaterEqualInt: |
- operand2.fS32 = operand1.fS32 >= operand2.fS32; |
- break; |
- case kGreaterEqualScalar: |
- operand2.fS32 = operand1.fScalar >= operand2.fScalar; |
- type2 = kInt; |
- break; |
- case kGreaterEqualString: |
- operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0; |
- type2 = kInt; |
- break; |
- case kLogicalAnd: |
- operand2.fS32 = !! operand2.fS32; // really, ToBool |
- break; |
- case kLogicalNot: |
- operand2.fS32 = ! operand2.fS32; |
- break; |
- case kLogicalOr: |
- SkASSERT(0); // should have already been processed |
- break; |
- case kMinusInt: |
- operand2.fS32 = -operand2.fS32; |
- break; |
- case kMinusScalar: |
- operand2.fScalar = -operand2.fScalar; |
- break; |
- case kModuloInt: |
- operand2.fS32 = operand1.fS32 % operand2.fS32; |
- break; |
- case kModuloScalar: |
- operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar); |
- break; |
- case kMultiplyInt: |
- operand2.fS32 *= operand1.fS32; |
- break; |
- case kMultiplyScalar: |
- operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar); |
- break; |
- case kShiftLeft: |
- operand2.fS32 = operand1.fS32 << operand2.fS32; |
- break; |
- case kShiftRight: |
- operand2.fS32 = operand1.fS32 >> operand2.fS32; |
- break; |
- case kSubtractInt: |
- operand2.fS32 = operand1.fS32 - operand2.fS32; |
- break; |
- case kSubtractScalar: |
- operand2.fScalar = operand1.fScalar - operand2.fScalar; |
- break; |
- case kXor: |
- operand2.fS32 ^= operand1.fS32; |
- break; |
- default: |
- SkASSERT(0); |
- } |
- fTypeStack.push(type2); |
- fOperandStack.push(operand2); |
- return true; |
-} |
- |
-void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) { |
- UserCallBack callBack; |
- callBack.fPropertyCallBack = prop; |
- commonCallBack(kProperty, callBack, userStorage); |
-} |
- |
-void SkScriptEngine::track(SkTypedArray* array) { |
- SkASSERT(fTrackArray.find(array) < 0); |
- *(fTrackArray.end() - 1) = array; |
- fTrackArray.appendClear(); |
-} |
- |
-void SkScriptEngine::track(SkString* string) { |
- SkASSERT(fTrackString.find(string) < 0); |
- *(fTrackString.end() - 1) = string; |
- fTrackString.appendClear(); |
-} |
- |
-void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) { |
- UserCallBack callBack; |
- callBack.fUnboxCallBack = func; |
- commonCallBack(kUnbox, callBack, userStorage); |
-} |
- |
-bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) { |
- SkASSERT(value); |
- if (SkDisplayType::IsEnum(NULL /* fMaker */, toType)) |
- toType = SkType_Int; |
- if (toType == SkType_Point || toType == SkType_3D_Point) |
- toType = SkType_Float; |
- if (toType == SkType_Drawable) |
- toType = SkType_Displayable; |
- SkDisplayTypes type = value->fType; |
- if (type == toType) |
- return true; |
- SkOperand& operand = value->fOperand; |
- bool success = true; |
- switch (toType) { |
- case SkType_Int: |
- if (type == SkType_Boolean) |
- break; |
- if (type == SkType_Float) |
- operand.fS32 = SkScalarFloor(operand.fScalar); |
- else { |
- if (type != SkType_String) { |
- success = false; |
- break; // error |
- } |
- success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL; |
- } |
- break; |
- case SkType_Float: |
- if (type == SkType_Int) { |
- if ((uint32_t)operand.fS32 == SK_NaN32) |
- operand.fScalar = SK_ScalarNaN; |
- else if (SkAbs32(operand.fS32) == SK_MaxS32) |
- operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax; |
- else |
- operand.fScalar = SkIntToScalar(operand.fS32); |
- } else { |
- if (type != SkType_String) { |
- success = false; |
- break; // error |
- } |
- success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL; |
- } |
- break; |
- case SkType_String: { |
- SkString* strPtr = new SkString(); |
- SkASSERT(engine); |
- engine->track(strPtr); |
- if (type == SkType_Int) |
- strPtr->appendS32(operand.fS32); |
- else if (type == SkType_Displayable) |
- SkASSERT(0); // must call through instance version instead of static version |
- else { |
- if (type != SkType_Float) { |
- success = false; |
- break; |
- } |
- strPtr->appendScalar(operand.fScalar); |
- } |
- operand.fString = strPtr; |
- } break; |
- case SkType_Array: { |
- SkTypedArray* array = new SkTypedArray(type); |
- *array->append() = operand; |
- engine->track(array); |
- operand.fArray = array; |
- } break; |
- default: |
- SkASSERT(0); |
- } |
- value->fType = toType; |
- if (success == false) |
- engine->fError = kTypeConversionFailed; |
- return success; |
-} |
- |
-SkScalar SkScriptEngine::IntToScalar(int32_t s32) { |
- SkScalar scalar; |
- if ((uint32_t)s32 == SK_NaN32) |
- scalar = SK_ScalarNaN; |
- else if (SkAbs32(s32) == SK_MaxS32) |
- scalar = SkSign32(s32) * SK_ScalarMax; |
- else |
- scalar = SkIntToScalar(s32); |
- return scalar; |
-} |
- |
-SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) { |
- int val = type; |
- switch (val) { |
- case kNoType: |
- return SkType_Unknown; |
- case kInt: |
- return SkType_Int; |
- case kScalar: |
- return SkType_Float; |
- case kString: |
- return SkType_String; |
- case kArray: |
- return SkType_Array; |
- case kObject: |
- return SkType_Displayable; |
-// case kStruct: |
-// return SkType_Structure; |
- default: |
- SkASSERT(0); |
- return SkType_Unknown; |
- } |
-} |
- |
-SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) { |
- if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type)) |
- return (SkOpType) kObject; |
- if (SkDisplayType::IsEnum(NULL /* fMaker */, type)) |
- return kInt; |
- switch (type) { |
- case SkType_ARGB: |
- case SkType_MSec: |
- case SkType_Int: |
- return kInt; |
- case SkType_Float: |
- case SkType_Point: |
- case SkType_3D_Point: |
- return kScalar; |
- case SkType_Base64: |
- case SkType_DynamicString: |
- case SkType_String: |
- return kString; |
- case SkType_Array: |
- return (SkOpType) kArray; |
- case SkType_Unknown: |
- return kNoType; |
- default: |
- SkASSERT(0); |
- return kNoType; |
- } |
-} |
- |
-bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) { |
- switch (value.fType) { |
- case kInt: |
- string->reset(); |
- string->appendS32(value.fOperand.fS32); |
- break; |
- case kScalar: |
- string->reset(); |
- string->appendScalar(value.fOperand.fScalar); |
- break; |
- case kString: |
- string->set(*value.fOperand.fString); |
- break; |
- default: |
- SkASSERT(0); |
- return false; |
- } |
- return true; // no error |
-} |
- |
-#ifdef SK_SUPPORT_UNITTEST |
- |
-#ifdef SK_CAN_USE_FLOAT |
- #include "SkFloatingPoint.h" |
-#endif |
- |
-#define DEF_SCALAR_ANSWER 0 |
-#define DEF_STRING_ANSWER NULL |
- |
-#define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER } |
-#ifdef SK_SCALAR_IS_FLOAT |
- #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER } |
- #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER } |
-#else |
- #ifdef SK_CAN_USE_FLOAT |
- #define testScalar(expression) { #expression, SkType_Float, 0, (int) ((expression) * 65536.0f), DEF_STRING_ANSWER } |
- #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, (int) (sk_float_mod(exp1, exp2) * 65536.0f), DEF_STRING_ANSWER } |
- #endif |
-#endif |
-#define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER } |
-#define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER } |
- |
-#if !defined(SK_BUILD_FOR_BREW) |
-static const SkScriptNAnswer scriptTests[] = { |
- testInt(1>1/2), |
- testInt((6+7)*8), |
- testInt(0&&1?2:3), |
- testInt(3*(4+5)), |
-#ifdef SK_CAN_USE_FLOAT |
- testScalar(1.0+2.0), |
- testScalar(1.0+5), |
- testScalar(3.0-1.0), |
- testScalar(6-1.0), |
- testScalar(- -5.5- -1.5), |
- 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\"", SkType_String, 0, 0, "123456" }, |
- { "123+\"456\"", SkType_String, 0, 0, "123456" }, |
- { "'123'+456", SkType_String, 0, 0, "123456" }, |
- { "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, |
- { "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, |
- { "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, |
- { "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, |
- { "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, |
- { "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }, |
- 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(1?(2?3:4):5), |
- testInt(0?(2?3:4):5), |
- testInt(1?(0?3:4):5), |
- testInt(0?(0?3:4):5), |
- testInt(1?2?3:4:5), |
- testInt(0?2?3:4:5), |
- testInt(1?0?3:4:5), |
- testInt(0?0?3:4:5), |
- |
- testInt(1?2:(3?4:5)), |
- testInt(0?2:(3?4:5)), |
- testInt(1?0:(3?4:5)), |
- testInt(0?0:(3?4:5)), |
- testInt(1?2:3?4:5), |
- testInt(0?2:3?4:5), |
- testInt(1?0:3?4:5), |
- testInt(0?0:3?4:5) |
-#ifdef SK_CAN_USE_FLOAT |
- , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER } |
-#endif |
-}; |
-#endif // build for brew |
- |
-#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests) |
- |
-void SkScriptEngine::UnitTest() { |
-#if !defined(SK_BUILD_FOR_BREW) |
- for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) { |
- SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType)); |
- SkScriptValue value; |
- const char* script = scriptTests[index].fScript; |
- SkASSERT(engine.evaluateScript(&script, &value) == true); |
- SkASSERT(value.fType == scriptTests[index].fType); |
- SkScalar error; |
- switch (value.fType) { |
- case SkType_Int: |
- SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer); |
- break; |
- case SkType_Float: |
- error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer); |
- SkASSERT(error < SK_Scalar1 / 10000); |
- break; |
- case SkType_String: |
- SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0); |
- break; |
- default: |
- SkASSERT(0); |
- } |
- } |
-#endif |
-} |
-#endif |
- |