Skip to content

Commit

Permalink
Required virtual methods should be required at compile-time
Browse files Browse the repository at this point in the history
  • Loading branch information
TitanNano authored and Bromeon committed Aug 5, 2024
1 parent 6101ea0 commit 5f8da9c
Show file tree
Hide file tree
Showing 20 changed files with 533 additions and 83 deletions.
2 changes: 1 addition & 1 deletion .github/composite/godot-itest/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ runs:
targetArgs="--target $TARGET"
fi
cargo build -p itest ${{ inputs.rust-extra-args }} $targetArgs
cargo build -p itest --no-default-features ${{ inputs.rust-extra-args }} $targetArgs
# Instead of modifying .gdextension, rename the output directory
if [[ -n "$TARGET" ]]; then
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/full-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,15 @@ jobs:
os: ubuntu-20.04
artifact-name: linux-nightly
godot-binary: godot.linuxbsd.editor.dev.x86_64
rust-extra-args: --features godot/__codegen-full
rust-extra-args: --features itest/codegen-full
with-hot-reload: true

# Combines now a lot of features, but should be OK. lazy-function-tables doesn't work with experimental-threads.
- name: linux-double-lazy
os: ubuntu-20.04
artifact-name: linux-double-nightly
godot-binary: godot.linuxbsd.editor.dev.double.x86_64
rust-extra-args: --features godot/api-custom,godot/double-precision,godot/__codegen-full,godot/lazy-function-tables
rust-extra-args: --features godot/api-custom,godot/double-precision,itest/codegen-full,godot/lazy-function-tables

- name: linux-features-experimental
os: ubuntu-20.04
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/minimal-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ jobs:
os: ubuntu-20.04
artifact-name: linux-nightly
godot-binary: godot.linuxbsd.editor.dev.x86_64
rust-extra-args: --features godot/__codegen-full
rust-extra-args: --features itest/codegen-full
with-hot-reload: true

- name: linux-features-experimental
Expand Down
4 changes: 2 additions & 2 deletions check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function cmd_fmt() {
}

function cmd_clippy() {
run cargo clippy --all-targets "${extraCargoArgs[@]}" -- \
run cargo clippy --all-targets "${extraCargoArgs[@]}" --features "itest/codegen-full" -- \
-D clippy::suspicious \
-D clippy::style \
-D clippy::complexity \
Expand All @@ -139,7 +139,7 @@ function cmd_clippy() {
}

function cmd_klippy() {
run cargo clippy --fix --all-targets "${extraCargoArgs[@]}" -- \
run cargo clippy --fix --all-targets "${extraCargoArgs[@]}" --features "itest/codegen-full" -- \
-D clippy::suspicious \
-D clippy::style \
-D clippy::complexity \
Expand Down
1 change: 1 addition & 0 deletions godot-codegen/src/generator/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ fn make_builtin_method_definition(
receiver,
varcall_invocation,
ptrcall_invocation,
is_virtual_required: false,
},
safety_doc,
&TokenStream::new(),
Expand Down
1 change: 1 addition & 0 deletions godot-codegen/src/generator/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ fn make_class_method_definition(
receiver,
varcall_invocation,
ptrcall_invocation,
is_virtual_required: false,
},
None,
cfg_attributes,
Expand Down
10 changes: 7 additions & 3 deletions godot-codegen/src/generator/functions_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub struct FnCode {
pub receiver: FnReceiver,
pub varcall_invocation: TokenStream,
pub ptrcall_invocation: TokenStream,
pub is_virtual_required: bool,
}

pub struct FnDefinition {
Expand Down Expand Up @@ -150,6 +151,11 @@ pub fn make_function_definition(
};

let return_decl = &sig.return_value().decl;
let fn_body = if code.is_virtual_required {
quote! { ; }
} else {
quote! { { unimplemented!() } }
};

let receiver_param = &code.receiver.param;
let primary_function = if sig.is_virtual() {
Expand All @@ -160,9 +166,7 @@ pub fn make_function_definition(
#maybe_unsafe fn #primary_fn_name(
#receiver_param
#( #params, )*
) #return_decl {
unimplemented!()
}
) #return_decl #fn_body
}
} else if sig.is_vararg() {
// Varargs (usually varcall, but not necessarily -- utilities use ptrcall)
Expand Down
1 change: 1 addition & 0 deletions godot-codegen/src/generator/utility_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub(crate) fn make_utility_function_definition(function: &UtilityFunction) -> To
receiver: FnReceiver::global_function(),
varcall_invocation,
ptrcall_invocation,
is_virtual_required: false,
},
None,
&TokenStream::new(),
Expand Down
1 change: 1 addition & 0 deletions godot-codegen/src/generator/virtual_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ fn make_virtual_method(method: &ClassMethod) -> Option<TokenStream> {
// make_return() requests following args, but they are not used for virtual methods. We can provide empty streams.
varcall_invocation: TokenStream::new(),
ptrcall_invocation: TokenStream::new(),
is_virtual_required: method.is_virtual_required(),
},
None,
&TokenStream::new(),
Expand Down
5 changes: 5 additions & 0 deletions godot-codegen/src/models/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ pub struct FunctionCommon {
pub return_value: FnReturn,
pub is_vararg: bool,
pub is_private: bool,
pub is_virtual_required: bool,
pub direction: FnDirection,
}

