@@ -17,15 +17,27 @@ uint256 constant MAX_LINE_LENGTH = 120;
1717/// needs to match what formatters expect.
1818string constant NEWLINE_DUE_TO_MAX_LENGTH = "\n " ;
1919
20- /// Thrown when a contract name is not a Solidity identifier. Such a name cannot
21- /// be interpolated into a file path or a constant declaration.
20+ /// Thrown when a name is not a Solidity identifier. Such a name cannot be
21+ /// interpolated into a file path or a constant declaration.
2222/// @param name The rejected name.
23- error InvalidContractName (string name );
23+ error InvalidIdentifier (string name );
2424
2525/// Thrown when a bytecode hash is asked for at an address that holds no code.
2626/// @param instance The address that holds no code.
2727error CodelessInstance (address instance );
2828
29+ /// Thrown when an SPDX licence identifier is not a non-empty single line. An
30+ /// empty identifier names no licence on a tag that a presence check accepts, and
31+ /// solc refuses the file it heads with "Invalid SPDX license identifier". A line
32+ /// break ends the tag's line so that everything after it lands as source.
33+ /// @param spdxLicenseIdentifier The rejected identifier.
34+ error InvalidSpdxLicenseIdentifier (string spdxLicenseIdentifier );
35+
36+ /// Thrown when a copyright text is not a non-empty single line, for the same
37+ /// reasons the licence identifier has to be one.
38+ /// @param copyrightText The rejected text.
39+ error InvalidCopyrightText (string copyrightText );
40+
2941/// @title LibCodeGen
3042/// @notice Library for generating Solidity code snippets for contract function
3143/// pointers, code hashes, associated comments, etc. All snippets are returned
@@ -34,22 +46,27 @@ error CodelessInstance(address instance);
3446library LibCodeGen {
3547 /// Reverts unless `name` is a Solidity identifier: at least one character,
3648 /// drawn from ASCII letters, digits, `_` and `$`, and not starting with a
37- /// digit. A contract name is such an identifier, and restricting it to one
38- /// is also what makes it safe to interpolate into a path: no identifier
39- /// contains a path separator, and none of them is `.` or `..`.
49+ /// digit. Every name this library interpolates verbatim into generated
50+ /// source or into a path is one, and being an identifier is what makes that
51+ /// interpolation safe. A declaration named by an identifier is the
52+ /// declaration the caller asked for and no other, because no identifier
53+ /// carries a space, a `;` or any other character that ends a declaration or
54+ /// starts another. A path built from an identifier stays a direct child of
55+ /// the directory it is joined to, because no identifier contains a path
56+ /// separator and none of them is `.` or `..`.
4057 /// @param name The name to check.
41- function requireContractName (string memory name ) internal pure {
58+ function requireIdentifier (string memory name ) internal pure {
4259 bytes memory nameBytes = bytes (name);
4360 if (nameBytes.length == 0 ) {
44- revert InvalidContractName (name);
61+ revert InvalidIdentifier (name);
4562 }
4663 for (uint256 i = 0 ; i < nameBytes.length ; i++ ) {
4764 bytes1 char = nameBytes[i];
4865 bool isLetter = (char >= 0x41 && char <= 0x5A ) || (char >= 0x61 && char <= 0x7A );
4966 bool isDigit = char >= 0x30 && char <= 0x39 ;
5067 bool isUnderscoreOrDollar = char == 0x5F || char == 0x24 ;
5168 if (! (isLetter || isUnderscoreOrDollar || (isDigit && i > 0 ))) {
52- revert InvalidContractName (name);
69+ revert InvalidIdentifier (name);
5370 }
5471 }
5572 }
@@ -66,16 +83,58 @@ library LibCodeGen {
6683 return bytes (comment).length == 0 ? "\n " : string .concat ("\n " , comment, "\n " );
6784 }
6885
86+ /// True when `text` can be interpolated into a header line as itself: at
87+ /// least one byte, and no byte that ends a line. Solidity ends a `//`
88+ /// comment at either a line feed or a carriage return, so a value carrying
89+ /// one would close the tag's line and continue as source.
90+ /// @param text The text to check.
91+ /// @return Whether the text is a non-empty single line.
92+ function isSingleLine (string memory text ) internal pure returns (bool ) {
93+ bytes memory textBytes = bytes (text);
94+ if (textBytes.length == 0 ) {
95+ return false ;
96+ }
97+ for (uint256 i = 0 ; i < textBytes.length ; i++ ) {
98+ if (textBytes[i] == 0x0A || textBytes[i] == 0x0D ) {
99+ return false ;
100+ }
101+ }
102+ return true ;
103+ }
104+
69105 /// The file prefix for autogenerated files outlines the license, pragma,
70106 /// and a note about the file being autogenerated. The pragma is ^ as the
71107 /// generated code is expected to be imported into some concrete contract
72108 /// with pragma = version.
73- function filePrefix () internal pure returns (string memory ) {
109+ ///
110+ /// The generated file lands in the calling project's repo, so the licence it
111+ /// is under and the copyright holder it names are the calling project's to
112+ /// state and are taken from the caller. Both are interpolated verbatim into
113+ /// their tags, and both have to be a non-empty single line for the tag they
114+ /// land on to say what it appears to.
115+ /// @param spdxLicenseIdentifier The SPDX licence identifier for the
116+ /// generated file, interpolated verbatim.
117+ /// @param copyrightText The copyright text for the generated file,
118+ /// interpolated verbatim.
119+ /// @return The text that heads the generated file.
120+ function filePrefix (string memory spdxLicenseIdentifier , string memory copyrightText )
121+ internal
122+ pure
123+ returns (string memory )
124+ {
125+ if (! isSingleLine (spdxLicenseIdentifier)) {
126+ revert InvalidSpdxLicenseIdentifier (spdxLicenseIdentifier);
127+ }
128+ if (! isSingleLine (copyrightText)) {
129+ revert InvalidCopyrightText (copyrightText);
130+ }
74131 //REUSE-IgnoreStart
75132 return string .concat (
76- "// SPDX-License-Identifier: LicenseRef-DCL-1.0\n "
77- "// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd\n "
78- "pragma solidity ^0.8.25;\n\n " ,
133+ "// SPDX-License-Identifier: " ,
134+ spdxLicenseIdentifier,
135+ "\n " "// SPDX-FileCopyrightText: " ,
136+ copyrightText,
137+ "\n " "pragma solidity ^0.8.25;\n\n " ,
79138 "// THIS FILE IS AUTOGENERATED BY THE BUILD SCRIPT. DO NOT EDIT BY HAND.\n "
80139 );
81140 //REUSE-IgnoreEnd
@@ -253,7 +312,7 @@ library LibCodeGen {
253312 /// @return A string containing the Solidity code for the described by meta
254313 /// hash constant.
255314 function describedByMetaHashConstantString (Vm vm , string memory name ) internal view returns (string memory ) {
256- requireContractName (name);
315+ requireIdentifier (name);
257316 bytes memory describedByMeta = vm.readFileBinary (string .concat ("meta/ " , name, ".rain.meta " ));
258317 return bytes32ConstantString (
259318 vm,
@@ -269,14 +328,16 @@ library LibCodeGen {
269328 /// @param vm The Vm instance used to format values as strings.
270329 /// @param comment The comment to include above the constant declaration.
271330 /// An empty comment emits no comment line.
272- /// @param name The name of the constant.
331+ /// @param name The name of the constant, interpolated verbatim. Has to be a
332+ /// Solidity identifier.
273333 /// @param data The bytes data for the constant.
274334 /// @return A string containing the Solidity code for the bytes constant.
275335 function bytesConstantString (Vm vm , string memory comment , string memory name , bytes memory data )
276336 internal
277337 pure
278338 returns (string memory )
279339 {
340+ requireIdentifier (name);
280341 string memory hexData = LibHexString.bytesToHex (vm, data);
281342 return string .concat (
282343 commentPrefix (comment),
@@ -297,14 +358,16 @@ library LibCodeGen {
297358 /// @param vm The Vm instance used to format values as strings.
298359 /// @param comment The comment to include above the constant declaration.
299360 /// An empty comment emits no comment line.
300- /// @param name The name of the constant.
361+ /// @param name The name of the constant, interpolated verbatim. Has to be a
362+ /// Solidity identifier.
301363 /// @param data The uint8 data for the constant.
302364 /// @return A string containing the Solidity code for the uint8 constant.
303365 function uint8ConstantString (Vm vm , string memory comment , string memory name , uint8 data )
304366 internal
305367 pure
306368 returns (string memory )
307369 {
370+ requireIdentifier (name);
308371 string memory intString = vm.toString (data);
309372 return string .concat (
310373 commentPrefix (comment),
@@ -325,14 +388,16 @@ library LibCodeGen {
325388 /// @param vm The Vm instance used to format values as strings.
326389 /// @param comment The comment to include above the constant declaration.
327390 /// An empty comment emits no comment line.
328- /// @param name The name of the constant.
391+ /// @param name The name of the constant, interpolated verbatim. Has to be a
392+ /// Solidity identifier.
329393 /// @param data The bytes32 value for the constant.
330394 /// @return A string containing the Solidity code for the bytes32 constant.
331395 function bytes32ConstantString (Vm vm , string memory comment , string memory name , bytes32 data )
332396 internal
333397 pure
334398 returns (string memory )
335399 {
400+ requireIdentifier (name);
336401 string memory hexString = vm.toString (data);
337402 return string .concat (
338403 commentPrefix (comment),
@@ -354,14 +419,16 @@ library LibCodeGen {
354419 /// @param vm The Vm instance used to format values as strings.
355420 /// @param comment The comment to include above the constant declaration.
356421 /// An empty comment emits no comment line.
357- /// @param name The name of the constant.
422+ /// @param name The name of the constant, interpolated verbatim. Has to be a
423+ /// Solidity identifier.
358424 /// @param data The address for the constant.
359425 /// @return A string containing the Solidity code for the address constant.
360426 function addressConstantString (Vm vm , string memory comment , string memory name , address data )
361427 internal
362428 pure
363429 returns (string memory )
364430 {
431+ requireIdentifier (name);
365432 string memory addressString = vm.toString (data);
366433 return string .concat (
367434 commentPrefix (comment),
0 commit comments