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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial