Reland "Interpreter: Bounds check array access, add bool return from run"
This is a reland of f42de9e1e5
Original change's description:
> Interpreter: Bounds check array access, add bool return from run
>
> Out of bounds access with constant indices is a compile error.
> At runtime, causes the interpreter to fail. Made several other
> conditions trigger the same failure logic, and updated all
> uses of the interpreter to validate success.
>
> Change-Id: I3720b3c83903220b010ec574121fc64dbe102378
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/228256
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Reed <reed@google.com>
Change-Id: I8849de815f7efb730ac9c55b6edd296cb9ca7599
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/228353
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
de71a74fc4
commit
869a3e81ea
@ -53,9 +53,10 @@ protected:
|
||||
fPixels.data() + 3 * fCount,
|
||||
};
|
||||
|
||||
fByteCode->runStriped(fMain, args, 4, fCount, nullptr, 0, nullptr, 0);
|
||||
SkAssertResult(fByteCode->runStriped(fMain, args, 4, fCount,
|
||||
nullptr, 0, nullptr, 0));
|
||||
} else {
|
||||
fByteCode->run(fMain, fPixels.data(), nullptr, fCount, nullptr, 0);
|
||||
SkAssertResult(fByteCode->run(fMain, fPixels.data(), nullptr, fCount, nullptr, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,7 +187,7 @@ protected:
|
||||
}
|
||||
|
||||
// Trigger one run now to check correctness
|
||||
fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0);
|
||||
SkAssertResult(fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0));
|
||||
for (int i = 0; i < fGroups; ++i) {
|
||||
for (int j = 1; j < fValues; ++j) {
|
||||
SkASSERT(fDst[i * fValues + j] >= fDst[i * fValues + j - 1]);
|
||||
@ -196,7 +197,7 @@ protected:
|
||||
|
||||
void onDraw(int loops, SkCanvas*) override {
|
||||
for (int i = 0; i < loops; i++) {
|
||||
fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0);
|
||||
SkAssertResult(fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,7 +477,7 @@ public:
|
||||
void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
fRandomValue->setRandom(&ps[i].fRandom);
|
||||
fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, ¶ms.fDeltaTime, 2);
|
||||
SkAssertResult(fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, ¶ms.fDeltaTime, 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2584,8 +2584,8 @@ STAGE(interpreter, SkRasterPipeline_InterpreterCtx* c) {
|
||||
sk_unaligned_store(aa, a);
|
||||
}
|
||||
|
||||
c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
|
||||
(const float*)c->inputs, c->ninputs, nullptr, 0);
|
||||
SkAssertResult(c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
|
||||
(const float*)c->inputs, c->ninputs, nullptr, 0));
|
||||
|
||||
r = sk_unaligned_load<F>(rr);
|
||||
g = sk_unaligned_load<F>(gg);
|
||||
|
@ -59,6 +59,7 @@ static const uint8_t* disassemble_instruction(const uint8_t* ip) {
|
||||
printf("callexternal %d, %d, %d", argumentCount, returnCount, externalValue);
|
||||
break;
|
||||
}
|
||||
case ByteCodeInstruction::kClampIndex: printf("clampindex %d", READ8()); break;
|
||||
VECTOR_DISASSEMBLE(kCompareIEQ, "compareieq")
|
||||
VECTOR_DISASSEMBLE(kCompareINEQ, "compareineq")
|
||||
VECTOR_MATRIX_DISASSEMBLE(kCompareFEQ, "comparefeq")
|
||||
@ -356,7 +357,7 @@ static T vec_mod(T a, T b) {
|
||||
|
||||
#define spf(index) sp[index].fFloat
|
||||
|
||||
static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack,
|
||||
static bool innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack,
|
||||
float* outReturn[], VValue globals[], bool stripedOutput, int N,
|
||||
int baseIndex) {
|
||||
// Needs to be the first N non-negative integers, at least as large as VecWidth
|
||||
@ -387,8 +388,7 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
|
||||
|
||||
if (f->fConditionCount + 1 > (int)SK_ARRAY_COUNT(condStack) ||
|
||||
f->fLoopCount + 1 > (int)SK_ARRAY_COUNT(loopStack)) {
|
||||
SkDEBUGFAIL("Function with too much nested control flow to evaluate");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mask = [&]() { return *maskPtr & *loopPtr; };
|
||||
@ -468,6 +468,14 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kClampIndex: {
|
||||
int length = READ8();
|
||||
if (skvx::any(mask() & ((sp[0].fSigned < 0) | (sp[0].fSigned >= length)))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_BINARY_OP(kCompareIEQ, fSigned, ==)
|
||||
VECTOR_MATRIX_BINARY_OP(kCompareFEQ, fFloat, ==)
|
||||
VECTOR_BINARY_OP(kCompareINEQ, fSigned, !=)
|
||||
@ -841,7 +849,7 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
} else {
|
||||
// When we were called, the caller reserved stack space for their copy of our
|
||||
// return value, then 'stack' was positioned after that, where our parameters
|
||||
@ -1076,8 +1084,11 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
|
||||
|
||||
default:
|
||||
SkDEBUGFAILF("unsupported instruction %d\n", (int) inst);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Unreachable
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Interpreter
|
||||
@ -1097,7 +1108,7 @@ void ByteCodeFunction::disassemble() const {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int N,
|
||||
bool ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int N,
|
||||
const float* uniforms, int uniformCount) const {
|
||||
#if defined(SK_ENABLE_SKSL_INTERPRETER)
|
||||
#ifdef TRACE
|
||||
@ -1106,13 +1117,17 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
|
||||
Interpreter::VValue stack[128];
|
||||
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
|
||||
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
|
||||
SkDEBUGFAIL("Function requires too much stack space to evaluate");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uniformCount != (int)fInputSlots.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkASSERT(uniformCount == (int)fInputSlots.size());
|
||||
Interpreter::VValue globals[32];
|
||||
SkASSERT((int)SK_ARRAY_COUNT(globals) >= fGlobalCount);
|
||||
if (fGlobalCount > (int)SK_ARRAY_COUNT(globals)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t slot : fInputSlots) {
|
||||
globals[slot].fFloat = *uniforms++;
|
||||
}
|
||||
@ -1136,7 +1151,9 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
|
||||
|
||||
bool stripedOutput = false;
|
||||
float** outArray = outReturn ? &outReturn : nullptr;
|
||||
innerRun(this, f, stack, outArray, globals, stripedOutput, w, baseIndex);
|
||||
if (!innerRun(this, f, stack, outArray, globals, stripedOutput, w, baseIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transpose out parameters back
|
||||
{
|
||||
@ -1164,12 +1181,14 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
|
||||
N -= w;
|
||||
baseIndex += w;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
SkDEBUGFAIL("ByteCode interpreter not enabled");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, int N,
|
||||
bool ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, int N,
|
||||
const float* uniforms, int uniformCount,
|
||||
float* outArgs[], int outCount) const {
|
||||
#if defined(SK_ENABLE_SKSL_INTERPRETER)
|
||||
@ -1179,8 +1198,21 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
|
||||
Interpreter::VValue stack[128];
|
||||
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
|
||||
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
|
||||
SkDEBUGFAIL("Function requires too much stack space to evaluate");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nargs != f->fParameterCount ||
|
||||
outCount != f->fReturnCount ||
|
||||
uniformCount != (int)fInputSlots.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Interpreter::VValue globals[32];
|
||||
if (fGlobalCount > (int)SK_ARRAY_COUNT(globals)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t slot : fInputSlots) {
|
||||
globals[slot].fFloat = *uniforms++;
|
||||
}
|
||||
|
||||
// innerRun just takes outArgs, so clear it if the count is zero
|
||||
@ -1188,15 +1220,6 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
|
||||
outArgs = nullptr;
|
||||
}
|
||||
|
||||
SkASSERT(nargs == f->fParameterCount);
|
||||
SkASSERT(outCount == f->fReturnCount);
|
||||
SkASSERT(uniformCount == (int)fInputSlots.size());
|
||||
Interpreter::VValue globals[32];
|
||||
SkASSERT((int)SK_ARRAY_COUNT(globals) >= fGlobalCount);
|
||||
for (uint8_t slot : fInputSlots) {
|
||||
globals[slot].fFloat = *uniforms++;
|
||||
}
|
||||
|
||||
int baseIndex = 0;
|
||||
|
||||
while (N) {
|
||||
@ -1208,7 +1231,9 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
|
||||
}
|
||||
|
||||
bool stripedOutput = true;
|
||||
innerRun(this, f, stack, outArgs, globals, stripedOutput, w, baseIndex);
|
||||
if (!innerRun(this, f, stack, outArgs, globals, stripedOutput, w, baseIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy out parameters back
|
||||
int slot = 0;
|
||||
@ -1228,8 +1253,11 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
|
||||
N -= w;
|
||||
baseIndex += w;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
SkDEBUGFAIL("ByteCode interpreter not enabled");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,8 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
// Followed by three bytes indicating: the number of argument slots, the number of return slots,
|
||||
// and the index of the external value to call
|
||||
kCallExternal,
|
||||
// For dynamic array access: Followed by byte indicating length of array
|
||||
kClampIndex,
|
||||
VECTOR(kCompareIEQ),
|
||||
VECTOR(kCompareINEQ),
|
||||
VECTOR_MATRIX(kCompareFEQ),
|
||||
@ -198,15 +200,13 @@ struct SK_API ByteCode {
|
||||
* The return value is stored in 'outReturn' (may be null, to discard the return value).
|
||||
* 'uniforms' are mapped to 'uniform' globals, in order.
|
||||
*/
|
||||
void run(const ByteCodeFunction*, float* args, float* outReturn, int N,
|
||||
const float* uniforms, int uniformCount) const;
|
||||
bool SKSL_WARN_UNUSED_RESULT run(const ByteCodeFunction*, float* args, float* outReturn, int N,
|
||||
const float* uniforms, int uniformCount) const;
|
||||
|
||||
// For now, if outArgCount > 0, then we will memcpy VecWidth number of elements into
|
||||
// each slot in outArgs. This may change in the future, as that may be more than is actually
|
||||
// valid (depending on the N that was passed in.
|
||||
void runStriped(const ByteCodeFunction*, float* args[], int nargs, int N,
|
||||
const float* uniforms, int uniformCount,
|
||||
float* outArgs[], int outArgCount) const;
|
||||
bool SKSL_WARN_UNUSED_RESULT runStriped(const ByteCodeFunction*,
|
||||
float* args[], int nargs, int N,
|
||||
const float* uniforms, int uniformCount,
|
||||
float* outArgs[], int outArgCount) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -208,6 +208,7 @@ int ByteCodeGenerator::StackUsage(ByteCodeInstruction inst, int count_) {
|
||||
case ByteCodeInstruction::kInverse3x3:
|
||||
case ByteCodeInstruction::kInverse4x4: return 0;
|
||||
|
||||
case ByteCodeInstruction::kClampIndex: return 0;
|
||||
case ByteCodeInstruction::kNotB: return 0;
|
||||
case ByteCodeInstruction::kNegateFN: return 0;
|
||||
|
||||
@ -459,9 +460,16 @@ int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* st
|
||||
case Expression::kIndex_Kind: {
|
||||
const IndexExpression& i = (const IndexExpression&)expr;
|
||||
int stride = SlotCount(i.fType);
|
||||
int length = i.fBase->fType.columns();
|
||||
SkASSERT(length <= 255);
|
||||
int offset = -1;
|
||||
if (i.fIndex->isConstant()) {
|
||||
offset = i.fIndex->getConstantInt() * stride;
|
||||
int64_t index = i.fIndex->getConstantInt();
|
||||
if (index < 0 || index >= length) {
|
||||
fErrors.error(i.fIndex->fOffset, "Array index out of bounds.");
|
||||
return 0;
|
||||
}
|
||||
offset = index * stride;
|
||||
} else {
|
||||
if (i.fIndex->hasSideEffects()) {
|
||||
// Having a side-effect in an indexer is technically safe for an rvalue,
|
||||
@ -471,6 +479,8 @@ int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* st
|
||||
return 0;
|
||||
}
|
||||
this->writeExpression(*i.fIndex);
|
||||
this->write(ByteCodeInstruction::kClampIndex);
|
||||
this->write8(length);
|
||||
if (stride != 1) {
|
||||
this->write(ByteCodeInstruction::kPushImmediate);
|
||||
this->write32(stride);
|
||||
@ -829,7 +839,7 @@ void ByteCodeGenerator::writeExternalValue(const ExternalValueReference& e) {
|
||||
}
|
||||
|
||||
void ByteCodeGenerator::writeVariableExpression(const Expression& expr) {
|
||||
Variable::Storage storage;
|
||||
Variable::Storage storage = Variable::kLocal_Storage;
|
||||
int location = this->getLocation(expr, &storage);
|
||||
bool isGlobal = storage == Variable::kGlobal_Storage;
|
||||
int count = SlotCount(expr.fType);
|
||||
@ -1230,7 +1240,7 @@ public:
|
||||
if (!discard) {
|
||||
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
|
||||
}
|
||||
Variable::Storage storage;
|
||||
Variable::Storage storage = Variable::kLocal_Storage;
|
||||
int location = fGenerator.getLocation(*fSwizzle.fBase, &storage);
|
||||
bool isGlobal = storage == Variable::kGlobal_Storage;
|
||||
if (location < 0) {
|
||||
@ -1275,7 +1285,7 @@ public:
|
||||
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
|
||||
}
|
||||
}
|
||||
Variable::Storage storage;
|
||||
Variable::Storage storage = Variable::kLocal_Storage;
|
||||
int location = fGenerator.getLocation(fExpression, &storage);
|
||||
bool isGlobal = storage == Variable::kGlobal_Storage;
|
||||
if (location < 0 || count > 4) {
|
||||
|
@ -29,12 +29,12 @@
|
||||
#include "include/core/SkTypes.h"
|
||||
#endif
|
||||
|
||||
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
|
||||
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define SKSL_PRINTF_LIKE(A, B)
|
||||
#define SKSL_WARN_UNUSED_RESULT
|
||||
#endif
|
||||
|
||||
#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
|
||||
|
@ -40,7 +40,7 @@ void test(skiatest::Reporter* r, const char* src, float* in, int expectedCount,
|
||||
}
|
||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||
std::unique_ptr<float[]> out = std::unique_ptr<float[]>(new float[expectedCount]);
|
||||
byteCode->run(main, in, out.get(), 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, in, out.get(), 1, nullptr, 0));
|
||||
bool valid = exactCompare ? !memcmp(out.get(), expected, sizeof(float) * expectedCount)
|
||||
: nearly_equal(out.get(), expected, expectedCount);
|
||||
if (!valid) {
|
||||
@ -95,11 +95,11 @@ void vec_test(skiatest::Reporter* r, const char* src) {
|
||||
|
||||
// First run in scalar mode to determine the expected output
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
byteCode->run(main, out_s + i * 4, nullptr, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, out_s + i * 4, nullptr, 1, nullptr, 0));
|
||||
}
|
||||
|
||||
// Now run in parallel and compare results
|
||||
byteCode->run(main, out_v, nullptr, 4, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, out_v, nullptr, 4, nullptr, 0));
|
||||
if (memcmp(out_s, out_v, sizeof(out_s)) != 0) {
|
||||
printf("for program: %s\n", src);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
@ -131,7 +131,7 @@ void test(skiatest::Reporter* r, const char* src, float inR, float inG, float in
|
||||
}
|
||||
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
|
||||
float inoutColor[4] = { inR, inG, inB, inA };
|
||||
byteCode->run(main, inoutColor, nullptr, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, inoutColor, nullptr, 1, nullptr, 0));
|
||||
if (inoutColor[0] != expectedR || inoutColor[1] != expectedG ||
|
||||
inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
|
||||
printf("for program: %s\n", src);
|
||||
@ -559,14 +559,14 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
||||
{
|
||||
SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
|
||||
int out = 0;
|
||||
byteCode->run(rect_height, (float*)&in, (float*)&out, 1, (float*)gRects, 16);
|
||||
SkAssertResult(byteCode->run(rect_height, (float*)&in, (float*)&out, 1, (float*)gRects, 16));
|
||||
REPORTER_ASSERT(r, out == 30);
|
||||
}
|
||||
|
||||
{
|
||||
int in[2] = { 15, 25 };
|
||||
RectAndColor out;
|
||||
byteCode->run(make_blue_rect, (float*)in, (float*)&out, 1, (float*)gRects, 16);
|
||||
SkAssertResult(byteCode->run(make_blue_rect, (float*)in, (float*)&out, 1, (float*)gRects, 16));
|
||||
REPORTER_ASSERT(r, out.fRect.width() == 15);
|
||||
REPORTER_ASSERT(r, out.fRect.height() == 25);
|
||||
SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
|
||||
@ -576,14 +576,14 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
||||
{
|
||||
int in[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
int out = 0;
|
||||
byteCode->run(median, (float*)in, (float*)&out, 1, (float*)gRects, 16);
|
||||
SkAssertResult(byteCode->run(median, (float*)in, (float*)&out, 1, (float*)gRects, 16));
|
||||
REPORTER_ASSERT(r, out == 8);
|
||||
}
|
||||
|
||||
{
|
||||
float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
float out[8] = { 0 };
|
||||
byteCode->run(sums, in, out, 1, (float*)gRects, 16);
|
||||
SkAssertResult(byteCode->run(sums, in, out, 1, (float*)gRects, 16));
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 2) / 2));
|
||||
}
|
||||
@ -592,7 +592,7 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
||||
{
|
||||
int in = 2;
|
||||
SkIRect out = SkIRect::MakeEmpty();
|
||||
byteCode->run(get_rect, (float*)&in, (float*)&out, 1, (float*)gRects, 16);
|
||||
SkAssertResult(byteCode->run(get_rect, (float*)&in, (float*)&out, 1, (float*)gRects, 16));
|
||||
REPORTER_ASSERT(r, out == gRects[2]);
|
||||
}
|
||||
|
||||
@ -600,7 +600,7 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
||||
ManyRects in;
|
||||
memset(&in, 0, sizeof(in));
|
||||
in.fNumRects = 2;
|
||||
byteCode->run(fill_rects, (float*)&in, nullptr, 1, (float*)gRects, 16);
|
||||
SkAssertResult(byteCode->run(fill_rects, (float*)&in, nullptr, 1, (float*)gRects, 16));
|
||||
ManyRects expected;
|
||||
memset(&expected, 0, sizeof(expected));
|
||||
expected.fNumRects = 2;
|
||||
@ -613,27 +613,55 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_failure(skiatest::Reporter* r, const char* src) {
|
||||
SkSL::Compiler compiler;
|
||||
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
|
||||
SkSL::Program::Settings());
|
||||
REPORTER_ASSERT(r, program);
|
||||
|
||||
auto byteCode = compiler.toByteCode(*program);
|
||||
REPORTER_ASSERT(r, compiler.errorCount() > 0);
|
||||
REPORTER_ASSERT(r, !byteCode);
|
||||
}
|
||||
|
||||
static void expect_run_failure(skiatest::Reporter* r, const char* src, float* in) {
|
||||
SkSL::Compiler compiler;
|
||||
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
|
||||
SkSL::Program::Settings());
|
||||
REPORTER_ASSERT(r, program);
|
||||
|
||||
auto byteCode = compiler.toByteCode(*program);
|
||||
REPORTER_ASSERT(r, byteCode);
|
||||
|
||||
bool result = byteCode->run(byteCode->getFunction("main"), in, nullptr, 1, nullptr, 0);
|
||||
REPORTER_ASSERT(r, !result);
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLInterpreterRestrictFunctionCalls, r) {
|
||||
auto check = [r](const char* src) {
|
||||
SkSL::Compiler compiler;
|
||||
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
|
||||
SkSL::Program::Settings());
|
||||
REPORTER_ASSERT(r, program);
|
||||
|
||||
auto byteCode = compiler.toByteCode(*program);
|
||||
REPORTER_ASSERT(r, compiler.errorCount() > 0);
|
||||
REPORTER_ASSERT(r, !byteCode);
|
||||
};
|
||||
|
||||
// Ensure that simple recursion is not allowed
|
||||
check("float main() { return main() + 1; }");
|
||||
expect_failure(r, "float main() { return main() + 1; }");
|
||||
|
||||
// Ensure that calls to undefined functions are not allowed (to prevent mutual recursion)
|
||||
check("float foo(); float bar() { return foo(); } float foo() { return bar(); }");
|
||||
expect_failure(r, "float foo(); float bar() { return foo(); } float foo() { return bar(); }");
|
||||
|
||||
// returns are not allowed inside conditionals (or loops, which are effectively the same thing)
|
||||
check("float main(float x, float y) { if (x < y) { return x; } return y; }");
|
||||
check("float main(float x) { while (x > 1) { return x; } return 0; }");
|
||||
expect_failure(r, "float main(float x, float y) { if (x < y) { return x; } return y; }");
|
||||
expect_failure(r, "float main(float x) { while (x > 1) { return x; } return 0; }");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLInterpreterArrayBounds, r) {
|
||||
// Out of bounds array access at compile time
|
||||
expect_failure(r, "float main(float x[4]) { return x[-1]; }");
|
||||
expect_failure(r, "float2 main(float2 x[2]) { return x[2]; }");
|
||||
|
||||
// Out of bounds array access at runtime is pinned, and we don't update any inout data
|
||||
float in[3] = { -1.0f, 1.0f, 2.0f };
|
||||
expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
|
||||
REPORTER_ASSERT(r, in[0] == -1.0f && in[1] == 1.0f && in[2] == 2.0f);
|
||||
|
||||
in[0] = 3.0f;
|
||||
expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
|
||||
REPORTER_ASSERT(r, in[0] == 3.0f && in[1] == 1.0f && in[2] == 2.0f);
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLInterpreterFunctions, r) {
|
||||
@ -674,13 +702,13 @@ DEF_TEST(SkSLInterpreterFunctions, r) {
|
||||
|
||||
float out = 0.0f;
|
||||
float in = 3.0f;
|
||||
byteCode->run(main, &in, &out, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, &in, &out, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, out = 6.0f);
|
||||
|
||||
byteCode->run(dot3, &in, &out, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(dot3, &in, &out, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, out = 9.0f);
|
||||
|
||||
byteCode->run(dot2, &in, &out, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(dot2, &in, &out, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, out = -1.0f);
|
||||
}
|
||||
|
||||
@ -910,7 +938,7 @@ DEF_TEST(SkSLInterpreterExternalValues, r) {
|
||||
}
|
||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||
float out;
|
||||
byteCode->run(main, nullptr, &out, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, nullptr, &out, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, out == 66.0);
|
||||
REPORTER_ASSERT(r, outValue == 152);
|
||||
} else {
|
||||
@ -942,7 +970,7 @@ DEF_TEST(SkSLInterpreterExternalValuesVector, r) {
|
||||
return;
|
||||
}
|
||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||
byteCode->run(main, nullptr, nullptr, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, nullptr, nullptr, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, value[0] == 2);
|
||||
REPORTER_ASSERT(r, value[1] == 4);
|
||||
REPORTER_ASSERT(r, value[2] == 6);
|
||||
@ -1008,7 +1036,7 @@ DEF_TEST(SkSLInterpreterExternalValuesCall, r) {
|
||||
}
|
||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||
float out;
|
||||
byteCode->run(main, nullptr, &out, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, nullptr, &out, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, out == 5.0);
|
||||
} else {
|
||||
printf("%s\n%s", src, compiler.errorText().c_str());
|
||||
@ -1076,7 +1104,7 @@ DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
|
||||
}
|
||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||
float out[4];
|
||||
byteCode->run(main, nullptr, out, 1, nullptr, 0);
|
||||
SkAssertResult(byteCode->run(main, nullptr, out, 1, nullptr, 0));
|
||||
REPORTER_ASSERT(r, out[0] == 1.0);
|
||||
REPORTER_ASSERT(r, out[1] == 2.0);
|
||||
REPORTER_ASSERT(r, out[2] == 3.0);
|
||||
|
Loading…
Reference in New Issue
Block a user