Expand Down Expand Up @@ -314,6 +315,10 @@ pub trait Function: fmt::Display {
fn direction(&self) -> FnDirection {
self.common().direction
}

fn is_virtual_required(&self) -> bool {
self.common().is_virtual_required
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions godot-codegen/src/models/domain_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ impl BuiltinMethod {
return_value: FnReturn::new(&return_value, ctx),
is_vararg: method.is_vararg,
is_private: special_cases::is_method_private(builtin_name, &method.name),
is_virtual_required: false,
direction: FnDirection::Outbound {
hash: method.hash.expect("hash absent for builtin method"),
},
Expand Down Expand Up @@ -463,6 +464,10 @@ impl ClassMethod {
return_value: FnReturn::new(&method.return_value, ctx),
is_vararg: method.is_vararg,
is_private,
is_virtual_required: special_cases::is_virtual_method_required(
&class_name.rust_ty.to_string(),
rust_method_name,
),
direction,
},
qualifier,
Expand Down Expand Up @@ -511,6 +516,7 @@ impl UtilityFunction {
return_value: FnReturn::new(&return_value, ctx),
is_vararg: function.is_vararg,
is_private: false,
is_virtual_required: false,
direction: FnDirection::Outbound {
hash: function.hash,
},
Expand Down
184 changes: 184 additions & 0 deletions godot-codegen/src/special_cases/special_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,190 @@ pub fn get_interface_extra_docs(trait_name: &str) -> Option<&'static str> {
}
}

pub fn is_virtual_method_required(class_name: &str, method: &str) -> bool {
match (class_name, method) {
("ScriptLanguageExtension", _) => method != "get_doc_comment_delimiters",

("ScriptExtension", "editor_can_reload_from_file")
| ("ScriptExtension", "can_instantiate")
| ("ScriptExtension", "get_base_script")
| ("ScriptExtension", "get_global_name")
| ("ScriptExtension", "inherits_script")
| ("ScriptExtension", "get_instance_base_type")
| ("ScriptExtension", "instance_create")
| ("ScriptExtension", "placeholder_instance_create")
| ("ScriptExtension", "instance_has")
| ("ScriptExtension", "has_source_code")
| ("ScriptExtension", "get_source_code")
| ("ScriptExtension", "set_source_code")
| ("ScriptExtension", "reload")
| ("ScriptExtension", "get_documentation")
| ("ScriptExtension", "has_method")
| ("ScriptExtension", "has_static_method")
| ("ScriptExtension", "get_method_info")
| ("ScriptExtension", "is_tool")
| ("ScriptExtension", "is_valid")
| ("ScriptExtension", "get_language")
| ("ScriptExtension", "has_script_signal")
| ("ScriptExtension", "get_script_signal_list")
| ("ScriptExtension", "has_property_default_value")
| ("ScriptExtension", "get_property_default_value")
| ("ScriptExtension", "update_exports")
| ("ScriptExtension", "get_script_method_list")
| ("ScriptExtension", "get_script_property_list")
| ("ScriptExtension", "get_member_line")
| ("ScriptExtension", "get_constants")
| ("ScriptExtension", "get_members")
| ("ScriptExtension", "is_placeholder_fallback_enabled")
| ("ScriptExtension", "get_rpc_config")
| ("EditorExportPlugin", "customize_resource")
| ("EditorExportPlugin", "customize_scene")
| ("EditorExportPlugin", "get_customization_configuration_hash")
| ("EditorExportPlugin", "get_name")
| ("EditorVcsInterface", _)
| ("MovieWriter", _)
| ("TextServerExtension", "has_feature")
| ("TextServerExtension", "get_name")
| ("TextServerExtension", "get_features")
| ("TextServerExtension", "free_rid")
| ("TextServerExtension", "has")
| ("TextServerExtension", "create_font")
| ("TextServerExtension", "font_set_fixed_size")
| ("TextServerExtension", "font_get_fixed_size")
| ("TextServerExtension", "font_set_fixed_size_scale_mode")
| ("TextServerExtension", "font_get_fixed_size_scale_mode")
| ("TextServerExtension", "font_get_size_cache_list")
| ("TextServerExtension", "font_clear_size_cache")
| ("TextServerExtension", "font_remove_size_cache")
| ("TextServerExtension", "font_set_ascent")
| ("TextServerExtension", "font_get_ascent")
| ("TextServerExtension", "font_set_descent")
| ("TextServerExtension", "font_get_descent")
| ("TextServerExtension", "font_set_underline_position")
| ("TextServerExtension", "font_get_underline_position")
| ("TextServerExtension", "font_set_underline_thickness")
| ("TextServerExtension", "font_get_underline_thickness")
| ("TextServerExtension", "font_set_scale")
| ("TextServerExtension", "font_get_scale")
| ("TextServerExtension", "font_get_texture_count")
| ("TextServerExtension", "font_clear_textures")
| ("TextServerExtension", "font_remove_texture")
| ("TextServerExtension", "font_set_texture_image")
| ("TextServerExtension", "font_get_texture_image")
| ("TextServerExtension", "font_get_glyph_list")
| ("TextServerExtension", "font_clear_glyphs")
| ("TextServerExtension", "font_remove_glyph")
| ("TextServerExtension", "font_get_glyph_advance")
| ("TextServerExtension", "font_set_glyph_advance")
| ("TextServerExtension", "font_get_glyph_offset")
| ("TextServerExtension", "font_set_glyph_offset")
| ("TextServerExtension", "font_get_glyph_size")
| ("TextServerExtension", "font_set_glyph_size")
| ("TextServerExtension", "font_get_glyph_uv_rect")
| ("TextServerExtension", "font_set_glyph_uv_rect")
| ("TextServerExtension", "font_get_glyph_texture_idx")
| ("TextServerExtension", "font_set_glyph_texture_idx")
| ("TextServerExtension", "font_get_glyph_texture_rid")
| ("TextServerExtension", "font_get_glyph_texture_size")
| ("TextServerExtension", "font_get_glyph_index")
| ("TextServerExtension", "font_get_char_from_glyph_index")
| ("TextServerExtension", "font_has_char")
| ("TextServerExtension", "font_get_supported_chars")
| ("TextServerExtension", "font_draw_glyph")
| ("TextServerExtension", "font_draw_glyph_outline")
| ("TextServerExtension", "create_shaped_text")
| ("TextServerExtension", "shaped_text_clear")
| ("TextServerExtension", "shaped_text_add_string")
| ("TextServerExtension", "shaped_text_add_object")
| ("TextServerExtension", "shaped_text_resize_object")
| ("TextServerExtension", "shaped_get_span_count")
| ("TextServerExtension", "shaped_get_span_meta")
| ("TextServerExtension", "shaped_set_span_update_font")
| ("TextServerExtension", "shaped_text_substr")
| ("TextServerExtension", "shaped_text_get_parent")
| ("TextServerExtension", "shaped_text_shape")
| ("TextServerExtension", "shaped_text_is_ready")
| ("TextServerExtension", "shaped_text_get_glyphs")
| ("TextServerExtension", "shaped_text_sort_logical")
| ("TextServerExtension", "shaped_text_get_glyph_count")
| ("TextServerExtension", "shaped_text_get_range")
| ("TextServerExtension", "shaped_text_get_trim_pos")
| ("TextServerExtension", "shaped_text_get_ellipsis_pos")
| ("TextServerExtension", "shaped_text_get_ellipsis_glyphs")
| ("TextServerExtension", "shaped_text_get_ellipsis_glyph_count")
| ("TextServerExtension", "shaped_text_get_objects")
| ("TextServerExtension", "shaped_text_get_object_rect")
| ("TextServerExtension", "shaped_text_get_object_range")
| ("TextServerExtension", "shaped_text_get_object_glyph")
| ("TextServerExtension", "shaped_text_get_size")
| ("TextServerExtension", "shaped_text_get_ascent")
| ("TextServerExtension", "shaped_text_get_descent")
| ("TextServerExtension", "shaped_text_get_width")
| ("TextServerExtension", "shaped_text_get_underline_position")
| ("TextServerExtension", "shaped_text_get_underline_thickness")
| ("AudioStreamPlayback", "mix")
| ("AudioStreamPlaybackResampled", "mix_resampled")
| ("AudioStreamPlaybackResampled", "get_stream_sampling_rate")
| ("AudioEffectInstance", "process")
| ("AudioEffect", "instantiate")
| ("PhysicsDirectBodyState2DExtension", _)
| ("PhysicsDirectBodyState3DExtension", _)
| ("PhysicsDirectSpaceState2DExtension", _)
| ("PhysicsDirectSpaceState3DExtension", _)
| ("PhysicsServer3DExtension", _)
| ("PhysicsServer2DExtension", _)
| ("EditorScript", "run")
| ("VideoStreamPlayback", "update")
| ("EditorFileSystemImportFormatSupportQuery", _)
| ("Mesh", _)
| ("Texture2D", "get_width")
| ("Texture2D", "get_height")
| ("Texture3D", _)
| ("TextureLayered", _)
| ("StyleBox", "draw")
| ("Material", "get_shader_rid")
| ("Material", "get_shader_mode")
| ("PacketPeerExtension", "get_available_packet_count")
| ("PacketPeerExtension", "get_max_packet_size")
| ("StreamPeerExtension", "get_available_bytes")
| ("WebRtcDataChannelExtension", "poll")
| ("WebRtcDataChannelExtension", "close")
| ("WebRtcDataChannelExtension", "set_write_mode")
| ("WebRtcDataChannelExtension", "get_write_mode")
| ("WebRtcDataChannelExtension", "was_string_packet")
| ("WebRtcDataChannelExtension", "get_ready_state")
| ("WebRtcDataChannelExtension", "get_label")
| ("WebRtcDataChannelExtension", "is_ordered")
| ("WebRtcDataChannelExtension", "get_id")
| ("WebRtcDataChannelExtension", "get_max_packet_life_time")
| ("WebRtcDataChannelExtension", "get_max_retransmits")
| ("WebRtcDataChannelExtension", "get_protocol")
| ("WebRtcDataChannelExtension", "is_negotiated")
| ("WebRtcDataChannelExtension", "get_buffered_amount")
| ("WebRtcDataChannelExtension", "get_available_packet_count")
| ("WebRtcDataChannelExtension", "get_max_packet_size")
| ("WebRtcPeerConnectionExtension", _)
| ("MultiplayerPeerExtension", "get_available_packet_count")
| ("MultiplayerPeerExtension", "get_max_packet_size")
| ("MultiplayerPeerExtension", "set_transfer_channel")
| ("MultiplayerPeerExtension", "get_transfer_channel")
| ("MultiplayerPeerExtension", "set_transfer_mode")
| ("MultiplayerPeerExtension", "get_transfer_mode")
| ("MultiplayerPeerExtension", "set_target_peer")
| ("MultiplayerPeerExtension", "get_packet_peer")
| ("MultiplayerPeerExtension", "get_packet_mode")
| ("MultiplayerPeerExtension", "get_packet_channel")
| ("MultiplayerPeerExtension", "is_server")
| ("MultiplayerPeerExtension", "poll")
| ("MultiplayerPeerExtension", "close")
| ("MultiplayerPeerExtension", "disconnect_peer")
| ("MultiplayerPeerExtension", "get_unique_id")
| ("MultiplayerPeerExtension", "get_connection_status") => true,

(_, _) => false,
}
}

#[rustfmt::skip]
pub fn is_class_level_server(class_name: &str) -> bool {
// Unclear on if some of these classes should be registered earlier than `Scene`:
Expand Down
31 changes: 31 additions & 0 deletions godot-core/src/obj/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,37 @@ use self::bounded_ptr_list::BoundedPtrList;
/// // through create_script_instance().
/// create_script_instance(script_instance)
/// }
/// # fn can_instantiate(&self) -> bool { unreachable!() }
/// # fn editor_can_reload_from_file(&mut self) -> bool { unreachable!() }
/// # fn get_base_script(&self) -> Option<Gd<Script>> { unreachable!() }
/// # fn get_global_name(&self) -> StringName { unreachable!() }
/// # fn inherits_script(&self, _script: Gd<Script>) -> bool { unreachable!() }
/// # fn get_instance_base_type(&self) -> StringName { unreachable!() }
/// # unsafe fn placeholder_instance_create(&self, _for_object: Gd<Object>) -> *mut c_void { unreachable!() }
/// # fn instance_has(&self, _object: Gd<Object>) -> bool { unreachable!() }
/// # fn has_source_code(&self) -> bool { unreachable!() }
/// # fn get_source_code(&self) -> GString { unreachable!() }
/// # fn set_source_code(&mut self, _code: GString) { unreachable!() }
/// # fn reload(&mut self, _keep_state: bool) -> Error { unreachable!() }
/// # fn get_documentation(&self) -> Array<Dictionary> { unreachable!() }
/// # fn has_method(&self, _method: StringName) -> bool { unreachable!() }
/// # fn has_static_method(&self, _method: StringName) -> bool { unreachable!() }
/// # fn get_method_info(&self, _method: StringName) -> Dictionary { unreachable!() }
/// # fn is_tool(&self) -> bool { unreachable!() }
/// # fn is_valid(&self) -> bool { unreachable!() }
/// # fn get_language(&self) -> Option<Gd<ScriptLanguage>> { unreachable!() }
/// # fn has_script_signal(&self, _signall: StringName) -> bool { unreachable!() }
/// # fn get_script_signal_list(&self) -> Array<Dictionary> { unreachable!() }
/// # fn has_property_default_value(&self, _property: StringName) -> bool { unreachable!() }
/// # fn get_property_default_value(&self, _property: StringName) -> Variant { unreachable!() }
/// # fn update_exports(&mut self) { unreachable!() }
/// # fn get_script_method_list(&self) -> Array<Dictionary> { unreachable!() }
/// # fn get_script_property_list(&self) -> Array<Dictionary> { unreachable!() }
/// # fn get_member_line(&self, _member: StringName) -> i32 { unreachable!() }
/// # fn get_constants(&self) -> godot::prelude::Dictionary { unreachable!() }
/// # fn get_members(&self) -> godot::prelude::Array<StringName> { unreachable!() }
/// # fn is_placeholder_fallback_enabled(&self) -> bool { unreachable!() }
/// # fn get_rpc_config(&self) -> Variant { unreachable!() }
/// }
/// ```
pub trait ScriptInstance: Sized {
Expand Down
Loading

0 comments on commit 5f8da9c

Please sign in to comment.