Updated string escaping in the assembler.

Strings are now escaped correctly when assembling.
TODO dissassembler support for strange strings (newlines for example).
This commit is contained in:
Andrew Woloszyn 2015-10-14 12:44:19 -04:00 committed by David Neto
parent daf493b202
commit 3e69cd1b9f
4 changed files with 57 additions and 39 deletions

View File

@ -110,11 +110,23 @@ spv_result_t spvTextToLiteral(const char* textValue, spv_literal_t* pLiteral) {
// TODO(dneto): Allow escaping.
if (len < 2 || textValue[0] != '"' || textValue[len - 1] != '"')
return SPV_FAILED_MATCH;
bool escaping = false;
size_t write_index = 0;
for(const char* val = textValue + 1; val != textValue + len - 1; ++val) {
if ((*val == '\\') && (!escaping)) {
escaping = true;
} else {
// Have to save space for the null-terminator
if (write_index >= sizeof(pLiteral->value.str) - 1)
return SPV_ERROR_OUT_OF_MEMORY;
pLiteral->value.str[write_index] = *val;
escaping = false;
++write_index;
}
}
pLiteral->type = SPV_LITERAL_TYPE_STRING;
// Need room for the null-terminator.
if (len >= sizeof(pLiteral->value.str)) return SPV_ERROR_OUT_OF_MEMORY;
strncpy(pLiteral->value.str, textValue + 1, len - 2);
pLiteral->value.str[len - 2] = 0;
pLiteral->value.str[write_index] = '\0';
} else if (numPeriods == 1) {
double d = std::strtod(textValue, nullptr);
float f = (float)d;

View File

@ -54,10 +54,10 @@ typedef struct spv_literal_t {
uint64_t u64;
float f;
double d;
// Allow room for the null terminator, and two surrounding quotes.
// Allow room for the null terminator
// TODO(dneto): This is a very large array. We should use a
// different kind of container.
char str[SPV_LIMIT_LITERAL_STRING_BYTES_MAX + 3];
char str[SPV_LIMIT_LITERAL_STRING_BYTES_MAX + 1];
} value;
} spv_literal_t;

View File

@ -27,14 +27,16 @@ but the assembler does not enforce this rule.
The opcode names and expected operands are described in section 3 of
the SPIR-V specification. An operand is one of:
* a literal integer: A decimal integer, or a hexadecimal integer.
A hexadecimal integer is indicated by a leading `0x` or `0X`. A hex
integer supplied for a signed integer value will be sign-extended.
For example, `0xffff` supplied as the literal for an `OpConstant`
on a signed 16-bit integer type will be interpreted as the value `-1`.
* a literal integer: A decimal integer, or a hexadecimal integer
(indicated by a leading `0x`).
* a literal floating point number.
* a literal string, surrounded by double-quotes `"`. TODO: describe quoting and
escaping rules.
* a literal string.
* A literal string is everything following a double-quote `"` until the
following un-escaped double-quote, this includes special characters such as
newlines.
* A backslash `\` may be used to escape characters in the string. The `\`
may be used to escape a double-quote or a `\` but is simply ignored when
preceding any other character.
* a named enumerated value, specific to that operand position. For example,
the `OpMemoryModel` takes a named Addressing Model operand (e.g. `Logical` or
`Physical32`), and a named Memory Model operand (e.g. `Simple` or `OpenCL`).
@ -154,11 +156,10 @@ instruction.)
The assembler processes the tokens encountered in alternate parsing mode as
follows:
* If the token is a number literal, since context may be lost, the number
is interpreted as a 32-bit value and output as a single word. In order to
specify multiple-word literals in alternate-parsing mode, further uses of
`!<integer>` tokens may be required.
All formats supported by `strtoul()` are accepted.
* If the token is a number literal, it outputs that number as one or more words,
as defined in the SPIR-V specification for Literal Number. The number must
fit within the unsigned 32-bit range. All formats supported by `strtoul()`
are accepted.
* If the token is a string literal, it outputs a sequence of words representing
the string as defined in the SPIR-V specification for Literal String.
* If the token is an ID, it outputs the ID's internal number.

View File

@ -105,32 +105,37 @@ TEST(TextLiteral, BadString) {
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("a\"", &l));
}
TEST(TextLiteral, GoodString) {
class GoodStringTest
: public ::testing::TestWithParam<std::pair<const char*, const char*>> {};
TEST_P(GoodStringTest, GoodStrings) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("\"-\"", &l));
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral(std::get<0>(GetParam()), &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_STREQ("-", l.value.str);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("\"--\"", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_STREQ("--", l.value.str);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("\"1-2\"", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_STREQ("1-2", l.value.str);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("\"123a\"", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_STREQ("123a", l.value.str);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("\"12.2.3\"", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_STREQ("12.2.3", l.value.str);
// TODO(dneto): escaping in strings is not supported yet.
EXPECT_STREQ(std::get<1>(GetParam()), l.value.str);
}
#define CASE(NAME) spv::Decoration##NAME, #NAME
INSTANTIATE_TEST_CASE_P(
TextLiteral, GoodStringTest,
::testing::ValuesIn(std::vector<std::pair<const char*, const char*>>{
{R"("-")", "-"},
{R"("--")", "--"},
{R"("1-2")", "1-2"},
{R"("123a")", "123a"},
{R"("12.2.3")", "12.2.3"},
{R"("\"")", "\""},
{R"("\\")", "\\"},
{"\"\\foo\nbar\"", "foo\nbar"},
{"\"\\foo\\\nbar\"", "foo\nbar"},
{"\"\U00E4BAB2\"", "\U00E4BAB2"},
{"\"\\\U00E4BAB2\"", "\U00E4BAB2"},
{"\"this \\\" and this \\\\ and \\\U00E4BAB2\"",
"this \" and this \\ and \U00E4BAB2"}
}));
#undef CASE
TEST(TextLiteral, StringTooLong) {
spv_literal_t l;
std::string too_long = std::string("\"") +