Skip to content

Commit 6bc992e

Browse files
committed
add attributes for struct api
1 parent 86b5c58 commit 6bc992e

File tree

69 files changed

+1464
-1036
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1464
-1036
lines changed

third_party/move/move-binary-format/serializer-tests/tests/serializer_tests.rs

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use move_binary_format::{
77
deserializer::DeserializerConfig,
88
file_format::{
99
empty_module, empty_script, AccessKind, AccessSpecifier, AddressIdentifierIndex,
10-
AddressSpecifier, CompiledModule, CompiledScript, FunctionHandle, IdentifierIndex,
11-
ResourceSpecifier, Signature, SignatureIndex, SignatureToken, TableIndex,
10+
AddressSpecifier, CompiledModule, CompiledScript, FunctionAttribute, FunctionHandle,
11+
IdentifierIndex, ResourceSpecifier, Signature, SignatureIndex, SignatureToken, TableIndex,
1212
},
13-
file_format_common::{IDENTIFIER_SIZE_MAX, VERSION_7, VERSION_MAX},
13+
file_format_common::{IDENTIFIER_SIZE_MAX, VERSION_10, VERSION_7, VERSION_MAX},
1414
};
1515
use move_core_types::{
1616
ability::AbilitySet, account_address::AccountAddress, identifier::Identifier,
@@ -137,3 +137,114 @@ fn simple_script_round_trip_version_failure() {
137137
.to_string()
138138
.contains("Access specifiers on scripts not supported"));
139139
}
140+
141+
#[test]
142+
fn test_borrow_field_attributes_round_trip() {
143+
// Create a module with BorrowFieldImmutable and BorrowFieldMutable attributes
144+
let mut module = empty_module();
145+
146+
// Add signatures for function parameters and return values
147+
let sig_unit_idx = SignatureIndex::new(module.signatures.len() as u16);
148+
module.signatures.push(Signature(vec![]));
149+
150+
// Add function names
151+
let func1_name_idx = IdentifierIndex::new(module.identifiers.len() as u16);
152+
module
153+
.identifiers
154+
.push(Identifier::new("borrow_immut").unwrap());
155+
156+
let func2_name_idx = IdentifierIndex::new(module.identifiers.len() as u16);
157+
module
158+
.identifiers
159+
.push(Identifier::new("borrow_mut").unwrap());
160+
161+
// Add function with BorrowFieldImmutable attribute
162+
let func_handle_idx = module.function_handles.len();
163+
module.function_handles.push(FunctionHandle {
164+
module: module.self_handle_idx(),
165+
name: func1_name_idx,
166+
parameters: sig_unit_idx,
167+
return_: sig_unit_idx,
168+
type_parameters: vec![],
169+
access_specifiers: None,
170+
attributes: vec![FunctionAttribute::BorrowFieldImmutable(5)],
171+
});
172+
173+
// Add function with BorrowFieldMutable attribute
174+
module.function_handles.push(FunctionHandle {
175+
module: module.self_handle_idx(),
176+
name: func2_name_idx,
177+
parameters: sig_unit_idx,
178+
return_: sig_unit_idx,
179+
type_parameters: vec![],
180+
access_specifiers: None,
181+
attributes: vec![FunctionAttribute::BorrowFieldMutable(3)],
182+
});
183+
184+
// Serialize with VERSION_10 (attributes supported from VERSION_8+)
185+
let mut serialized = Vec::new();
186+
module
187+
.serialize_for_version(Some(VERSION_10), &mut serialized)
188+
.expect("serialization should work");
189+
190+
// Deserialize and verify
191+
let deserialized = CompiledModule::deserialize_with_config(
192+
&serialized,
193+
&DeserializerConfig::new(VERSION_10, IDENTIFIER_SIZE_MAX),
194+
)
195+
.expect("deserialization should work");
196+
197+
// Verify attributes preserved
198+
assert_eq!(
199+
module.function_handles.len(),
200+
deserialized.function_handles.len()
201+
);
202+
assert_eq!(
203+
module.function_handles[func_handle_idx].attributes,
204+
deserialized.function_handles[func_handle_idx].attributes
205+
);
206+
assert_eq!(
207+
module.function_handles[func_handle_idx + 1].attributes,
208+
deserialized.function_handles[func_handle_idx + 1].attributes
209+
);
210+
211+
// Verify the full module matches
212+
assert_eq!(module, deserialized);
213+
}
214+
215+
#[test]
216+
fn test_borrow_field_attributes_version_gating() {
217+
// Verify that serializing to VERSION_7 rejects attributes
218+
let mut module = empty_module();
219+
220+
// Add signatures
221+
let sig_unit_idx = SignatureIndex::new(module.signatures.len() as u16);
222+
module.signatures.push(Signature(vec![]));
223+
224+
// Add function name
225+
let func_name_idx = IdentifierIndex::new(module.identifiers.len() as u16);
226+
module
227+
.identifiers
228+
.push(Identifier::new("borrow_field").unwrap());
229+
230+
// Add function with BorrowFieldImmutable attribute
231+
module.function_handles.push(FunctionHandle {
232+
module: module.self_handle_idx(),
233+
name: func_name_idx,
234+
parameters: sig_unit_idx,
235+
return_: sig_unit_idx,
236+
type_parameters: vec![],
237+
access_specifiers: None,
238+
attributes: vec![FunctionAttribute::BorrowFieldImmutable(2)],
239+
});
240+
241+
// Attempt to serialize with VERSION_7 (should fail)
242+
let mut serialized = Vec::new();
243+
let err = module
244+
.serialize_for_version(Some(VERSION_7), &mut serialized)
245+
.expect_err("should fail for VERSION_7");
246+
247+
assert!(err
248+
.to_string()
249+
.contains("not supported in bytecode version"));
250+
}

