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