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