third_party/move/move-binary-format/src/deserializer.rs

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -937,11 +937,7 @@ fn load_function_handle(
937937
None
938938
};
939939

940-
let attributes = if version >= VERSION_8 {
941-
load_function_attributes(cursor)?
942-
} else {
943-
vec![]
944-
};
940+
let attributes = load_function_attributes(version, cursor)?;
945941

946942
Ok(FunctionHandle {
947943
module,
@@ -1060,24 +1056,119 @@ fn load_signature_tokens(cursor: &mut VersionedCursor) -> BinaryLoaderResult<Vec
10601056
}
10611057

10621058
fn load_function_attributes(
1059+
bytecode_version: u32,
10631060
cursor: &mut VersionedCursor,
10641061
) -> BinaryLoaderResult<Vec<FunctionAttribute>> {
1062+
if bytecode_version < VERSION_8 {
1063+
return Ok(vec![]);
1064+
}
10651065
let count = read_uleb_internal(cursor, ATTRIBUTE_COUNT_MAX)?;
10661066
let mut attributes = Vec::with_capacity(count);
10671067
for _ in 0..count {
1068-
attributes.push(load_attribute(cursor)?);
1068+
attributes.push(load_attribute(bytecode_version, cursor)?);
10691069
}
10701070
Ok(attributes)
10711071
}
10721072

1073-
fn load_attribute(cursor: &mut VersionedCursor) -> BinaryLoaderResult<FunctionAttribute> {
1073+
fn deserialize_function_attribute<E, F>(
1074+
bytecode_version: u32,
1075+
attr_loader: F,
1076+
attr_name: &str,
1077+
err_msg: E,
1078+
cursor: &mut VersionedCursor,
1079+
) -> BinaryLoaderResult<FunctionAttribute>
1080+
where
1081+
F: Fn(&mut VersionedCursor) -> BinaryLoaderResult<FunctionAttribute>,
1082+
E: Fn(&str) -> String,
1083+
{
1084+
if bytecode_version < VERSION_10 {
1085+
Err(PartialVMError::new(StatusCode::MALFORMED).with_message(err_msg(attr_name)))
1086+
} else {
1087+
attr_loader(cursor)
1088+
}
1089+
}
1090+
1091+
fn load_attribute(
1092+
bytecode_version: u32,
1093+
cursor: &mut VersionedCursor,
1094+
) -> BinaryLoaderResult<FunctionAttribute> {
10741095
use SerializedFunctionAttribute::*;
1075-
Ok(
1076-
match SerializedFunctionAttribute::from_u8(load_u8(cursor)?)? {
1077-
PERSISTENT => FunctionAttribute::Persistent,
1078-
MODULE_LOCK => FunctionAttribute::ModuleLock,
1079-
},
1080-
)
1096+
if bytecode_version < VERSION_8 {
1097+
return Err(
1098+
PartialVMError::new(StatusCode::MALFORMED).with_message(format!(
1099+
"Attribute not supported in bytecode version {}",
1100+
bytecode_version
1101+
)),
1102+
);
1103+
}
1104+
let err_msg = |attr: &str| {
1105+
format!(
1106+
"Attribute {} not supported in bytecode version {}",
1107+
attr, bytecode_version
1108+
)
1109+
};
1110+
1111+
match SerializedFunctionAttribute::from_u8(load_u8(cursor)?)? {
1112+
PERSISTENT => Ok(FunctionAttribute::Persistent),
1113+
MODULE_LOCK => Ok(FunctionAttribute::ModuleLock),
1114+
PACK => deserialize_function_attribute(
1115+
bytecode_version,
1116+
|_cursor| Ok(FunctionAttribute::Pack),
1117+
"pack",
1118+
err_msg,
1119+
cursor,
1120+
),
1121+
PACK_VARIANT => deserialize_function_attribute(
1122+
bytecode_version,
1123+
|cursor| Ok(FunctionAttribute::PackVariant(read_u16_internal(cursor)?)),
1124+
"pack_variant",
1125+
err_msg,
1126+
cursor,
1127+
),
1128+
UNPACK => deserialize_function_attribute(
1129+
bytecode_version,
1130+
|_cursor| Ok(FunctionAttribute::Unpack),
1131+
"unpack",
1132+
err_msg,
1133+
cursor,
1134+
),
1135+
UNPACK_VARIANT => deserialize_function_attribute(
1136+
bytecode_version,
1137+
|cursor| Ok(FunctionAttribute::UnpackVariant(read_u16_internal(cursor)?)),
1138+
"unpack_variant",
1139+
err_msg,
1140+
cursor,
1141+
),
1142+
TEST_VARIANT => deserialize_function_attribute(
1143+
bytecode_version,
1144+
|cursor| Ok(FunctionAttribute::TestVariant(read_u16_internal(cursor)?)),
1145+
"test_variant",
1146+
err_msg,
1147+
cursor,
1148+
),
1149+
BORROW_FIELD_IMMUTABLE => deserialize_function_attribute(
1150+
bytecode_version,
1151+
|cursor| {
1152+
Ok(FunctionAttribute::BorrowFieldImmutable(read_u16_internal(
1153+
cursor,
1154+
)?))
1155+
},
1156+
"borrow",
1157+
err_msg,
1158+
cursor,
1159+
),
1160+
BORROW_FIELD_MUTABLE => deserialize_function_attribute(
1161+
bytecode_version,
1162+
|cursor| {
1163+
Ok(FunctionAttribute::BorrowFieldMutable(read_u16_internal(
1164+
cursor,
1165+
)?))
1166+
},
1167+
"borrow_mut",
1168+
err_msg,
1169+
cursor,
1170+
),
1171+
}
10811172
}
10821173

10831174
fn load_access_specifiers(
@@ -2260,8 +2351,15 @@ impl SerializedFunctionAttribute {
22602351
match value {
22612352
0x1 => Ok(PERSISTENT),
22622353
0x2 => Ok(MODULE_LOCK),
2354+
0x3 => Ok(PACK),
2355+
0x4 => Ok(PACK_VARIANT),
2356+
0x5 => Ok(UNPACK),
2357+
0x6 => Ok(UNPACK_VARIANT),
2358+
0x7 => Ok(TEST_VARIANT),
2359+
0x8 => Ok(BORROW_FIELD_IMMUTABLE),
2360+
0x9 => Ok(BORROW_FIELD_MUTABLE),
22632361
_ => Err(PartialVMError::new(StatusCode::MALFORMED)
2264-
.with_message("malformed attribute".to_owned())),
2362+
.with_message(format!("malformed attribute: {}", value))),
22652363
}
22662364
}
22672365
}

third_party/move/move-binary-format/src/file_format.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -368,18 +368,50 @@ pub enum FunctionAttribute {
368368
Persistent,
369369
/// During execution of the function, a module reentrancy lock is established.
370370
ModuleLock,
371+
/// Attribute for the API packing a non-private struct.
372+
Pack,
373+
/// Attribute for the API packing a non-private enum.
374+
PackVariant(VariantIndex),
375+
/// Attribute for the API unpacking a non-private struct.
376+
Unpack,
377+
/// Attribute for the API unpacking a non-private enum.
378+
UnpackVariant(VariantIndex),
379+
/// Attribute for the API testing variants for a non-private enum.
380+
TestVariant(VariantIndex),
381+
/// Attribute for the API immutably borrowing a field from a non-private struct or enum.
382+
/// MemberCount represents the offset of the field.
383+
BorrowFieldImmutable(MemberCount),
384+
/// Attribute for the API mutably borrowing a field from a non-private struct or enum.
385+
/// MemberCount represents the offset of the field.
386+
BorrowFieldMutable(MemberCount),
371387
}
372388

373389
impl FunctionAttribute {
374390
/// Returns true if the attributes in `with` are compatible with
375391
/// the attributes in `this`. Typically, `this` is an imported
376392
/// function handle and `with` the matching definition. Currently,
377-
/// only the `Persistent` attribute is relevant for this check.
393+
/// `persistent` and all non-private struct related attributes are relevant for this check.
378394
pub fn is_compatible_with(this: &[Self], with: &[Self]) -> bool {
379-
if this.contains(&FunctionAttribute::Persistent) {
380-
with.contains(&FunctionAttribute::Persistent)
381-
} else {
382-
true
395+
for attr in this {
396+
if attr.cannot_be_removed() && !with.contains(attr) {
397+
return false;
398+
}
399+
}
400+
true
401+
}
402+
403+
/// Returns true if the attribute cannot be removed from the function handle once added.
404+
pub fn cannot_be_removed(&self) -> bool {
405+
match self {
406+
FunctionAttribute::ModuleLock => false,
407+
FunctionAttribute::Persistent
408+
| FunctionAttribute::Pack
409+
| FunctionAttribute::PackVariant(_)
410+
| FunctionAttribute::Unpack
411+
| FunctionAttribute::UnpackVariant(_)
412+
| FunctionAttribute::TestVariant(_)
413+
| FunctionAttribute::BorrowFieldImmutable(_)
414+
| FunctionAttribute::BorrowFieldMutable(_) => true,
383415
}
384416
}
385417
}
@@ -389,6 +421,27 @@ impl fmt::Display for FunctionAttribute {
389421
match self {
390422
FunctionAttribute::Persistent => write!(f, "persistent"),
391423
FunctionAttribute::ModuleLock => write!(f, "module_lock"),
424+
FunctionAttribute::Pack => {
425+
write!(f, "pack")
426+
},
427+
FunctionAttribute::PackVariant(_) => {
428+
write!(f, "pack_variant")
429+
},
430+
FunctionAttribute::Unpack => {
431+
write!(f, "unpack")
432+
},
433+
FunctionAttribute::UnpackVariant(_) => {
434+
write!(f, "unpack_variant")
435+
},
436+
FunctionAttribute::TestVariant(_) => {
437+
write!(f, "test_variant")
438+
},
439+
FunctionAttribute::BorrowFieldImmutable(_) => {
440+
write!(f, "borrow")
441+
},
442+
FunctionAttribute::BorrowFieldMutable(_) => {
443+
write!(f, "borrow_mut")
444+
},
392445
}
393446
}
394447
}

third_party/move/move-binary-format/src/file_format_common.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ pub enum SerializedNativeStructFlag {
222222
pub enum SerializedFunctionAttribute {
223223
PERSISTENT = 0x1,
224224
MODULE_LOCK = 0x2,
225+
PACK = 0x3,
226+
PACK_VARIANT = 0x4,
227+
UNPACK = 0x5,
228+
UNPACK_VARIANT = 0x6,
229+
TEST_VARIANT = 0x7,
230+
BORROW_FIELD_IMMUTABLE = 0x8,
231+
BORROW_FIELD_MUTABLE = 0x9,
225232
}
226233

227234
/// List of opcodes constants.
@@ -556,6 +563,7 @@ pub const VERSION_9: u32 = 9;
556563

557564
/// Version 10: changes compared to version 9
558565
/// + abort with message instruction
566+
/// + new attributes for structs api
559567
pub const VERSION_10: u32 = 10;
560568

561569
/// Mark which oldest version is supported.

0 commit comments

Comments
 (0)