Merge pull request #9412 from ethereum/unicode-string

[BREAKING] Support unicode string literal type
This commit is contained in:
chriseth
2020-07-28 11:42:23 +02:00
committed by GitHub
32 changed files with 794 additions and 29 deletions
+131
View File
@@ -0,0 +1,131 @@
{
"absolutePath": "a",
"exportedSymbols":
{
"C":
[
9
]
},
"id": 10,
"nodeType": "SourceUnit",
"nodes":
[
{
"abstract": false,
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 9,
"linearizedBaseContracts":
[
9
],
"name": "C",
"nodeType": "ContractDefinition",
"nodes":
[
{
"body":
{
"id": 7,
"nodeType": "Block",
"src": "33:36:1",
"statements":
[
{
"assignments":
[
4
],
"declarations":
[
{
"constant": false,
"id": 4,
"mutability": "mutable",
"name": "x",
"nodeType": "VariableDeclaration",
"scope": 7,
"src": "35:15:1",
"stateVariable": false,
"storageLocation": "memory",
"typeDescriptions":
{
"typeIdentifier": "t_string_memory_ptr",
"typeString": "string"
},
"typeName":
{
"id": 3,
"name": "string",
"nodeType": "ElementaryTypeName",
"src": "35:6:1",
"typeDescriptions":
{
"typeIdentifier": "t_string_storage_ptr",
"typeString": "string"
}
},
"visibility": "internal"
}
],
"id": 6,
"initialValue":
{
"hexValue": "48656c6c6f20576f726c64",
"id": 5,
"isConstant": false,
"isLValue": false,
"isPure": true,
"kind": "string",
"lValueRequested": false,
"nodeType": "Literal",
"src": "53:13:1",
"typeDescriptions":
{
"typeIdentifier": "t_stringliteral_592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba",
"typeString": "literal_string \"Hello World\""
},
"value": "Hello World"
},
"nodeType": "VariableDeclarationStatement",
"src": "35:31:1"
}
]
},
"functionSelector": "26121ff0",
"id": 8,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "f",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 1,
"nodeType": "ParameterList",
"parameters": [],
"src": "23:2:1"
},
"returnParameters":
{
"id": 2,
"nodeType": "ParameterList",
"parameters": [],
"src": "33:0:1"
},
"scope": 9,
"src": "13:56:1",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
}
],
"scope": 10,
"src": "0:71:1"
}
],
"src": "0:72:1"
}
+3
View File
@@ -0,0 +1,3 @@
contract C { function f() public { string memory x = "Hello World"; } }
// ----
+165
View File
@@ -0,0 +1,165 @@
{
"attributes":
{
"absolutePath": "a",
"exportedSymbols":
{
"C":
[
9
]
}
},
"children":
[
{
"attributes":
{
"abstract": false,
"baseContracts":
[
null
],
"contractDependencies":
[
null
],
"contractKind": "contract",
"fullyImplemented": true,
"linearizedBaseContracts":
[
9
],
"name": "C",
"scope": 10
},
"children":
[
{
"attributes":
{
"functionSelector": "26121ff0",
"implemented": true,
"isConstructor": false,
"kind": "function",
"modifiers":
[
null
],
"name": "f",
"scope": 9,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
"children":
[
{
"attributes":
{
"parameters":
[
null
]
},
"children": [],
"id": 1,
"name": "ParameterList",
"src": "23:2:1"
},
{
"attributes":
{
"parameters":
[
null
]
},
"children": [],
"id": 2,
"name": "ParameterList",
"src": "33:0:1"
},
{
"children":
[
{
"attributes":
{
"assignments":
[
4
]
},
"children":
[
{
"attributes":
{
"constant": false,
"mutability": "mutable",
"name": "x",
"scope": 7,
"stateVariable": false,
"storageLocation": "memory",
"type": "string",
"visibility": "internal"
},
"children":
[
{
"attributes":
{
"name": "string",
"type": "string"
},
"id": 3,
"name": "ElementaryTypeName",
"src": "35:6:1"
}
],
"id": 4,
"name": "VariableDeclaration",
"src": "35:15:1"
},
{
"attributes":
{
"hexvalue": "48656c6c6f20576f726c64",
"isConstant": false,
"isLValue": false,
"isPure": true,
"lValueRequested": false,
"token": "string",
"type": "literal_string \"Hello World\"",
"value": "Hello World"
},
"id": 5,
"name": "Literal",
"src": "53:13:1"
}
],
"id": 6,
"name": "VariableDeclarationStatement",
"src": "35:31:1"
}
],
"id": 7,
"name": "Block",
"src": "33:36:1"
}
],
"id": 8,
"name": "FunctionDefinition",
"src": "13:56:1"
}
],
"id": 9,
"name": "ContractDefinition",
"src": "0:71:1"
}
],
"id": 10,
"name": "SourceUnit",
"src": "0:72:1"
}
+131
View File
@@ -0,0 +1,131 @@
{
"absolutePath": "a",
"exportedSymbols":
{
"C":
[
9
]
},
"id": 10,
"nodeType": "SourceUnit",
"nodes":
[
{
"abstract": false,
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 9,
"linearizedBaseContracts":
[
9
],
"name": "C",
"nodeType": "ContractDefinition",
"nodes":
[
{
"body":
{
"id": 7,
"nodeType": "Block",
"src": "33:42:1",
"statements":
[
{
"assignments":
[
4
],
"declarations":
[
{
"constant": false,
"id": 4,
"mutability": "mutable",
"name": "x",
"nodeType": "VariableDeclaration",
"scope": 7,
"src": "35:15:1",
"stateVariable": false,
"storageLocation": "memory",
"typeDescriptions":
{
"typeIdentifier": "t_string_memory_ptr",
"typeString": "string"
},
"typeName":
{
"id": 3,
"name": "string",
"nodeType": "ElementaryTypeName",
"src": "35:6:1",
"typeDescriptions":
{
"typeIdentifier": "t_string_storage_ptr",
"typeString": "string"
}
},
"visibility": "internal"
}
],
"id": 6,
"initialValue":
{
"hexValue": "48656c6c6f20f09f9883",
"id": 5,
"isConstant": false,
"isLValue": false,
"isPure": true,
"kind": "unicodeString",
"lValueRequested": false,
"nodeType": "Literal",
"src": "53:19:1",
"typeDescriptions":
{
"typeIdentifier": "t_stringliteral_cd7a99177cebb3d14b8cc54e313dbf76867c71cd6fbb9a33ce3870dc80e9992b",
"typeString": "literal_string \"Hello \ud83d\ude03\""
},
"value": "Hello \ud83d\ude03"
},
"nodeType": "VariableDeclarationStatement",
"src": "35:37:1"
}
]
},
"functionSelector": "26121ff0",
"id": 8,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "f",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 1,
"nodeType": "ParameterList",
"parameters": [],
"src": "23:2:1"
},
"returnParameters":
{
"id": 2,
"nodeType": "ParameterList",
"parameters": [],
"src": "33:0:1"
},
"scope": 9,
"src": "13:62:1",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
}
],
"scope": 10,
"src": "0:77:1"
}
],
"src": "0:78:1"
}
+3
View File
@@ -0,0 +1,3 @@
contract C { function f() public { string memory x = unicode"Hello 😃"; } }
// ----
@@ -0,0 +1,165 @@
{
"attributes":
{
"absolutePath": "a",
"exportedSymbols":
{
"C":
[
9
]
}
},
"children":
[
{
"attributes":
{
"abstract": false,
"baseContracts":
[
null
],
"contractDependencies":
[
null
],
"contractKind": "contract",
"fullyImplemented": true,
"linearizedBaseContracts":
[
9
],
"name": "C",
"scope": 10
},
"children":
[
{
"attributes":
{
"functionSelector": "26121ff0",
"implemented": true,
"isConstructor": false,
"kind": "function",
"modifiers":
[
null
],
"name": "f",
"scope": 9,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
"children":
[
{
"attributes":
{
"parameters":
[
null
]
},
"children": [],
"id": 1,
"name": "ParameterList",
"src": "23:2:1"
},
{
"attributes":
{
"parameters":
[
null
]
},
"children": [],
"id": 2,
"name": "ParameterList",
"src": "33:0:1"
},
{
"children":
[
{
"attributes":
{
"assignments":
[
4
]
},
"children":
[
{
"attributes":
{
"constant": false,
"mutability": "mutable",
"name": "x",
"scope": 7,
"stateVariable": false,
"storageLocation": "memory",
"type": "string",
"visibility": "internal"
},
"children":
[
{
"attributes":
{
"name": "string",
"type": "string"
},
"id": 3,
"name": "ElementaryTypeName",
"src": "35:6:1"
}
],
"id": 4,
"name": "VariableDeclaration",
"src": "35:15:1"
},
{
"attributes":
{
"hexvalue": "48656c6c6f20f09f9883",
"isConstant": false,
"isLValue": false,
"isPure": true,
"lValueRequested": false,
"token": "unicodeString",
"type": "literal_string \"Hello \ud83d\ude03\"",
"value": "Hello \ud83d\ude03"
},
"id": 5,
"name": "Literal",
"src": "53:19:1"
}
],
"id": 6,
"name": "VariableDeclarationStatement",
"src": "35:37:1"
}
],
"id": 7,
"name": "Block",
"src": "33:42:1"
}
],
"id": 8,
"name": "FunctionDefinition",
"src": "13:62:1"
}
],
"id": 9,
"name": "ContractDefinition",
"src": "0:77:1"
}
],
"id": 10,
"name": "SourceUnit",
"src": "0:78:1"
}
+60
View File
@@ -81,6 +81,45 @@ BOOST_AUTO_TEST_CASE(assembly_multiple_assign)
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(string_printable)
{
for (unsigned v = 0x20; v < 0x7e; v++) {
string lit{static_cast<char>(v)};
// Escape \ and " (since we are quoting with ")
if (v == '\\' || v == '"')
lit = string{'\\'} + lit;
Scanner scanner(CharStream(" { \"" + lit + "\"", ""));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), string{static_cast<char>(v)});
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
// Special case of unescaped " for strings quoted with '
Scanner scanner(CharStream(" { '\"'", ""));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "\"");
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(string_nonprintable)
{
for (unsigned v = 0; v < 0xff; v++) {
// Skip the valid ones
if (v >= 0x20 && v <= 0x7e)
continue;
string lit{static_cast<char>(v)};
Scanner scanner(CharStream(" { \"" + lit + "\"", ""));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
if (v == '\n' || v == '\v' || v == '\f' || v == '\r')
BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalStringEndQuote);
else
BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalCharacterInString);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "");
}
}
BOOST_AUTO_TEST_CASE(string_escapes)
{
Scanner scanner(CharStream(" { \"a\\x61\"", ""));
@@ -506,6 +545,8 @@ BOOST_AUTO_TEST_CASE(empty_comment)
}
// Unicode string escapes
BOOST_AUTO_TEST_CASE(valid_unicode_string_escape)
{
Scanner scanner(CharStream("{ \"\\u00DAnicode\"", ""));
@@ -545,6 +586,25 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape)
BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
}
// Unicode string literal
BOOST_AUTO_TEST_CASE(valid_unicode_literal)
{
Scanner scanner(CharStream("{ unicode\"Hello 😃\"", ""));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
BOOST_CHECK_EQUAL(scanner.next(), Token::UnicodeStringLiteral);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \xf0\x9f\x98\x83", 10));
}
BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal)
{
// Non-printable characters are allowed in unicode strings...
Scanner scanner(CharStream("{ unicode\"Hello \007😃\"", ""));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
BOOST_CHECK_EQUAL(scanner.next(), Token::UnicodeStringLiteral);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \x07\xf0\x9f\x98\x83", 11));
}
// HEX STRING LITERAL
BOOST_AUTO_TEST_CASE(valid_hex_literal)
@@ -1,9 +1,14 @@
contract C {
function f() public pure returns (string memory) {
return "😃, 😭, and 😈";
return unicode"😃, 😭, and 😈";
}
function g() public pure returns (string memory) {
return unicode"😃, 😭,\
and 😈";
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x20, 0x14, "\xf0\x9f\x98\x83, \xf0\x9f\x98\xad, and \xf0\x9f\x98\x88"
// g() -> 0x20, 0x14, "\xf0\x9f\x98\x83, \xf0\x9f\x98\xad, and \xf0\x9f\x98\x88"
@@ -1,4 +1,7 @@
contract test {
function fixedBytesHex() public returns(bytes32 ret) {
return hex"aabb00ff";
}
function fixedBytes() public returns(bytes32 ret) {
return "abc\x00\xff__";
}
@@ -11,5 +14,6 @@ contract test {
// ====
// compileViaYul: also
// ----
// fixedBytesHex() -> "\xaa\xbb\0\xff"
// fixedBytes() -> "abc\0\xff__"
// pipeThrough(bytes2, bool): "\0\x02", true -> "\0\x2", true
@@ -1,3 +1,4 @@
contract C {
string s = "\xf0\x9f\xa6\x84";
}
// ----
@@ -0,0 +1,9 @@
contract test {
function f() public pure returns (string memory) {
return "hello world";
}
function g() public pure returns (string memory) {
return unicode"hello world";
}
}
// ----
@@ -0,0 +1,7 @@
contract test {
function f() public pure returns (bytes32) {
bytes32 escapeCharacters = unicode"foo" unicode"😃, 😭, and 😈" unicode"!";
return escapeCharacters;
}
}
// ----
@@ -0,0 +1,8 @@
contract test {
function f() public pure returns (bytes32) {
bytes32 escapeCharacters = "foo" hex"aa" unicode"😃, 😭, and 😈" "!" hex"00";
return escapeCharacters;
}
}
// ----
// ParserError 2314: (106-113): Expected ';' but got 'HexStringLiteral'
@@ -1,6 +1,6 @@
contract test {
function f() public pure returns (string memory) {
return "😃, 😭, and 😈";
return unicode"😃, 😭, and 😈";
}
}
// ----
@@ -0,0 +1,7 @@
contract test {
function f() public pure returns (string memory) {
return "😃, 😭, and 😈";
}
}
// ----
// ParserError 8936: (86-88): Invalid character in string.
@@ -19,3 +19,4 @@ contract test {
return res;
}
}
// ----