| |
| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #include "SkScriptRuntime.h" |
| #include "SkScript2.h" |
| #include "SkParse.h" |
| #include "SkScriptCallBack.h" |
| #include "SkString.h" |
| #include "SkOpArray.h" |
| |
| // script tokenizer |
| |
| // turn text into token string |
| // turn number literals into inline UTF8-style values |
| // process operators to turn standard notation into stack notation |
| |
| // defer processing until the tokens can all be resolved |
| // then, turn token strings into indices into the appropriate tables / dictionaries |
| |
| // consider: const evaluation? |
| |
| // replace script string with script tokens preceeded by special value |
| |
| // need second version of script plugins that return private index of found value? |
| // then would need in script index of plugin, private index |
| |
| // encode brace stack push/pop as opcodes |
| |
| // should token script enocde type where possible? |
| |
| // current flow: |
| // strip whitespace |
| // if in array brace [ recurse, continue |
| // if token, handle function, or array, or property (continue) |
| // parse number, continue |
| // parse token, continue |
| // parse string literal, continue |
| // if dot operator, handle dot, continue |
| // if [ , handle array literal or accessor, continue |
| // if ), pop (if function, break) |
| // if ], pop ; if ',' break |
| // handle logical ops |
| // or, handle arithmetic ops |
| // loop |
| |
| // !!! things to do |
| // add separate processing loop to advance while suppressed |
| // or, include jump offset to skip suppressed code? |
| |
| SkScriptRuntime::~SkScriptRuntime() { |
| for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++) |
| delete *stringPtr; |
| for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++) |
| delete *arrayPtr; |
| } |
| |
| bool SkScriptRuntime::executeTokens(unsigned char* opCode) { |
| SkOperand2 operand[2]; // 1=accumulator and 2=operand |
| SkScriptEngine2::TypeOp op; |
| size_t ref; |
| int index, size; |
| int registerLoad; |
| SkScriptCallBack* callBack SK_INIT_TO_AVOID_WARNING; |
| do { |
| switch ((op = (SkScriptEngine2::TypeOp) *opCode++)) { |
| case SkScriptEngine2::kArrayToken: // create an array |
| operand[0].fArray = new SkOpArray(SkOperand2::kNoType /*fReturnType*/); |
| break; |
| case SkScriptEngine2::kArrayIndex: // array accessor |
| index = operand[1].fS32; |
| if (index >= operand[0].fArray->count()) { |
| fError = kArrayIndexOutOfBounds; |
| return false; |
| } |
| operand[0] = operand[0].fArray->begin()[index]; |
| break; |
| case SkScriptEngine2::kArrayParam: // array initializer, or function param |
| *operand[0].fArray->append() = operand[1]; |
| break; |
| case SkScriptEngine2::kCallback: |
| memcpy(&index, opCode, sizeof(index)); |
| opCode += sizeof(index); |
| callBack = fCallBackArray[index]; |
| break; |
| case SkScriptEngine2::kFunctionCall: { |
| memcpy(&ref, opCode, sizeof(ref)); |
| opCode += sizeof(ref); |
| SkScriptCallBackFunction* callBackFunction = (SkScriptCallBackFunction*) callBack; |
| if (callBackFunction->invoke(ref, operand[0].fArray, /* params */ |
| &operand[0] /* result */) == false) { |
| fError = kFunctionCallFailed; |
| return false; |
| } |
| } break; |
| case SkScriptEngine2::kMemberOp: { |
| memcpy(&ref, opCode, sizeof(ref)); |
| opCode += sizeof(ref); |
| SkScriptCallBackMember* callBackMember = (SkScriptCallBackMember*) callBack; |
| if (callBackMember->invoke(ref, operand[0].fObject, &operand[0]) == false) { |
| fError = kMemberOpFailed; |
| return false; |
| } |
| } break; |
| case SkScriptEngine2::kPropertyOp: { |
| memcpy(&ref, opCode, sizeof(ref)); |
| opCode += sizeof(ref); |
| SkScriptCallBackProperty* callBackProperty = (SkScriptCallBackProperty*) callBack; |
| if (callBackProperty->getResult(ref, &operand[0])== false) { |
| fError = kPropertyOpFailed; |
| return false; |
| } |
| } break; |
| case SkScriptEngine2::kAccumulatorPop: |
| fRunStack.pop(&operand[0]); |
| break; |
| case SkScriptEngine2::kAccumulatorPush: |
| *fRunStack.push() = operand[0]; |
| break; |
| case SkScriptEngine2::kIntegerAccumulator: |
| case SkScriptEngine2::kIntegerOperand: |
| registerLoad = op - SkScriptEngine2::kIntegerAccumulator; |
| memcpy(&operand[registerLoad].fS32, opCode, sizeof(int32_t)); |
| opCode += sizeof(int32_t); |
| break; |
| case SkScriptEngine2::kScalarAccumulator: |
| case SkScriptEngine2::kScalarOperand: |
| registerLoad = op - SkScriptEngine2::kScalarAccumulator; |
| memcpy(&operand[registerLoad].fScalar, opCode, sizeof(SkScalar)); |
| opCode += sizeof(SkScalar); |
| break; |
| case SkScriptEngine2::kStringAccumulator: |
| case SkScriptEngine2::kStringOperand: { |
| SkString* strPtr = new SkString(); |
| track(strPtr); |
| registerLoad = op - SkScriptEngine2::kStringAccumulator; |
| memcpy(&size, opCode, sizeof(size)); |
| opCode += sizeof(size); |
| strPtr->set((char*) opCode, size); |
| opCode += size; |
| operand[registerLoad].fString = strPtr; |
| } break; |
| case SkScriptEngine2::kStringTrack: // call after kObjectToValue |
| track(operand[0].fString); |
| break; |
| case SkScriptEngine2::kBoxToken: { |
| SkOperand2::OpType type; |
| memcpy(&type, opCode, sizeof(type)); |
| opCode += sizeof(type); |
| SkScriptCallBackConvert* callBackBox = (SkScriptCallBackConvert*) callBack; |
| if (callBackBox->convert(type, &operand[0]) == false) |
| return false; |
| } break; |
| case SkScriptEngine2::kUnboxToken: |
| case SkScriptEngine2::kUnboxToken2: { |
| SkScriptCallBackConvert* callBackUnbox = (SkScriptCallBackConvert*) callBack; |
| if (callBackUnbox->convert(SkOperand2::kObject, &operand[0]) == false) |
| return false; |
| } break; |
| case SkScriptEngine2::kIfOp: |
| case SkScriptEngine2::kLogicalAndInt: |
| memcpy(&size, opCode, sizeof(size)); |
| opCode += sizeof(size); |
| if (operand[0].fS32 == 0) |
| opCode += size; // skip to else (or end of if predicate) |
| break; |
| case SkScriptEngine2::kElseOp: |
| memcpy(&size, opCode, sizeof(size)); |
| opCode += sizeof(size); |
| opCode += size; // if true: after predicate, always skip to end of else |
| break; |
| case SkScriptEngine2::kLogicalOrInt: |
| memcpy(&size, opCode, sizeof(size)); |
| opCode += sizeof(size); |
| if (operand[0].fS32 != 0) |
| opCode += size; // skip to kToBool opcode after || predicate |
| break; |
| // arithmetic conversion ops |
| case SkScriptEngine2::kFlipOpsOp: |
| SkTSwap(operand[0], operand[1]); |
| break; |
| case SkScriptEngine2::kIntToString: |
| case SkScriptEngine2::kIntToString2: |
| case SkScriptEngine2::kScalarToString: |
| case SkScriptEngine2::kScalarToString2:{ |
| SkString* strPtr = new SkString(); |
| track(strPtr); |
| if (op == SkScriptEngine2::kIntToString || op == SkScriptEngine2::kIntToString2) |
| strPtr->appendS32(operand[op - SkScriptEngine2::kIntToString].fS32); |
| else |
| strPtr->appendScalar(operand[op - SkScriptEngine2::kScalarToString].fScalar); |
| operand[0].fString = strPtr; |
| } break; |
| case SkScriptEngine2::kIntToScalar: |
| case SkScriptEngine2::kIntToScalar2: |
| operand[0].fScalar = SkScriptEngine2::IntToScalar(operand[op - SkScriptEngine2::kIntToScalar].fS32); |
| break; |
| case SkScriptEngine2::kStringToInt: |
| if (SkParse::FindS32(operand[0].fString->c_str(), &operand[0].fS32) == false) |
| return false; |
| break; |
| case SkScriptEngine2::kStringToScalar: |
| case SkScriptEngine2::kStringToScalar2: |
| if (SkParse::FindScalar(operand[0].fString->c_str(), |
| &operand[op - SkScriptEngine2::kStringToScalar].fScalar) == false) |
| return false; |
| break; |
| case SkScriptEngine2::kScalarToInt: |
| operand[0].fS32 = SkScalarFloor(operand[0].fScalar); |
| break; |
| // arithmetic ops |
| case SkScriptEngine2::kAddInt: |
| operand[0].fS32 += operand[1].fS32; |
| break; |
| case SkScriptEngine2::kAddScalar: |
| operand[0].fScalar += operand[1].fScalar; |
| break; |
| case SkScriptEngine2::kAddString: |
| // if (fTrackString.find(operand[1].fString) < 0) { |
| // operand[1].fString = SkNEW_ARGS(SkString, (*operand[1].fString)); |
| // track(operand[1].fString); |
| // } |
| operand[0].fString->append(*operand[1].fString); |
| break; |
| case SkScriptEngine2::kBitAndInt: |
| operand[0].fS32 &= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kBitNotInt: |
| operand[0].fS32 = ~operand[0].fS32; |
| break; |
| case SkScriptEngine2::kBitOrInt: |
| operand[0].fS32 |= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kDivideInt: |
| SkASSERT(operand[1].fS32 != 0); |
| if (operand[1].fS32 == 0) |
| operand[0].fS32 = operand[0].fS32 == 0 ? SK_NaN32 : |
| operand[0].fS32 > 0 ? SK_MaxS32 : -SK_MaxS32; |
| else |
| if (operand[1].fS32 != 0) // throw error on divide by zero? |
| operand[0].fS32 /= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kDivideScalar: |
| if (operand[1].fScalar == 0) |
| operand[0].fScalar = operand[0].fScalar == 0 ? SK_ScalarNaN : |
| operand[0].fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax; |
| else |
| operand[0].fScalar = SkScalarDiv(operand[0].fScalar, operand[1].fScalar); |
| break; |
| case SkScriptEngine2::kEqualInt: |
| operand[0].fS32 = operand[0].fS32 == operand[1].fS32; |
| break; |
| case SkScriptEngine2::kEqualScalar: |
| operand[0].fS32 = operand[0].fScalar == operand[1].fScalar; |
| break; |
| case SkScriptEngine2::kEqualString: |
| operand[0].fS32 = *operand[0].fString == *operand[1].fString; |
| break; |
| case SkScriptEngine2::kGreaterEqualInt: |
| operand[0].fS32 = operand[0].fS32 >= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kGreaterEqualScalar: |
| operand[0].fS32 = operand[0].fScalar >= operand[1].fScalar; |
| break; |
| case SkScriptEngine2::kGreaterEqualString: |
| operand[0].fS32 = strcmp(operand[0].fString->c_str(), operand[1].fString->c_str()) >= 0; |
| break; |
| case SkScriptEngine2::kToBool: |
| operand[0].fS32 = !! operand[0].fS32; |
| break; |
| case SkScriptEngine2::kLogicalNotInt: |
| operand[0].fS32 = ! operand[0].fS32; |
| break; |
| case SkScriptEngine2::kMinusInt: |
| operand[0].fS32 = -operand[0].fS32; |
| break; |
| case SkScriptEngine2::kMinusScalar: |
| operand[0].fScalar = -operand[0].fScalar; |
| break; |
| case SkScriptEngine2::kModuloInt: |
| operand[0].fS32 %= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kModuloScalar: |
| operand[0].fScalar = SkScalarMod(operand[0].fScalar, operand[1].fScalar); |
| break; |
| case SkScriptEngine2::kMultiplyInt: |
| operand[0].fS32 *= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kMultiplyScalar: |
| operand[0].fScalar = SkScalarMul(operand[0].fScalar, operand[1].fScalar); |
| break; |
| case SkScriptEngine2::kShiftLeftInt: |
| operand[0].fS32 <<= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kShiftRightInt: |
| operand[0].fS32 >>= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kSubtractInt: |
| operand[0].fS32 -= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kSubtractScalar: |
| operand[0].fScalar -= operand[1].fScalar; |
| break; |
| case SkScriptEngine2::kXorInt: |
| operand[0].fS32 ^= operand[1].fS32; |
| break; |
| case SkScriptEngine2::kEnd: |
| goto done; |
| case SkScriptEngine2::kNop: |
| SkASSERT(0); |
| default: |
| break; |
| } |
| } while (true); |
| done: |
| fRunStack.push(operand[0]); |
| return true; |
| } |
| |
| bool SkScriptRuntime::getResult(SkOperand2* result) { |
| if (fRunStack.count() == 0) |
| return false; |
| fRunStack.pop(result); |
| return true; |
| } |
| |
| void SkScriptRuntime::track(SkOpArray* array) { |
| SkASSERT(fTrackArray.find(array) < 0); |
| *fTrackArray.append() = array; |
| } |
| |
| void SkScriptRuntime::track(SkString* string) { |
| SkASSERT(fTrackString.find(string) < 0); |
| *fTrackString.append() = string; |
| } |
| |
| void SkScriptRuntime::untrack(SkOpArray* array) { |
| int index = fTrackArray.find(array); |
| SkASSERT(index >= 0); |
| fTrackArray.begin()[index] = NULL; |
| } |
| |
| void SkScriptRuntime::untrack(SkString* string) { |
| int index = fTrackString.find(string); |
| SkASSERT(index >= 0); |
| fTrackString.begin()[index] = NULL; |
| } |
| |