blob: bf55cb574a10daf4f1bb37766b91e0128fb6154e [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkScript.h"
11#include "SkMath.h"
12#include "SkParse.h"
13#include "SkString.h"
14#include "SkTypedArray.h"
15
16/* things to do
17 ? re-enable support for struct literals (e.g., for initializing points or rects)
18 {x:1, y:2}
rmistry@google.comd6176b02012-08-23 18:14:13 +000019 ? use standard XML / script notation like document.getElementById("canvas");
reed@android.com8a1c16f2008-12-17 15:59:43 +000020 finish support for typed arrays
21 ? allow indexing arrays by string
22 this could map to the 'name' attribute of a given child of an array
23 ? allow multiple types in the array
24 remove SkDisplayType.h // from SkOperand.h
25 merge type and operand arrays into scriptvalue array
26*/
27
28#ifdef SK_DEBUG
29static const char* errorStrings[] = {
30 "array index of out bounds", // kArrayIndexOutOfBounds
31 "could not find reference id", // kCouldNotFindReferencedID
32 "dot operator expects object", // kDotOperatorExpectsObject
33 "error in array index", // kErrorInArrrayIndex
34 "error in function parameters", // kErrorInFunctionParameters
35 "expected array", // kExpectedArray
36 "expected boolean expression", // kExpectedBooleanExpression
37 "expected field name", // kExpectedFieldName
38 "expected hex", // kExpectedHex
39 "expected int for condition operator", // kExpectedIntForConditionOperator
40 "expected number", // kExpectedNumber
41 "expected number for array index", // kExpectedNumberForArrayIndex
42 "expected operator", // kExpectedOperator
43 "expected token", // kExpectedToken
44 "expected token before dot operator", // kExpectedTokenBeforeDotOperator
45 "expected value", // kExpectedValue
46 "handle member failed", // kHandleMemberFailed
47 "handle member function failed", // kHandleMemberFunctionFailed
48 "handle unbox failed", // kHandleUnboxFailed
49 "index out of range", // kIndexOutOfRange
50 "mismatched array brace", // kMismatchedArrayBrace
51 "mismatched brackets", // kMismatchedBrackets
52 "no function handler found", // kNoFunctionHandlerFound
53 "premature end", // kPrematureEnd
54 "too many parameters", // kTooManyParameters
55 "type conversion failed", // kTypeConversionFailed
56 "unterminated string" // kUnterminatedString
57};
58#endif
59
60const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = {
61 { kNoType, kNoType, kNoBias }, // kUnassigned,
62 { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd
63 // kAddInt = kAdd,
64 { kNoType, kNoType, kNoBias }, // kAddScalar,
65 { kNoType, kNoType, kNoBias }, // kAddString,
66 { kNoType, kNoType, kNoBias }, // kArrayOp,
67 { kInt, kInt, kNoBias }, // kBitAnd
68 { kNoType, kInt, kNoBias }, // kBitNot
69 { kInt, kInt, kNoBias }, // kBitOr
70 { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide
71 // kDivideInt = kDivide
72 { kNoType, kNoType, kNoBias }, // kDivideScalar
73 { kNoType, kNoType, kNoBias }, // kElse
74 { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual
75 // kEqualInt = kEqual
76 { kNoType, kNoType, kNoBias }, // kEqualScalar
77 { kNoType, kNoType, kNoBias }, // kEqualString
78 { kInt, kNoType, kNoBias }, // kFlipOps
79 { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual
80 // kGreaterEqualInt = kGreaterEqual
81 { kNoType, kNoType, kNoBias }, // kGreaterEqualScalar
82 { kNoType, kNoType, kNoBias }, // kGreaterEqualString
83 { kNoType, kNoType, kNoBias }, // kIf
84 { kNoType, kInt, kNoBias }, // kLogicalAnd (really, ToBool)
85 { kNoType, kInt, kNoBias }, // kLogicalNot
86 { kInt, kInt, kNoBias }, // kLogicalOr
87 { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus
88 // kMinusInt = kMinus
89 { kNoType, kNoType, kNoBias }, // kMinusScalar
90 { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo
91 // kModuloInt = kModulo
92 { kNoType, kNoType, kNoBias }, // kModuloScalar
93 { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply
94 // kMultiplyInt = kMultiply
95 { kNoType, kNoType, kNoBias }, // kMultiplyScalar
96 { kNoType, kNoType, kNoBias }, // kParen
97 { kInt, kInt, kNoBias }, // kShiftLeft
98 { kInt, kInt, kNoBias }, // kShiftRight
99 { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract
100 // kSubtractInt = kSubtract
101 { kNoType, kNoType, kNoBias }, // kSubtractScalar
102 { kInt, kInt, kNoBias } // kXor
103};
104
105// Note that the real precedence for () [] is '2'
106// but here, precedence means 'while an equal or smaller precedence than the current operator
107// is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply
108// is preformed, since the add precedence is not smaller than multiply.
109// But, (3*4 does not process the '(', since brackets are greater than all other precedences
110#define kBracketPrecedence 16
111#define kIfElsePrecedence 15
112
113const signed char SkScriptEngine::gPrecedence[] = {
114 -1, // kUnassigned,
115 6, // kAdd,
116 // kAddInt = kAdd,
117 6, // kAddScalar,
118 6, // kAddString, // string concat
119 kBracketPrecedence, // kArrayOp,
120 10, // kBitAnd,
121 4, // kBitNot,
122 12, // kBitOr,
123 5, // kDivide,
124 // kDivideInt = kDivide,
125 5, // kDivideScalar,
126 kIfElsePrecedence, // kElse,
127 9, // kEqual,
128 // kEqualInt = kEqual,
129 9, // kEqualScalar,
130 9, // kEqualString,
131 -1, // kFlipOps,
132 8, // kGreaterEqual,
133 // kGreaterEqualInt = kGreaterEqual,
134 8, // kGreaterEqualScalar,
135 8, // kGreaterEqualString,
136 kIfElsePrecedence, // kIf,
137 13, // kLogicalAnd,
138 4, // kLogicalNot,
139 14, // kLogicalOr,
140 4, // kMinus,
141 // kMinusInt = kMinus,
142 4, // kMinusScalar,
143 5, // kModulo,
144 // kModuloInt = kModulo,
145 5, // kModuloScalar,
146 5, // kMultiply,
147 // kMultiplyInt = kMultiply,
148 5, // kMultiplyScalar,
149 kBracketPrecedence, // kParen,
150 7, // kShiftLeft,
151 7, // kShiftRight, // signed
152 6, // kSubtract,
153 // kSubtractInt = kSubtract,
154 6, // kSubtractScalar,
155 11, // kXor
156};
157
158static inline bool is_between(int c, int min, int max)
159{
160 return (unsigned)(c - min) <= (unsigned)(max - min);
161}
162
163static inline bool is_ws(int c)
164{
165 return is_between(c, 1, 32);
166}
167
168static int token_length(const char* start) {
169 char ch = start[0];
170 if (! is_between(ch, 'a' , 'z') && ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
171 return -1;
172 int length = 0;
173 do
174 ch = start[++length];
175 while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
176 ch == '_' || ch == '$');
177 return length;
178}
179
180SkScriptEngine::SkScriptEngine(SkOpType returnType) :
181 fTokenLength(0), fReturnType(returnType), fError(kNoError)
182{
183 SkSuppress noInitialSuppress;
184 noInitialSuppress.fOperator = kUnassigned;
185 noInitialSuppress.fOpStackDepth = 0;
186 noInitialSuppress.fSuppress = false;
tomhudson@google.coma7ed3cc2011-07-29 13:20:06 +0000187 noInitialSuppress.fElse = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 fSuppressStack.push(noInitialSuppress);
189 *fOpStack.push() = kParen;
190 fTrackArray.appendClear();
191 fTrackString.appendClear();
192}
193
194SkScriptEngine::~SkScriptEngine() {
195 for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
196 delete *stringPtr;
197 for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
198 delete *arrayPtr;
199}
200
201int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) {
202 SkOp op = kUnassigned;
203 bool reverseOperands = false;
204 bool negateResult = false;
205 int advance = 1;
206 switch (ch) {
207 case '+':
208 // !!! ignoring unary plus as implemented here has the side effect of
209 // suppressing errors like +"hi"
210 if (lastPush == false) // unary plus, don't push an operator
211 goto returnAdv;
212 op = kAdd;
213 break;
214 case '-':
215 op = lastPush ? kSubtract : kMinus;
216 break;
217 case '*':
218 op = kMultiply;
219 break;
220 case '/':
221 op = kDivide;
222 break;
223 case '>':
224 if (nextChar == '>') {
225 op = kShiftRight;
226 goto twoChar;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000227 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 op = kGreaterEqual;
229 if (nextChar == '=')
230 goto twoChar;
231 reverseOperands = negateResult = true;
232 break;
233 case '<':
234 if (nextChar == '<') {
235 op = kShiftLeft;
236 goto twoChar;
237 }
238 op = kGreaterEqual;
239 reverseOperands = nextChar == '=';
240 negateResult = ! reverseOperands;
241 advance += reverseOperands;
242 break;
243 case '=':
244 if (nextChar == '=') {
245 op = kEqual;
246 goto twoChar;
247 }
248 break;
249 case '!':
250 if (nextChar == '=') {
251 op = kEqual;
252 negateResult = true;
253twoChar:
254 advance++;
255 break;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000256 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 op = kLogicalNot;
258 break;
259 case '?':
260 op = kIf;
261 break;
262 case ':':
263 op = kElse;
264 break;
265 case '^':
266 op = kXor;
267 break;
268 case '(':
269 *fOpStack.push() = kParen; // push even if eval is suppressed
270 goto returnAdv;
271 case '&':
272 SkASSERT(nextChar != '&');
273 op = kBitAnd;
274 break;
275 case '|':
276 SkASSERT(nextChar != '|');
277 op = kBitOr;
278 break;
279 case '%':
280 op = kModulo;
281 break;
282 case '~':
283 op = kBitNot;
284 break;
285 }
286 if (op == kUnassigned)
287 return 0;
288 if (fSuppressStack.top().fSuppress == false) {
289 signed char precedence = gPrecedence[op];
290 do {
291 int idx = 0;
292 SkOp compare;
293 do {
294 compare = fOpStack.index(idx);
295 if ((compare & kArtificialOp) == 0)
296 break;
297 idx++;
298 } while (true);
299 signed char topPrecedence = gPrecedence[compare];
300 SkASSERT(topPrecedence != -1);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000301 if (topPrecedence > precedence || (topPrecedence == precedence &&
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000302 gOpAttributes[op].fLeftType == kNoType)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 break;
304 }
305 if (processOp() == false)
306 return 0; // error
307 } while (true);
308 if (negateResult)
309 *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp);
310 fOpStack.push(op);
311 if (reverseOperands)
312 *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp);
313 }
314returnAdv:
315 return advance;
316}
317
318void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) {
319 UserCallBack callBack;
320 callBack.fBoxCallBack = func;
321 commonCallBack(kBox, callBack, userStorage);
322}
323
324void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) {
325 callBack.fCallBackType = type;
326 callBack.fUserStorage = userStorage;
327 *fUserCallBacks.prepend() = callBack;
328}
329
rmistry@google.comd6176b02012-08-23 18:14:13 +0000330bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 const SkFunctionParamType* paramTypes, int paramCount) {
332 if (params.count() > paramCount) {
333 fError = kTooManyParameters;
334 return false; // too many parameters passed
335 }
336 for (int index = 0; index < params.count(); index++) {
337 if (convertTo((SkDisplayTypes) paramTypes[index], &params[index]) == false)
338 return false;
339 }
340 return true;
341}
342
343bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) {
344 SkDisplayTypes type = value->fType;
345 if (type == toType)
346 return true;
347 if (ToOpType(type) == kObject) {
348#if 0 // !!! I want object->string to get string from displaystringtype, not id
349 if (ToOpType(toType) == kString) {
350 bool success = handleObjectToString(value->fOperand.fObject);
351 if (success == false)
352 return false;
353 SkOpType type;
354 fTypeStack.pop(&type);
355 value->fType = ToDisplayType(type);
356 fOperandStack.pop(&value->fOperand);
357 return true;
358 }
359#endif
360 if (handleUnbox(value) == false) {
361 fError = kHandleUnboxFailed;
362 return false;
363 }
364 return convertTo(toType, value);
365 }
366 return ConvertTo(this, toType, value);
367}
368
rmistry@google.comd6176b02012-08-23 18:14:13 +0000369bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 size_t fieldLength = token_length(++script); // skip dot
371 if (fieldLength == 0) {
372 fError = kExpectedFieldName;
373 return false;
374 }
375 const char* field = script;
376 script += fieldLength;
377 bool success = handleProperty(suppressed);
378 if (success == false) {
379 fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins
380 return false;
381 }
382 return evaluateDotParam(script, suppressed, field, fieldLength);
383}
384
rmistry@google.comd6176b02012-08-23 18:14:13 +0000385bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed,
386 const char* field, size_t fieldLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 void* object;
388 if (suppressed)
389 object = NULL;
390 else {
391 if (fTypeStack.top() != kObject) {
392 fError = kDotOperatorExpectsObject;
393 return false;
394 }
395 object = fOperandStack.top().fObject;
396 fTypeStack.pop();
397 fOperandStack.pop();
398 }
399 char ch; // see if it is a simple member or a function
rmistry@google.comd6176b02012-08-23 18:14:13 +0000400 while (is_ws(ch = script[0]))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 script++;
402 bool success = true;
403 if (ch != '(') {
404 if (suppressed == false) {
405 if ((success = handleMember(field, fieldLength, object)) == false)
406 fError = kHandleMemberFailed;
407 }
408 } else {
409 SkTDArray<SkScriptValue> params;
410 *fBraceStack.push() = kFunctionBrace;
411 success = functionParams(&script, params);
412 if (success && suppressed == false &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000413 (success = handleMemberFunction(field, fieldLength, object, params)) == false)
414 fError = kHandleMemberFunctionFailed;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000416 return success;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417}
418
419bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) {
420#ifdef SK_DEBUG
421 const char** original = scriptPtr;
422#endif
423 bool success;
424 const char* inner;
425 if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
426 *scriptPtr += sizeof("#script:") - 1;
427 if (fReturnType == kNoType || fReturnType == kString) {
428 success = innerScript(scriptPtr, value);
429 if (success == false)
430 goto end;
431 inner = value->fOperand.fString->c_str();
432 scriptPtr = &inner;
433 }
434 }
435 {
436 success = innerScript(scriptPtr, value);
437 if (success == false)
438 goto end;
439 const char* script = *scriptPtr;
440 char ch;
441 while (is_ws(ch = script[0]))
442 script++;
443 if (ch != '\0') {
444 // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
445 fError = kPrematureEnd;
446 success = false;
447 }
448 }
449end:
450#ifdef SK_DEBUG
451 if (success == false) {
452 SkDebugf("script failed: %s", *original);
453 if (fError)
454 SkDebugf(" %s", errorStrings[fError - 1]);
455 SkDebugf("\n");
456 }
457#endif
458 return success;
459}
460
461void SkScriptEngine::forget(SkTypedArray* array) {
462 if (array->getType() == SkType_String) {
463 for (int index = 0; index < array->count(); index++) {
464 SkString* string = (*array)[index].fString;
465 int found = fTrackString.find(string);
466 if (found >= 0)
467 fTrackString.remove(found);
468 }
469 return;
470 }
471 if (array->getType() == SkType_Array) {
472 for (int index = 0; index < array->count(); index++) {
473 SkTypedArray* child = (*array)[index].fArray;
474 forget(child); // forgets children of child
475 int found = fTrackArray.find(child);
476 if (found >= 0)
477 fTrackArray.remove(found);
478 }
479 }
480}
481
482void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) {
483 UserCallBack callBack;
484 callBack.fFunctionCallBack = func;
485 commonCallBack(kFunction, callBack, userStorage);
486}
487
488bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) {
489 (*scriptPtr)++; // skip open paren
490 *fOpStack.push() = kParen;
491 *fBraceStack.push() = kFunctionBrace;
492 SkBool suppressed = fSuppressStack.top().fSuppress;
493 do {
494 SkScriptValue value;
495 bool success = innerScript(scriptPtr, suppressed ? NULL : &value);
496 if (success == false) {
497 fError = kErrorInFunctionParameters;
498 return false;
499 }
500 if (suppressed)
501 continue;
502 *params.append() = value;
503 } while ((*scriptPtr)[-1] == ',');
504 fBraceStack.pop();
505 fOpStack.pop(); // pop paren
506 (*scriptPtr)++; // advance beyond close paren
507 return true;
508}
509
510#ifdef SK_DEBUG
511bool SkScriptEngine::getErrorString(SkString* str) const {
512 if (fError)
513 str->set(errorStrings[fError - 1]);
514 return fError != 0;
515}
516#endif
517
518bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) {
519 const char* script = *scriptPtr;
520 char ch;
521 bool lastPush = false;
522 bool success = true;
523 int opBalance = fOpStack.count();
524 int baseBrace = fBraceStack.count();
525 int suppressBalance = fSuppressStack.count();
526 while ((ch = script[0]) != '\0') {
527 if (is_ws(ch)) {
528 script++;
529 continue;
530 }
531 SkBool suppressed = fSuppressStack.top().fSuppress;
532 SkOperand operand;
533 const char* dotCheck;
534 if (fBraceStack.count() > baseBrace) {
535#if 0 // disable support for struct brace
536 if (ch == ':') {
537 SkASSERT(fTokenLength > 0);
538 SkASSERT(fBraceStack.top() == kStructBrace);
539 ++script;
540 SkASSERT(fDisplayable);
541 SkString token(fToken, fTokenLength);
542 fTokenLength = 0;
543 const char* tokenName = token.c_str();
544 const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING;
545 if (suppressed == false) {
546 SkDisplayTypes type = fInfo->getType();
547 tokenInfo = SkDisplayType::GetMember(type, &tokenName);
548 SkASSERT(tokenInfo);
549 }
550 SkScriptValue tokenValue;
551 success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace
552 SkASSERT(success);
553 if (suppressed == false) {
554 if (tokenValue.fType == SkType_Displayable) {
555 SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType()));
556 fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable);
557 } else {
558 if (tokenValue.fType != tokenInfo->getType()) {
559 if (convertTo(tokenInfo->getType(), &tokenValue) == false)
560 return false;
561 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000562 tokenInfo->writeValue(fDisplayable, NULL, 0, 0,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset),
564 tokenInfo->getType(), tokenValue);
565 }
566 }
567 lastPush = false;
568 continue;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000569 } else
570#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 if (fBraceStack.top() == kArrayBrace) {
572 SkScriptValue tokenValue;
573 success = innerScript(&script, &tokenValue); // terminate and return on comma, close brace
574 if (success == false) {
575 fError = kErrorInArrrayIndex;
576 return false;
577 }
578 if (suppressed == false) {
579#if 0 // no support for structures for now
580 if (tokenValue.fType == SkType_Structure) {
581 fArrayOffset += (int) fInfo->getSize(fDisplayable);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000582 } else
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583#endif
584 {
585 SkDisplayTypes type = ToDisplayType(fReturnType);
586 if (fReturnType == kNoType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000587 // !!! short sighted; in the future, allow each returned array component to carry
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 // its own type, and let caller do any needed conversions
589 if (value->fOperand.fArray->count() == 0)
590 value->fOperand.fArray->setType(type = tokenValue.fType);
591 else
592 type = value->fOperand.fArray->getType();
593 }
594 if (tokenValue.fType != type) {
595 if (convertTo(type, &tokenValue) == false)
596 return false;
597 }
598 *value->fOperand.fArray->append() = tokenValue.fOperand;
599 }
600 }
601 lastPush = false;
602 continue;
603 } else {
604 if (token_length(script) == 0) {
605 fError = kExpectedToken;
606 return false;
607 }
608 }
609 }
610 if (lastPush != false && fTokenLength > 0) {
611 if (ch == '(') {
612 *fBraceStack.push() = kFunctionBrace;
613 if (handleFunction(&script, SkToBool(suppressed)) == false)
614 return false;
615 lastPush = true;
616 continue;
617 } else if (ch == '[') {
618 if (handleProperty(SkToBool(suppressed)) == false)
619 return false; // note: never triggered by standard animator plugins
620 if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
621 return false;
622 lastPush = true;
623 continue;
624 } else if (ch != '.') {
625 if (handleProperty(SkToBool(suppressed)) == false)
626 return false; // note: never triggered by standard animator plugins
627 lastPush = true;
628 continue;
629 }
630 }
631 if (ch == '0' && (script[1] & ~0x20) == 'X') {
632 if (lastPush != false) {
633 fError = kExpectedOperator;
634 return false;
635 }
636 script += 2;
637 script = SkParse::FindHex(script, (uint32_t*)&operand.fS32);
638 if (script == NULL) {
639 fError = kExpectedHex;
640 return false;
641 }
642 goto intCommon;
643 }
644 if (lastPush == false && ch == '.')
645 goto scalarCommon;
646 if (ch >= '0' && ch <= '9') {
647 if (lastPush != false) {
648 fError = kExpectedOperator;
649 return false;
650 }
651 dotCheck = SkParse::FindS32(script, &operand.fS32);
652 if (dotCheck[0] != '.') {
653 script = dotCheck;
654intCommon:
655 if (suppressed == false)
656 *fTypeStack.push() = kInt;
657 } else {
658scalarCommon:
659 script = SkParse::FindScalar(script, &operand.fScalar);
660 if (suppressed == false)
661 *fTypeStack.push() = kScalar;
662 }
663 if (suppressed == false)
664 fOperandStack.push(operand);
665 lastPush = true;
666 continue;
667 }
668 int length = token_length(script);
669 if (length > 0) {
670 if (lastPush != false) {
671 fError = kExpectedOperator;
672 return false;
673 }
674 fToken = script;
675 fTokenLength = length;
676 script += length;
677 lastPush = true;
678 continue;
679 }
680 char startQuote = ch;
681 if (startQuote == '\'' || startQuote == '\"') {
682 if (lastPush != false) {
683 fError = kExpectedOperator;
684 return false;
685 }
686 operand.fString = new SkString();
687 track(operand.fString);
688 ++script;
689
690 // <mrr> this is a lot of calls to append() one char at at time
691 // how hard to preflight script so we know how much to grow fString by?
692 do {
693 if (script[0] == '\\')
694 ++script;
695 operand.fString->append(script, 1);
696 ++script;
697 if (script[0] == '\0') {
698 fError = kUnterminatedString;
699 return false;
700 }
701 } while (script[0] != startQuote);
702 ++script;
703 if (suppressed == false) {
704 *fTypeStack.push() = kString;
705 fOperandStack.push(operand);
706 }
707 lastPush = true;
708 continue;
709 }
710 ;
711 if (ch == '.') {
712 if (fTokenLength == 0) {
713 SkScriptValue scriptValue;
714 SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
715 int tokenLength = token_length(++script);
716 const char* token = script;
717 script += tokenLength;
718 if (suppressed == false) {
719 if (fTypeStack.count() == 0) {
720 fError = kExpectedTokenBeforeDotOperator;
721 return false;
722 }
723 SkOpType topType;
724 fTypeStack.pop(&topType);
725 fOperandStack.pop(&scriptValue.fOperand);
726 scriptValue.fType = ToDisplayType(topType);
727 handleBox(&scriptValue);
728 }
729 success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength);
730 if (success == false)
731 return false;
732 lastPush = true;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000733 continue;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 }
735 // get next token, and evaluate immediately
736 success = evaluateDot(script, SkToBool(suppressed));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000737 if (success == false)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 return false;
739 lastPush = true;
740 continue;
741 }
742 if (ch == '[') {
743 if (lastPush == false) {
744 script++;
745 *fBraceStack.push() = kArrayBrace;
746 if (suppressed)
747 continue;
748 operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType));
749 track(value->fOperand.fArray);
750 *fTypeStack.push() = (SkOpType) kArray;
751 fOperandStack.push(operand);
752 continue;
753 }
754 if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
755 return false;
756 lastPush = true;
757 continue;
758 }
759#if 0 // structs not supported for now
760 if (ch == '{') {
761 if (lastPush == false) {
762 script++;
763 *fBraceStack.push() = kStructBrace;
764 if (suppressed)
765 continue;
766 operand.fS32 = 0;
767 *fTypeStack.push() = (SkOpType) kStruct;
768 fOperandStack.push(operand);
769 continue;
770 }
771 SkASSERT(0); // braces in other contexts aren't supported yet
772 }
773#endif
774 if (ch == ')' && fBraceStack.count() > 0) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000775 SkBraceStyle braceStyle = fBraceStack.top();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 if (braceStyle == kFunctionBrace) {
777 fBraceStack.pop();
778 break;
779 }
780 }
781 if (ch == ',' || ch == ']') {
782 if (ch != ',') {
783 SkBraceStyle match;
784 fBraceStack.pop(&match);
785 if (match != kArrayBrace) {
786 fError = kMismatchedArrayBrace;
787 return false;
788 }
789 }
790 script++;
791 // !!! see if brace or bracket is correct closer
792 break;
793 }
794 char nextChar = script[1];
795 int advance = logicalOp(ch, nextChar);
796 if (advance < 0) // error
797 return false;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000798 if (advance == 0)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 advance = arithmeticOp(ch, nextChar, lastPush);
800 if (advance == 0) // unknown token
801 return false;
802 if (advance > 0)
803 script += advance;
804 lastPush = ch == ']' || ch == ')';
805 }
806 bool suppressed = SkToBool(fSuppressStack.top().fSuppress);
807 if (fTokenLength > 0) {
808 success = handleProperty(suppressed);
809 if (success == false)
810 return false; // note: never triggered by standard animator plugins
811 }
812 while (fOpStack.count() > opBalance) { // leave open paren
813 if ((fError = opError()) != kNoError)
814 return false;
815 if (processOp() == false)
816 return false;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000817 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType;
819 if (suppressed == false && topType != fReturnType &&
820 topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value
821 SkString* string = fOperandStack.top().fString;
822 fToken = string->c_str();
823 fTokenLength = string->size();
824 fOperandStack.pop();
825 fTypeStack.pop();
826 success = handleProperty(SkToBool(fSuppressStack.top().fSuppress));
827 if (success == false) { // if it couldn't convert, return string (error?)
828 SkOperand operand;
829 operand.fS32 = 0;
830 *fTypeStack.push() = kString;
831 operand.fString = string;
832 fOperandStack.push(operand);
833 }
834 }
835 if (value) {
836 if (fOperandStack.count() == 0)
837 return false;
838 SkASSERT(fOperandStack.count() >= 1);
839 SkASSERT(fTypeStack.count() >= 1);
840 fOperandStack.pop(&value->fOperand);
841 SkOpType type;
842 fTypeStack.pop(&type);
843 value->fType = ToDisplayType(type);
844// SkASSERT(value->fType != SkType_Unknown);
845 if (topType != fReturnType && topType == kObject && fReturnType != kNoType) {
846 if (convertTo(ToDisplayType(fReturnType), value) == false)
847 return false;
848 }
849 }
850 while (fSuppressStack.count() > suppressBalance)
851 fSuppressStack.pop();
852 *scriptPtr = script;
853 return true; // no error
854}
855
856void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) {
857 UserCallBack callBack;
858 callBack.fMemberCallBack = member;
859 commonCallBack(kMember, callBack, userStorage);
860}
861
862void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) {
863 UserCallBack callBack;
864 callBack.fMemberFunctionCallBack = func;
865 commonCallBack(kMemberFunction, callBack, userStorage);
866}
867
868#if 0
869void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) {
870 UserCallBack callBack;
871 callBack.fObjectToStringCallBack = func;
872 commonCallBack(kObjectToString, callBack, userStorage);
873}
874#endif
875
876bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) {
877 SkScriptValue scriptValue;
878 (*scriptPtr)++;
879 *fOpStack.push() = kParen;
880 *fBraceStack.push() = kArrayBrace;
881 SkOpType saveType = fReturnType;
882 fReturnType = kInt;
883 bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL);
884 if (success == false)
885 return false;
886 fReturnType = saveType;
887 if (suppressed == false) {
888 if (convertTo(SkType_Int, &scriptValue) == false)
889 return false;
890 int index = scriptValue.fOperand.fS32;
891 SkScriptValue scriptValue;
892 SkOpType type;
893 fTypeStack.pop(&type);
894 fOperandStack.pop(&scriptValue.fOperand);
895 scriptValue.fType = ToDisplayType(type);
896 if (type == kObject) {
897 success = handleUnbox(&scriptValue);
898 if (success == false)
899 return false;
900 if (ToOpType(scriptValue.fType) != kArray) {
901 fError = kExpectedArray;
902 return false;
903 }
904 }
905 *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType();
906// SkASSERT(index >= 0);
907 if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
908 fError = kArrayIndexOutOfBounds;
909 return false;
910 }
911 scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
912 fOperandStack.push(scriptValue.fOperand);
913 }
914 fOpStack.pop(); // pop paren
915 return success;
916}
917
918bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) {
919 bool success = true;
920 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
921 if (callBack->fCallBackType != kBox)
922 continue;
923 success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue);
924 if (success) {
925 fOperandStack.push(scriptValue->fOperand);
926 *fTypeStack.push() = ToOpType(scriptValue->fType);
927 goto done;
928 }
929 }
930done:
931 return success;
932}
933
934bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) {
935 SkScriptValue callbackResult;
936 SkTDArray<SkScriptValue> params;
937 SkString functionName(fToken, fTokenLength);
938 fTokenLength = 0;
939 bool success = functionParams(scriptPtr, params);
940 if (success == false)
941 goto done;
942 if (suppressed == true)
943 return true;
944 {
945 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
946 if (callBack->fCallBackType != kFunction)
947 continue;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000948 success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 callBack->fUserStorage, &callbackResult);
950 if (success) {
951 fOperandStack.push(callbackResult.fOperand);
952 *fTypeStack.push() = ToOpType(callbackResult.fType);
953 goto done;
954 }
955 }
956 }
957 fError = kNoFunctionHandlerFound;
958 return false;
959done:
960 return success;
961}
962
963bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) {
964 SkScriptValue callbackResult;
965 bool success = true;
966 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
967 if (callBack->fCallBackType != kMember)
968 continue;
969 success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult);
970 if (success) {
971 if (callbackResult.fType == SkType_String)
972 track(callbackResult.fOperand.fString);
973 fOperandStack.push(callbackResult.fOperand);
974 *fTypeStack.push() = ToOpType(callbackResult.fType);
975 goto done;
976 }
977 }
978 return false;
979done:
980 return success;
981}
982
983bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) {
984 SkScriptValue callbackResult;
985 bool success = true;
986 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
987 if (callBack->fCallBackType != kMemberFunction)
988 continue;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000989 success = (*callBack->fMemberFunctionCallBack)(field, len, object, params,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 callBack->fUserStorage, &callbackResult);
991 if (success) {
992 if (callbackResult.fType == SkType_String)
993 track(callbackResult.fOperand.fString);
994 fOperandStack.push(callbackResult.fOperand);
995 *fTypeStack.push() = ToOpType(callbackResult.fType);
996 goto done;
997 }
998 }
999 return false;
1000done:
1001 return success;
1002}
1003
1004#if 0
1005bool SkScriptEngine::handleObjectToString(void* object) {
1006 SkScriptValue callbackResult;
1007 bool success = true;
1008 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
1009 if (callBack->fCallBackType != kObjectToString)
1010 continue;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001011 success = (*callBack->fObjectToStringCallBack)(object,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 callBack->fUserStorage, &callbackResult);
1013 if (success) {
1014 if (callbackResult.fType == SkType_String)
1015 track(callbackResult.fOperand.fString);
1016 fOperandStack.push(callbackResult.fOperand);
1017 *fTypeStack.push() = ToOpType(callbackResult.fType);
1018 goto done;
1019 }
1020 }
1021 return false;
1022done:
1023 return success;
1024}
1025#endif
1026
1027bool SkScriptEngine::handleProperty(bool suppressed) {
1028 SkScriptValue callbackResult;
1029 bool success = true;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001030 if (suppressed)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 goto done;
1032 success = false; // note that with standard animator-script plugins, callback never returns false
1033 {
1034 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
1035 if (callBack->fCallBackType != kProperty)
1036 continue;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001037 success = (*callBack->fPropertyCallBack)(fToken, fTokenLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 callBack->fUserStorage, &callbackResult);
1039 if (success) {
1040 if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) {
1041 callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
1042 track(callbackResult.fOperand.fString);
1043 }
1044 fOperandStack.push(callbackResult.fOperand);
1045 *fTypeStack.push() = ToOpType(callbackResult.fType);
1046 goto done;
1047 }
1048 }
1049 }
1050done:
1051 fTokenLength = 0;
1052 return success;
1053}
1054
1055bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) {
1056 bool success = true;
1057 for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
1058 if (callBack->fCallBackType != kUnbox)
1059 continue;
1060 success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue);
1061 if (success) {
1062 if (scriptValue->fType == SkType_String)
1063 track(scriptValue->fOperand.fString);
1064 goto done;
1065 }
1066 }
1067 return false;
1068done:
1069 return success;
1070}
1071
1072// note that entire expression is treated as if it were enclosed in parens
1073// an open paren is always the first thing in the op stack
1074
1075int SkScriptEngine::logicalOp(char ch, char nextChar) {
1076 int advance = 1;
1077 SkOp match;
1078 signed char precedence;
1079 switch (ch) {
1080 case ')':
1081 match = kParen;
1082 break;
1083 case ']':
1084 match = kArrayOp;
1085 break;
1086 case '?':
1087 match = kIf;
1088 break;
1089 case ':':
1090 match = kElse;
1091 break;
1092 case '&':
1093 if (nextChar != '&')
1094 goto noMatch;
1095 match = kLogicalAnd;
1096 advance = 2;
1097 break;
1098 case '|':
1099 if (nextChar != '|')
1100 goto noMatch;
1101 match = kLogicalOr;
1102 advance = 2;
1103 break;
1104 default:
1105noMatch:
1106 return 0;
1107 }
1108 SkSuppress suppress;
1109 precedence = gPrecedence[match];
1110 if (fSuppressStack.top().fSuppress) {
1111 if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) {
1112 SkOp topOp = fOpStack.top();
1113 if (gPrecedence[topOp] <= precedence)
1114 fOpStack.pop();
1115 goto goHome;
1116 }
1117 bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence;
1118 if (changedPrecedence)
1119 fSuppressStack.pop();
1120 if (precedence == kIfElsePrecedence) {
1121 if (match == kIf) {
1122 if (changedPrecedence)
1123 fOpStack.pop();
1124 else
1125 *fOpStack.push() = kIf;
1126 } else {
1127 if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) {
1128 goto flipSuppress;
1129 }
1130 fOpStack.pop();
1131 }
1132 }
1133 if (changedPrecedence == false)
1134 goto goHome;
1135 }
1136 while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) {
1137 if (processOp() == false)
1138 return false;
1139 }
1140 if (fSuppressStack.top().fOpStackDepth > fOpStack.count())
1141 fSuppressStack.pop();
1142 switch (match) {
1143 case kParen:
1144 case kArrayOp:
1145 if (fOpStack.count() <= 1 || fOpStack.top() != match) {
1146 fError = kMismatchedBrackets;
1147 return -1;
1148 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001149 if (match == kParen)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 fOpStack.pop();
1151 else {
1152 SkOpType indexType;
1153 fTypeStack.pop(&indexType);
1154 if (indexType != kInt && indexType != kScalar) {
1155 fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually)
1156 return -1;
1157 }
1158 SkOperand indexOperand;
1159 fOperandStack.pop(&indexOperand);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001160 int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) :
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 indexOperand.fS32;
1162 SkOpType arrayType;
1163 fTypeStack.pop(&arrayType);
1164 if ((unsigned)arrayType != (unsigned)kArray) {
1165 fError = kExpectedArray;
1166 return -1;
1167 }
1168 SkOperand arrayOperand;
1169 fOperandStack.pop(&arrayOperand);
1170 SkTypedArray* array = arrayOperand.fArray;
1171 SkOperand operand;
1172 if (array->getIndex(index, &operand) == false) {
1173 fError = kIndexOutOfRange;
1174 return -1;
1175 }
1176 SkOpType resultType = array->getOpType();
1177 fTypeStack.push(resultType);
1178 fOperandStack.push(operand);
1179 }
1180 break;
1181 case kIf: {
1182 SkScriptValue ifValue;
1183 SkOpType ifType;
1184 fTypeStack.pop(&ifType);
1185 ifValue.fType = ToDisplayType(ifType);
1186 fOperandStack.pop(&ifValue.fOperand);
1187 if (convertTo(SkType_Int, &ifValue) == false)
1188 return -1;
1189 if (ifValue.fType != SkType_Int) {
1190 fError = kExpectedIntForConditionOperator;
1191 return -1;
1192 }
1193 suppress.fSuppress = ifValue.fOperand.fS32 == 0;
1194 suppress.fOperator = kIf;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001195 suppress.fOpStackDepth = fOpStack.count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 suppress.fElse = false;
1197 fSuppressStack.push(suppress);
1198 // if left is true, do only up to colon
1199 // if left is false, do only after colon
1200 } break;
1201 case kElse:
1202flipSuppress:
reed@google.com21b1c5c2011-05-23 12:44:44 +00001203 if (fSuppressStack.top().fElse)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 fSuppressStack.pop();
1205 fSuppressStack.top().fElse = true;
1206 fSuppressStack.top().fSuppress ^= true;
1207 // flip last do / don't do consideration from last '?'
1208 break;
1209 case kLogicalAnd:
1210 case kLogicalOr: {
1211 if (fTypeStack.top() != kInt) {
1212 fError = kExpectedBooleanExpression;
1213 return -1;
1214 }
1215 int32_t topInt = fOperandStack.top().fS32;
1216 if (fOpStack.top() != kLogicalAnd)
1217 *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or'
1218 if (match == kLogicalOr ? topInt != 0 : topInt == 0) {
1219 suppress.fSuppress = true;
1220 suppress.fOperator = match;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001221 suppress.fOpStackDepth = fOpStack.count();
tomhudson@google.coma7ed3cc2011-07-29 13:20:06 +00001222 suppress.fElse = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 fSuppressStack.push(suppress);
1224 } else {
1225 fTypeStack.pop();
1226 fOperandStack.pop();
1227 }
1228 } break;
1229 default:
1230 SkASSERT(0);
1231 }
1232goHome:
1233 return advance;
1234}
1235
1236SkScriptEngine::Error SkScriptEngine::opError() {
1237 int opCount = fOpStack.count();
1238 int operandCount = fOperandStack.count();
1239 if (opCount == 0) {
1240 if (operandCount != 1)
1241 return kExpectedOperator;
1242 return kNoError;
1243 }
1244 SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp);
1245 const SkOperatorAttributes* attributes = &gOpAttributes[op];
1246 if (attributes->fLeftType != kNoType && operandCount < 2)
1247 return kExpectedValue;
1248 if (attributes->fLeftType == kNoType && operandCount < 1)
1249 return kExpectedValue;
1250 return kNoError;
1251}
1252
1253bool SkScriptEngine::processOp() {
1254 SkOp op;
1255 fOpStack.pop(&op);
1256 op = (SkOp) (op & ~kArtificialOp);
1257 const SkOperatorAttributes* attributes = &gOpAttributes[op];
1258 SkOpType type2;
1259 fTypeStack.pop(&type2);
1260 SkOpType type1 = type2;
1261 SkOperand operand2;
1262 fOperandStack.pop(&operand2);
1263 SkOperand operand1 = operand2; // !!! not really needed, suppresses warning
1264 if (attributes->fLeftType != kNoType) {
1265 fTypeStack.pop(&type1);
1266 fOperandStack.pop(&operand1);
1267 if (op == kFlipOps) {
1268 SkTSwap(type1, type2);
1269 SkTSwap(operand1, operand2);
1270 fOpStack.pop(&op);
1271 op = (SkOp) (op & ~kArtificialOp);
1272 attributes = &gOpAttributes[op];
1273 }
1274 if (type1 == kObject && (type1 & attributes->fLeftType) == 0) {
1275 SkScriptValue val;
1276 val.fType = ToDisplayType(type1);
1277 val.fOperand = operand1;
1278 bool success = handleUnbox(&val);
1279 if (success == false)
1280 return false;
1281 type1 = ToOpType(val.fType);
1282 operand1 = val.fOperand;
1283 }
1284 }
1285 if (type2 == kObject && (type2 & attributes->fLeftType) == 0) {
1286 SkScriptValue val;
1287 val.fType = ToDisplayType(type2);
1288 val.fOperand = operand2;
1289 bool success = handleUnbox(&val);
1290 if (success == false)
1291 return false;
1292 type2 = ToOpType(val.fType);
1293 operand2 = val.fOperand;
1294 }
1295 if (attributes->fLeftType != kNoType) {
1296 if (type1 != type2) {
1297 if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) {
1298 if (type1 == kInt || type1 == kScalar) {
1299 convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float);
1300 type1 = kString;
1301 }
1302 if (type2 == kInt || type2 == kScalar) {
1303 convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float);
1304 type2 = kString;
1305 }
1306 } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) {
1307 if (type1 == kInt) {
1308 operand1.fScalar = IntToScalar(operand1.fS32);
1309 type1 = kScalar;
1310 }
1311 if (type2 == kInt) {
1312 operand2.fScalar = IntToScalar(operand2.fS32);
1313 type2 = kScalar;
1314 }
1315 }
1316 }
1317 if ((type1 & attributes->fLeftType) == 0 || type1 != type2) {
1318 if (type1 == kString) {
1319 const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar);
1320 if (result == NULL) {
1321 fError = kExpectedNumber;
1322 return false;
1323 }
1324 type1 = kScalar;
1325 }
1326 if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) {
1327 operand1.fS32 = SkScalarFloor(operand1.fScalar);
1328 type1 = kInt;
1329 }
1330 }
1331 }
1332 if ((type2 & attributes->fRightType) == 0 || type1 != type2) {
1333 if (type2 == kString) {
1334 const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar);
1335 if (result == NULL) {
1336 fError = kExpectedNumber;
1337 return false;
1338 }
1339 type2 = kScalar;
1340 }
1341 if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) {
1342 operand2.fS32 = SkScalarFloor(operand2.fScalar);
1343 type2 = kInt;
1344 }
1345 }
1346 if (type2 == kScalar)
1347 op = (SkOp) (op + 1);
1348 else if (type2 == kString)
1349 op = (SkOp) (op + 2);
1350 switch(op) {
1351 case kAddInt:
1352 operand2.fS32 += operand1.fS32;
1353 break;
1354 case kAddScalar:
1355 operand2.fScalar += operand1.fScalar;
1356 break;
1357 case kAddString:
1358 if (fTrackString.find(operand1.fString) < 0) {
1359 operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString));
1360 track(operand1.fString);
1361 }
1362 operand1.fString->append(*operand2.fString);
1363 operand2 = operand1;
1364 break;
1365 case kBitAnd:
1366 operand2.fS32 &= operand1.fS32;
1367 break;
1368 case kBitNot:
1369 operand2.fS32 = ~operand2.fS32;
1370 break;
1371 case kBitOr:
1372 operand2.fS32 |= operand1.fS32;
1373 break;
1374 case kDivideInt:
1375 if (operand2.fS32 == 0) {
1376 operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
1377 break;
1378 } else {
1379 int32_t original = operand2.fS32;
1380 operand2.fS32 = operand1.fS32 / operand2.fS32;
1381 if (original * operand2.fS32 == operand1.fS32)
1382 break; // integer divide was good enough
1383 operand2.fS32 = original;
1384 type2 = kScalar;
1385 }
1386 case kDivideScalar:
1387 if (operand2.fScalar == 0)
1388 operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
1389 else
1390 operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar);
1391 break;
1392 case kEqualInt:
1393 operand2.fS32 = operand1.fS32 == operand2.fS32;
1394 break;
1395 case kEqualScalar:
1396 operand2.fS32 = operand1.fScalar == operand2.fScalar;
1397 type2 = kInt;
1398 break;
1399 case kEqualString:
1400 operand2.fS32 = *operand1.fString == *operand2.fString;
1401 type2 = kInt;
1402 break;
1403 case kGreaterEqualInt:
1404 operand2.fS32 = operand1.fS32 >= operand2.fS32;
1405 break;
1406 case kGreaterEqualScalar:
1407 operand2.fS32 = operand1.fScalar >= operand2.fScalar;
1408 type2 = kInt;
1409 break;
1410 case kGreaterEqualString:
1411 operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0;
1412 type2 = kInt;
1413 break;
1414 case kLogicalAnd:
1415 operand2.fS32 = !! operand2.fS32; // really, ToBool
1416 break;
1417 case kLogicalNot:
1418 operand2.fS32 = ! operand2.fS32;
1419 break;
1420 case kLogicalOr:
1421 SkASSERT(0); // should have already been processed
1422 break;
1423 case kMinusInt:
1424 operand2.fS32 = -operand2.fS32;
1425 break;
1426 case kMinusScalar:
1427 operand2.fScalar = -operand2.fScalar;
1428 break;
1429 case kModuloInt:
1430 operand2.fS32 = operand1.fS32 % operand2.fS32;
1431 break;
1432 case kModuloScalar:
1433 operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar);
1434 break;
1435 case kMultiplyInt:
1436 operand2.fS32 *= operand1.fS32;
1437 break;
1438 case kMultiplyScalar:
1439 operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar);
1440 break;
1441 case kShiftLeft:
1442 operand2.fS32 = operand1.fS32 << operand2.fS32;
1443 break;
1444 case kShiftRight:
1445 operand2.fS32 = operand1.fS32 >> operand2.fS32;
1446 break;
1447 case kSubtractInt:
1448 operand2.fS32 = operand1.fS32 - operand2.fS32;
1449 break;
1450 case kSubtractScalar:
1451 operand2.fScalar = operand1.fScalar - operand2.fScalar;
1452 break;
1453 case kXor:
1454 operand2.fS32 ^= operand1.fS32;
1455 break;
1456 default:
1457 SkASSERT(0);
1458 }
1459 fTypeStack.push(type2);
1460 fOperandStack.push(operand2);
1461 return true;
1462}
1463
1464void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) {
1465 UserCallBack callBack;
1466 callBack.fPropertyCallBack = prop;
1467 commonCallBack(kProperty, callBack, userStorage);
1468}
1469
rmistry@google.comd6176b02012-08-23 18:14:13 +00001470void SkScriptEngine::track(SkTypedArray* array) {
1471 SkASSERT(fTrackArray.find(array) < 0);
1472 *(fTrackArray.end() - 1) = array;
1473 fTrackArray.appendClear();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
rmistry@google.comd6176b02012-08-23 18:14:13 +00001476void SkScriptEngine::track(SkString* string) {
1477 SkASSERT(fTrackString.find(string) < 0);
1478 *(fTrackString.end() - 1) = string;
1479 fTrackString.appendClear();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480}
1481
1482void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) {
1483 UserCallBack callBack;
1484 callBack.fUnboxCallBack = func;
1485 commonCallBack(kUnbox, callBack, userStorage);
1486}
1487
1488bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) {
1489 SkASSERT(value);
1490 if (SkDisplayType::IsEnum(NULL /* fMaker */, toType))
1491 toType = SkType_Int;
1492 if (toType == SkType_Point || toType == SkType_3D_Point)
1493 toType = SkType_Float;
1494 if (toType == SkType_Drawable)
1495 toType = SkType_Displayable;
1496 SkDisplayTypes type = value->fType;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001497 if (type == toType)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 return true;
1499 SkOperand& operand = value->fOperand;
1500 bool success = true;
1501 switch (toType) {
1502 case SkType_Int:
1503 if (type == SkType_Boolean)
1504 break;
1505 if (type == SkType_Float)
1506 operand.fS32 = SkScalarFloor(operand.fScalar);
1507 else {
1508 if (type != SkType_String) {
1509 success = false;
1510 break; // error
1511 }
1512 success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
1513 }
1514 break;
1515 case SkType_Float:
1516 if (type == SkType_Int) {
humper@google.com0e515772013-01-07 19:54:40 +00001517 if (operand.fS32 == SK_NaN32)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518 operand.fScalar = SK_ScalarNaN;
1519 else if (SkAbs32(operand.fS32) == SK_MaxS32)
1520 operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
1521 else
1522 operand.fScalar = SkIntToScalar(operand.fS32);
1523 } else {
1524 if (type != SkType_String) {
1525 success = false;
1526 break; // error
1527 }
1528 success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
1529 }
1530 break;
1531 case SkType_String: {
1532 SkString* strPtr = new SkString();
1533 SkASSERT(engine);
1534 engine->track(strPtr);
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +00001535 if (type == SkType_Int) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536 strPtr->appendS32(operand.fS32);
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +00001537 } else if (type == SkType_Displayable) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 SkASSERT(0); // must call through instance version instead of static version
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +00001539 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540 if (type != SkType_Float) {
1541 success = false;
1542 break;
1543 }
1544 strPtr->appendScalar(operand.fScalar);
1545 }
1546 operand.fString = strPtr;
1547 } break;
1548 case SkType_Array: {
1549 SkTypedArray* array = new SkTypedArray(type);
1550 *array->append() = operand;
1551 engine->track(array);
1552 operand.fArray = array;
1553 } break;
1554 default:
1555 SkASSERT(0);
1556 }
1557 value->fType = toType;
1558 if (success == false)
1559 engine->fError = kTypeConversionFailed;
1560 return success;
1561}
1562
1563SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
1564 SkScalar scalar;
humper@google.com0e515772013-01-07 19:54:40 +00001565 if (s32 == SK_NaN32)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 scalar = SK_ScalarNaN;
1567 else if (SkAbs32(s32) == SK_MaxS32)
1568 scalar = SkSign32(s32) * SK_ScalarMax;
1569 else
1570 scalar = SkIntToScalar(s32);
1571 return scalar;
1572}
1573
1574SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) {
1575 int val = type;
1576 switch (val) {
1577 case kNoType:
1578 return SkType_Unknown;
1579 case kInt:
1580 return SkType_Int;
1581 case kScalar:
1582 return SkType_Float;
1583 case kString:
1584 return SkType_String;
1585 case kArray:
1586 return SkType_Array;
1587 case kObject:
1588 return SkType_Displayable;
1589// case kStruct:
1590// return SkType_Structure;
1591 default:
1592 SkASSERT(0);
1593 return SkType_Unknown;
1594 }
1595}
1596
1597SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) {
1598 if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
1599 return (SkOpType) kObject;
1600 if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
1601 return kInt;
1602 switch (type) {
1603 case SkType_ARGB:
1604 case SkType_MSec:
1605 case SkType_Int:
1606 return kInt;
1607 case SkType_Float:
1608 case SkType_Point:
1609 case SkType_3D_Point:
1610 return kScalar;
1611 case SkType_Base64:
1612 case SkType_DynamicString:
1613 case SkType_String:
1614 return kString;
1615 case SkType_Array:
1616 return (SkOpType) kArray;
1617 case SkType_Unknown:
1618 return kNoType;
1619 default:
1620 SkASSERT(0);
1621 return kNoType;
1622 }
1623}
1624
1625bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) {
1626 switch (value.fType) {
1627 case kInt:
1628 string->reset();
1629 string->appendS32(value.fOperand.fS32);
1630 break;
1631 case kScalar:
1632 string->reset();
1633 string->appendScalar(value.fOperand.fScalar);
1634 break;
1635 case kString:
1636 string->set(*value.fOperand.fString);
1637 break;
1638 default:
1639 SkASSERT(0);
1640 return false;
1641 }
1642 return true; // no error
1643}
1644
1645#ifdef SK_SUPPORT_UNITTEST
1646
reed@google.com7886ad32012-06-11 21:21:26 +00001647#include "SkFloatingPoint.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648
1649#define DEF_SCALAR_ANSWER 0
1650#define DEF_STRING_ANSWER NULL
1651
1652#define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER }
1654 #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655#define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
1656#define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
1657
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658static const SkScriptNAnswer scriptTests[] = {
1659 testInt(1>1/2),
1660 testInt((6+7)*8),
1661 testInt(0&&1?2:3),
1662 testInt(3*(4+5)),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001663 testScalar(1.0+2.0),
1664 testScalar(1.0+5),
1665 testScalar(3.0-1.0),
1666 testScalar(6-1.0),
1667 testScalar(- -5.5- -1.5),
1668 testScalar(2.5*6.),
1669 testScalar(0.5*4),
1670 testScalar(4.5/.5),
1671 testScalar(9.5/19),
1672 testRemainder(9.5, 0.5),
1673 testRemainder(9.,2),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 testRemainder(9,2.5),
1675 testRemainder(-9,2.5),
1676 testTrue(-9==-9.0),
1677 testTrue(-9.==-4.0-5),
1678 testTrue(-9.*1==-4-5),
1679 testFalse(-9!=-9.0),
1680 testFalse(-9.!=-4.0-5),
1681 testFalse(-9.*1!=-4-5),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682 testInt(0x123),
1683 testInt(0XABC),
1684 testInt(0xdeadBEEF),
1685 { "'123'+\"456\"", SkType_String, 0, 0, "123456" },
1686 { "123+\"456\"", SkType_String, 0, 0, "123456" },
1687 { "'123'+456", SkType_String, 0, 0, "123456" },
1688 { "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
1689 { "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
1690 { "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
1691 { "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
1692 { "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
1693 { "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
1694 testInt(123),
1695 testInt(-345),
1696 testInt(+678),
1697 testInt(1+2+3),
1698 testInt(3*4+5),
1699 testInt(6+7*8),
1700 testInt(-1-2-8/4),
1701 testInt(-9%4),
1702 testInt(9%-4),
1703 testInt(-9%-4),
1704 testInt(123|978),
1705 testInt(123&978),
1706 testInt(123^978),
1707 testInt(2<<4),
1708 testInt(99>>3),
1709 testInt(~55),
1710 testInt(~~55),
1711 testInt(!55),
1712 testInt(!!55),
1713 // both int
1714 testInt(2<2),
1715 testInt(2<11),
1716 testInt(20<11),
1717 testInt(2<=2),
1718 testInt(2<=11),
1719 testInt(20<=11),
1720 testInt(2>2),
1721 testInt(2>11),
1722 testInt(20>11),
1723 testInt(2>=2),
1724 testInt(2>=11),
1725 testInt(20>=11),
1726 testInt(2==2),
1727 testInt(2==11),
1728 testInt(20==11),
1729 testInt(2!=2),
1730 testInt(2!=11),
1731 testInt(20!=11),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 // left int, right scalar
1733 testInt(2<2.),
1734 testInt(2<11.),
1735 testInt(20<11.),
1736 testInt(2<=2.),
1737 testInt(2<=11.),
1738 testInt(20<=11.),
1739 testInt(2>2.),
1740 testInt(2>11.),
1741 testInt(20>11.),
1742 testInt(2>=2.),
1743 testInt(2>=11.),
1744 testInt(20>=11.),
1745 testInt(2==2.),
1746 testInt(2==11.),
1747 testInt(20==11.),
1748 testInt(2!=2.),
1749 testInt(2!=11.),
1750 testInt(20!=11.),
1751 // left scalar, right int
1752 testInt(2.<2),
1753 testInt(2.<11),
1754 testInt(20.<11),
1755 testInt(2.<=2),
1756 testInt(2.<=11),
1757 testInt(20.<=11),
1758 testInt(2.>2),
1759 testInt(2.>11),
1760 testInt(20.>11),
1761 testInt(2.>=2),
1762 testInt(2.>=11),
1763 testInt(20.>=11),
1764 testInt(2.==2),
1765 testInt(2.==11),
1766 testInt(20.==11),
1767 testInt(2.!=2),
1768 testInt(2.!=11),
1769 testInt(20.!=11),
1770 // both scalar
1771 testInt(2.<11.),
1772 testInt(20.<11.),
1773 testInt(2.<=2.),
1774 testInt(2.<=11.),
1775 testInt(20.<=11.),
1776 testInt(2.>2.),
1777 testInt(2.>11.),
1778 testInt(20.>11.),
1779 testInt(2.>=2.),
1780 testInt(2.>=11.),
1781 testInt(20.>=11.),
1782 testInt(2.==2.),
1783 testInt(2.==11.),
1784 testInt(20.==11.),
1785 testInt(2.!=2.),
1786 testInt(2.!=11.),
1787 testInt(20.!=11.),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788 // int, string (string is int)
1789 testFalse(2<'2'),
1790 testTrue(2<'11'),
1791 testFalse(20<'11'),
1792 testTrue(2<='2'),
1793 testTrue(2<='11'),
1794 testFalse(20<='11'),
1795 testFalse(2>'2'),
1796 testFalse(2>'11'),
1797 testTrue(20>'11'),
1798 testTrue(2>='2'),
1799 testFalse(2>='11'),
1800 testTrue(20>='11'),
1801 testTrue(2=='2'),
1802 testFalse(2=='11'),
1803 testFalse(2!='2'),
1804 testTrue(2!='11'),
1805 // int, string (string is scalar)
1806 testFalse(2<'2.'),
1807 testTrue(2<'11.'),
1808 testFalse(20<'11.'),
1809 testTrue(2=='2.'),
1810 testFalse(2=='11.'),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 // scalar, string
1812 testFalse(2.<'2.'),
1813 testTrue(2.<'11.'),
1814 testFalse(20.<'11.'),
1815 testTrue(2.=='2.'),
1816 testFalse(2.=='11.'),
1817 // string, int
1818 testFalse('2'<2),
1819 testTrue('2'<11),
1820 testFalse('20'<11),
1821 testTrue('2'==2),
1822 testFalse('2'==11),
1823 // string, scalar
1824 testFalse('2'<2.),
1825 testTrue('2'<11.),
1826 testFalse('20'<11.),
1827 testTrue('2'==2.),
1828 testFalse('2'==11.),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 // string, string
1830 testFalse('2'<'2'),
1831 testFalse('2'<'11'),
1832 testFalse('20'<'11'),
1833 testTrue('2'=='2'),
1834 testFalse('2'=='11'),
1835 // logic
1836 testInt(1?2:3),
1837 testInt(0?2:3),
commit-bot@chromium.org2dc8b962013-08-14 11:36:27 +00001838 testInt((1&&2)||3),
1839 testInt((1&&0)||3),
1840 testInt((1&&0)||0),
1841 testInt(1||(0&&3)),
1842 testInt(0||(0&&3)),
1843 testInt(0||(1&&3)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844 testInt(1?(2?3:4):5),
1845 testInt(0?(2?3:4):5),
1846 testInt(1?(0?3:4):5),
1847 testInt(0?(0?3:4):5),
1848 testInt(1?2?3:4:5),
1849 testInt(0?2?3:4:5),
1850 testInt(1?0?3:4:5),
1851 testInt(0?0?3:4:5),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001852
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853 testInt(1?2:(3?4:5)),
1854 testInt(0?2:(3?4:5)),
1855 testInt(1?0:(3?4:5)),
1856 testInt(0?0:(3?4:5)),
1857 testInt(1?2:3?4:5),
1858 testInt(0?2:3?4:5),
1859 testInt(1?0:3?4:5),
1860 testInt(0?0:3?4:5)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862};
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863
1864#define SkScriptNAnswer_testCount SK_ARRAY_COUNT(scriptTests)
1865
1866void SkScriptEngine::UnitTest() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
1868 SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType));
1869 SkScriptValue value;
1870 const char* script = scriptTests[index].fScript;
1871 SkASSERT(engine.evaluateScript(&script, &value) == true);
1872 SkASSERT(value.fType == scriptTests[index].fType);
1873 SkScalar error;
1874 switch (value.fType) {
1875 case SkType_Int:
1876 SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
1877 break;
1878 case SkType_Float:
1879 error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
1880 SkASSERT(error < SK_Scalar1 / 10000);
1881 break;
1882 case SkType_String:
1883 SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
1884 break;
1885 default:
1886 SkASSERT(0);
1887 }
1888 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889}
1890#endif