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