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