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