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