diff options
Diffstat (limited to 'modules')
51 files changed, 838 insertions, 464 deletions
diff --git a/modules/astcenc/image_compress_astcenc.cpp b/modules/astcenc/image_compress_astcenc.cpp index ce10201343..1c643d780d 100644 --- a/modules/astcenc/image_compress_astcenc.cpp +++ b/modules/astcenc/image_compress_astcenc.cpp @@ -35,7 +35,7 @@ #include <astcenc.h> -void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format) { +void _compress_astc(Image *r_img, Image::ASTCFormat p_format) { uint64_t start_time = OS::get_singleton()->get_ticks_msec(); // TODO: See how to handle lossy quality. @@ -83,65 +83,91 @@ void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_for const bool mipmaps = r_img->has_mipmaps(); int width = r_img->get_width(); int height = r_img->get_height(); + int required_width = (width % block_x) != 0 ? width + (block_x - (width % block_x)) : width; + int required_height = (height % block_y) != 0 ? height + (block_y - (height % block_y)) : height; + + if (width != required_width || height != required_height) { + // Resize texture to fit block size. + r_img->resize(required_width, required_height); + width = required_width; + height = required_height; + } print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : "")); // Initialize astcenc. + int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); + Vector<uint8_t> dest_data; + dest_data.resize(dest_size); + uint8_t *dest_write = dest_data.ptrw(); + astcenc_config config; config.block_x = block_x; config.block_y = block_y; config.profile = profile; - const float quality = ASTCENC_PRE_MEDIUM; - astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); + const float quality = ASTCENC_PRE_MEDIUM; + astcenc_error status = astcenc_config_init(profile, block_x, block_y, 1, quality, 0, &config); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); // Context allocation. astcenc_context *context; - const unsigned int thread_count = OS::get_singleton()->get_processor_count(); - + const unsigned int thread_count = 1; // Godot compresses multiple images each on a thread, which is more efficient for large amount of images imported. status = astcenc_context_alloc(&config, thread_count, &context); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); - // Compress image. - Vector<uint8_t> image_data = r_img->get_data(); - uint8_t *slices = image_data.ptrw(); - astcenc_image image; - image.dim_x = width; - image.dim_y = height; - image.dim_z = 1; - image.data_type = ASTCENC_TYPE_U8; - if (is_hdr) { - image.data_type = ASTCENC_TYPE_F32; - } - image.data = reinterpret_cast<void **>(&slices); + int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; + for (int i = 0; i < mip_count + 1; i++) { + int src_mip_w, src_mip_h; + int src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h); - // Compute the number of ASTC blocks in each dimension. - unsigned int block_count_x = (width + block_x - 1) / block_x; - unsigned int block_count_y = (height + block_y - 1) / block_y; - size_t comp_len = block_count_x * block_count_y * 16; + const uint8_t *slices = &image_data.ptr()[src_ofs]; - Vector<uint8_t> compressed_data; - compressed_data.resize(comp_len); - compressed_data.fill(0); + int dst_mip_w, dst_mip_h; + int dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h); + // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer). + ERR_FAIL_COND(dst_ofs % 8 != 0); + uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs]; - const astcenc_swizzle swizzle = { - ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A - }; + // Compress image. - status = astcenc_compress_image(context, &image, &swizzle, compressed_data.ptrw(), comp_len, 0); - ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, - vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status))); + astcenc_image image; + image.dim_x = src_mip_w; + image.dim_y = src_mip_h; + image.dim_z = 1; + image.data_type = ASTCENC_TYPE_U8; + if (is_hdr) { + image.data_type = ASTCENC_TYPE_F32; + } + image.data = (void **)(&slices); + + // Compute the number of ASTC blocks in each dimension. + unsigned int block_count_x = (src_mip_w + block_x - 1) / block_x; + unsigned int block_count_y = (src_mip_h + block_y - 1) / block_y; + size_t comp_len = block_count_x * block_count_y * 16; + + const astcenc_swizzle swizzle = { + ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A + }; + + status = astcenc_compress_image(context, &image, &swizzle, dest_mip_write, comp_len, 0); + + ERR_BREAK_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status))); + astcenc_compress_reset(context); + } + + astcenc_context_free(context); // Replace original image with compressed one. - r_img->set_data(width, height, mipmaps, target_format, compressed_data); + r_img->set_data(width, height, mipmaps, target_format, dest_data); print_verbose(vformat("astcenc: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } @@ -184,68 +210,81 @@ void _decompress_astc(Image *r_img) { astcenc_config config; const float quality = ASTCENC_PRE_MEDIUM; - astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); + astcenc_error status = astcenc_config_init(profile, block_x, block_y, 1, quality, 0, &config); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); // Context allocation. astcenc_context *context = nullptr; - const unsigned int thread_count = OS::get_singleton()->get_processor_count(); + const unsigned int thread_count = 1; status = astcenc_context_alloc(&config, thread_count, &context); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); - // Decompress image. + Image::Format target_format = is_hdr ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8; const bool mipmaps = r_img->has_mipmaps(); int width = r_img->get_width(); int height = r_img->get_height(); + int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); + Vector<uint8_t> dest_data; + dest_data.resize(dest_size); + uint8_t *dest_write = dest_data.ptrw(); - astcenc_image image; - image.dim_x = width; - image.dim_y = height; - image.dim_z = 1; - image.data_type = ASTCENC_TYPE_U8; - Image::Format target_format = Image::FORMAT_RGBA8; - if (is_hdr) { - target_format = Image::FORMAT_RGBAF; - image.data_type = ASTCENC_TYPE_F32; - } + // Decompress image. Vector<uint8_t> image_data = r_img->get_data(); + int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; - Vector<uint8_t> new_image_data; - new_image_data.resize(Image::get_image_data_size(width, height, target_format, false)); - new_image_data.fill(0); - uint8_t *slices = new_image_data.ptrw(); - image.data = reinterpret_cast<void **>(&slices); + for (int i = 0; i < mip_count + 1; i++) { + int src_mip_w, src_mip_h; + + int src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h); + const uint8_t *src_data = &image_data.ptr()[src_ofs]; + int src_size; + if (i == mip_count) { + src_size = image_data.size() - src_ofs; + } else { + int auxw, auxh; + src_size = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i + 1, auxw, auxh) - src_ofs; + } - const astcenc_swizzle swizzle = { - ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A - }; + int dst_mip_w, dst_mip_h; + int dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h); + // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer). + ERR_FAIL_COND(dst_ofs % 8 != 0); + uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs]; + + astcenc_image image; + image.dim_x = dst_mip_w; + image.dim_y = dst_mip_h; + image.dim_z = 1; + image.data_type = ASTCENC_TYPE_U8; + if (is_hdr) { + target_format = Image::FORMAT_RGBAF; + image.data_type = ASTCENC_TYPE_F32; + } - status = astcenc_decompress_image(context, image_data.ptr(), image_data.size(), &image, &swizzle, 0); - ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, - vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status))); - ERR_FAIL_COND_MSG(image.dim_z > 1, - "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported."); + image.data = (void **)(&dest_mip_write); - // Replace original image with compressed one. + const astcenc_swizzle swizzle = { + ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A + }; - Image::Format image_format = Image::FORMAT_RGBA8; - if (image.data_type == ASTCENC_TYPE_F32) { - image_format = Image::FORMAT_RGBAF; - } else if (image.data_type == ASTCENC_TYPE_U8) { - image_format = Image::FORMAT_RGBA8; - } else if (image.data_type == ASTCENC_TYPE_F16) { - image_format = Image::FORMAT_RGBAH; - } else { - ERR_FAIL_MSG("astcenc: ASTC decompression failed with an unknown format."); + status = astcenc_decompress_image(context, src_data, src_size, &image, &swizzle, 0); + ERR_BREAK_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status))); + ERR_BREAK_MSG(image.dim_z > 1, + "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported."); + astcenc_compress_reset(context); } + astcenc_context_free(context); + + // Replace original image with compressed one. - r_img->set_data(image.dim_x, image.dim_y, mipmaps, image_format, new_image_data); + r_img->set_data(width, height, mipmaps, target_format, dest_data); print_verbose(vformat("astcenc: Decompression took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } diff --git a/modules/astcenc/image_compress_astcenc.h b/modules/astcenc/image_compress_astcenc.h index a197a91e0d..ad157d7c0a 100644 --- a/modules/astcenc/image_compress_astcenc.h +++ b/modules/astcenc/image_compress_astcenc.h @@ -33,7 +33,7 @@ #include "core/io/image.h" -void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format); +void _compress_astc(Image *r_img, Image::ASTCFormat p_format); void _decompress_astc(Image *r_img); #endif // IMAGE_COMPRESS_ASTCENC_H diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 9cc3d0413d..13c7a8202c 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -558,7 +558,7 @@ void CSGShape3D::_notification(int p_what) { set_collision_layer(collision_layer); set_collision_mask(collision_mask); set_collision_priority(collision_priority); - _update_collision_faces(); + _make_dirty(); } } break; @@ -1763,7 +1763,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { } } - if (!path) { + if (!path || !path->is_inside_tree()) { return new_brush; } diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 4982b6b995..f19228cb18 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -129,7 +129,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const } } -void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels) { +void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) { return; //do not compress, already compressed } diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h index 5dc8b6f52c..ca88a9d4c9 100644 --- a/modules/cvtt/image_compress_cvtt.h +++ b/modules/cvtt/image_compress_cvtt.h @@ -33,7 +33,7 @@ #include "core/io/image.h" -void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels); +void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels); void image_decompress_cvtt(Image *p_image); #endif // IMAGE_COMPRESS_CVTT_H diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index a6aeec54cc..16a59d3880 100644 --- a/modules/etcpak/image_compress_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -74,25 +74,23 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) { } } -void _compress_etc1(Image *r_img, float p_lossy_quality) { - _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img, p_lossy_quality); +void _compress_etc1(Image *r_img) { + _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img); } -void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { +void _compress_etc2(Image *r_img, Image::UsedChannels p_channels) { EtcpakType type = _determine_etc_type(p_channels); - _compress_etcpak(type, r_img, p_lossy_quality); + _compress_etcpak(type, r_img); } -void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { +void _compress_bc(Image *r_img, Image::UsedChannels p_channels) { EtcpakType type = _determine_dxt_type(p_channels); - _compress_etcpak(type, r_img, p_lossy_quality); + _compress_etcpak(type, r_img); } -void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality) { +void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) { uint64_t start_time = OS::get_singleton()->get_ticks_msec(); - // TODO: See how to handle lossy quality. - Image::Format img_format = r_img->get_format(); if (img_format >= Image::FORMAT_DXT1) { return; // Do not compress, already compressed. diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h index 8cb17b1c8a..ff267631a6 100644 --- a/modules/etcpak/image_compress_etcpak.h +++ b/modules/etcpak/image_compress_etcpak.h @@ -43,10 +43,10 @@ enum class EtcpakType { ETCPAK_TYPE_DXT5_RA_AS_RG, }; -void _compress_etc1(Image *r_img, float p_lossy_quality); -void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); -void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); +void _compress_etc1(Image *r_img); +void _compress_etc2(Image *r_img, Image::UsedChannels p_channels); +void _compress_bc(Image *r_img, Image::UsedChannels p_channels); -void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality); +void _compress_etcpak(EtcpakType p_compresstype, Image *r_img); #endif // IMAGE_COMPRESS_ETCPAK_H diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d9b8a540c0..a8667e9cec 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -706,11 +706,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc } members_cache.push_back(member.variable->export_info); - Variant default_value; - if (member.variable->initializer && member.variable->initializer->is_constant) { - default_value = member.variable->initializer->reduced_value; - GDScriptCompiler::convert_to_initializer_type(default_value, member.variable); - } + Variant default_value = analyzer.make_variable_default_value(member.variable); member_default_values_cache[member.variable->identifier->name] = default_value; } break; case GDScriptParser::ClassNode::Member::SIGNAL: { @@ -1525,41 +1521,24 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name); if (E) { const GDScript::MemberInfo *member = &E->value; - if (member->setter) { - const Variant *val = &p_value; + Variant value = p_value; + if (member->data_type.has_type && !member->data_type.is_type(value)) { + const Variant *args = &p_value; Callable::CallError err; - callp(member->setter, &val, 1, err); - if (err.error == Callable::CallError::CALL_OK) { - return true; //function exists, call was successful - } else { + Variant::construct(member->data_type.builtin_type, value, &args, 1, err); + if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { return false; } + } + if (member->setter) { + const Variant *args = &value; + Callable::CallError err; + callp(member->setter, &args, 1, err); + return err.error == Callable::CallError::CALL_OK; } else { - if (member->data_type.has_type) { - if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) { - // Typed array. - if (p_value.get_type() == Variant::ARRAY) { - return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value); - } else { - return false; - } - } else if (!member->data_type.is_type(p_value)) { - // Try conversion - Callable::CallError ce; - const Variant *value = &p_value; - Variant converted; - Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce); - if (ce.error == Callable::CallError::CALL_OK) { - members.write[member->index] = converted; - return true; - } else { - return false; - } - } - } - members.write[member->index] = p_value; + members.write[member->index] = value; + return true; } - return true; } } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 8dd65a700a..f788236af3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -580,6 +580,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type if (result.builtin_type == Variant::ARRAY) { GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type)); if (container_type.kind != GDScriptParser::DataType::VARIANT) { + container_type.is_constant = false; result.set_container_element_type(container_type); } } @@ -1571,18 +1572,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer); - if ((p_assignable->infer_datatype && array->elements.size() > 0) || (has_specified_type && specified_type.has_container_element_type())) { - update_array_literal_element_type(specified_type, array); + if (has_specified_type && specified_type.has_container_element_type()) { + update_array_literal_element_type(array, specified_type.get_container_element_type()); } } - if (is_constant) { - if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer), true); - } else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer), true); - } - if (!p_assignable->initializer->is_constant) { + if (is_constant && !p_assignable->initializer->is_constant) { + bool is_initializer_value_reduced = false; + Variant initializer_value = make_expression_reduced_value(p_assignable->initializer, is_initializer_value_reduced); + if (is_initializer_value_reduced) { + p_assignable->initializer->is_constant = true; + p_assignable->initializer->reduced_value = initializer_value; + } else { push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer); } } @@ -1630,6 +1631,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi } else { push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer); } + } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) { + mark_node_unsafe(p_assignable->initializer); #ifdef DEBUG_ENABLED } else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) { parser->push_warning(p_assignable->initializer, GDScriptWarning::NARROWING_CONVERSION); @@ -1970,11 +1973,8 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { if (p_return->return_value != nullptr) { reduce_expression(p_return->return_value); - if (p_return->return_value->type == GDScriptParser::Node::ARRAY) { - // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) { - update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); - } + if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type()); } if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) { push_error("A void function cannot return a value.", p_return); @@ -2183,49 +2183,26 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr // When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed. // This function determines which type is that (if any). -void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal) { - GDScriptParser::DataType array_type = p_array_literal->get_datatype(); - if (p_array_literal->elements.size() == 0) { - // Empty array literal, just make the same type as the storage. - array_type.set_container_element_type(p_base_type.get_container_element_type()); - } else { - // Check if elements match. - bool all_same_type = true; - bool all_have_type = true; - - GDScriptParser::DataType element_type; - for (int i = 0; i < p_array_literal->elements.size(); i++) { - if (i == 0) { - element_type = p_array_literal->elements[0]->get_datatype(); - } else { - GDScriptParser::DataType this_element_type = p_array_literal->elements[i]->get_datatype(); - if (this_element_type.has_no_type()) { - all_same_type = false; - all_have_type = false; - break; - } else if (element_type != this_element_type) { - if (!is_type_compatible(element_type, this_element_type, false)) { - if (is_type_compatible(this_element_type, element_type, false)) { - // This element is a super-type to the previous type, so we use the super-type. - element_type = this_element_type; - } else { - // It's incompatible. - all_same_type = false; - break; - } - } - } - } +void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) { + for (int i = 0; i < p_array->elements.size(); i++) { + GDScriptParser::ExpressionNode *element_node = p_array->elements[i]; + if (element_node->is_constant) { + update_const_expression_builtin_type(element_node, p_element_type, "include"); + } + const GDScriptParser::DataType &element_type = element_node->get_datatype(); + if (element_type.has_no_type() || element_type.is_variant() || !element_type.is_hard_type()) { + mark_node_unsafe(element_node); + continue; } - if (all_same_type) { - element_type.is_constant = false; - array_type.set_container_element_type(element_type); - } else if (all_have_type) { - push_error(vformat(R"(Variant array is not compatible with an array of type "%s".)", p_base_type.get_container_element_type().to_string()), p_array_literal); + if (!is_type_compatible(p_element_type, element_type, true, p_array)) { + push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", element_type.to_string(), p_element_type.to_string()), element_node); + return; } } - // Update the type on the value itself. - p_array_literal->set_datatype(array_type); + + GDScriptParser::DataType array_type = p_array->get_datatype(); + array_type.set_container_element_type(p_element_type); + p_array->set_datatype(array_type); } void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) { @@ -2243,8 +2220,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) { - update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value)); + if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type()); } if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) { @@ -2322,6 +2299,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig // weak non-variant assignee and incompatible result downgrades_assignee = true; } + } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) { + // typed array assignee and untyped array result + mark_node_unsafe(p_assignment); } } } @@ -2822,7 +2802,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; if (index < par_types.size() && par_types[index].has_container_element_type()) { - update_array_literal_element_type(par_types[index], E.value); + update_array_literal_element_type(E.value, par_types[index].get_container_element_type()); } } validate_call_arg(par_types, default_arg_count, is_vararg, p_call); @@ -2933,6 +2913,10 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } } + if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type()); + } + if (!cast_type.is_variant()) { GDScriptParser::DataType op_type = p_cast->operand->get_datatype(); if (op_type.is_variant() || !op_type.is_hard_type()) { @@ -3038,10 +3022,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide p_identifier->set_datatype(p_identifier_datatype); Error err = OK; - GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr(); - ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path)); - scr = scr->find_class(p_identifier_datatype.class_type->fqcn); - p_identifier->reduced_value = scr; + Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err); + if (err) { + push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier); + return; + } + p_identifier->reduced_value = scr->find_class(p_identifier_datatype.class_type->fqcn); p_identifier->is_constant = true; } @@ -3585,12 +3571,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); } else { reduce_expression(p_subscript->base); - - if (p_subscript->base->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base), false); - } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base), false); - } } GDScriptParser::DataType result_type; @@ -3915,58 +3895,146 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) p_unary_op->set_datatype(result); } -void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) { +Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) { + Variant value; + + if (p_expression->is_constant) { + is_reduced = true; + value = p_expression->reduced_value; + } else if (p_expression->type == GDScriptParser::Node::ARRAY) { + value = make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), is_reduced); + } else if (p_expression->type == GDScriptParser::Node::DICTIONARY) { + value = make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), is_reduced); + } else if (p_expression->type == GDScriptParser::Node::SUBSCRIPT) { + value = make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), is_reduced); + } + + return value; +} + +Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) { + Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array(); + + array.resize(p_array->elements.size()); for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; - if (element->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element), p_is_const); - } else if (element->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const); + bool is_element_value_reduced = false; + Variant element_value = make_expression_reduced_value(element, is_element_value_reduced); + if (!is_element_value_reduced) { + return Variant(); } - if (!element->is_constant) { - return; - } + array[i] = element_value; } - Array array; - array.resize(p_array->elements.size()); - for (int i = 0; i < p_array->elements.size(); i++) { - array[i] = p_array->elements[i]->reduced_value; - } - if (p_is_const) { - array.make_read_only(); - } - p_array->is_constant = true; - p_array->reduced_value = array; + array.make_read_only(); + + is_reduced = true; + return array; } -void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) { +Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced) { + Dictionary dictionary; + for (int i = 0; i < p_dictionary->elements.size(); i++) { const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; - if (element.value->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value), p_is_const); - } else if (element.value->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const); + bool is_element_key_reduced = false; + Variant element_key = make_expression_reduced_value(element.key, is_element_key_reduced); + if (!is_element_key_reduced) { + return Variant(); } - if (!element.key->is_constant || !element.value->is_constant) { - return; + bool is_element_value_reduced = false; + Variant element_value = make_expression_reduced_value(element.value, is_element_value_reduced); + if (!is_element_value_reduced) { + return Variant(); } + + dictionary[element_key] = element_value; } - Dictionary dict; - for (int i = 0; i < p_dictionary->elements.size(); i++) { - const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; - dict[element.key->reduced_value] = element.value->reduced_value; + dictionary.make_read_only(); + + is_reduced = true; + return dictionary; +} + +Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced) { + bool is_base_value_reduced = false; + Variant base_value = make_expression_reduced_value(p_subscript->base, is_base_value_reduced); + if (!is_base_value_reduced) { + return Variant(); + } + + if (p_subscript->is_attribute) { + bool is_valid = false; + Variant value = base_value.get_named(p_subscript->attribute->name, is_valid); + if (is_valid) { + is_reduced = true; + return value; + } else { + return Variant(); + } + } else { + bool is_index_value_reduced = false; + Variant index_value = make_expression_reduced_value(p_subscript->index, is_index_value_reduced); + if (!is_index_value_reduced) { + return Variant(); + } + + bool is_valid = false; + Variant value = base_value.get(index_value, &is_valid); + if (is_valid) { + is_reduced = true; + return value; + } else { + return Variant(); + } + } +} + +Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node) { + Array array; + + Ref<Script> script_type = p_element_datatype.script_type; + if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) { + Error err = OK; + Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err); + if (err) { + push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node); + return array; + } + script_type.reference_ptr(scr->find_class(p_element_datatype.class_type->fqcn)); } - if (p_is_const) { - dict.make_read_only(); + + array.set_typed(p_element_datatype.builtin_type, p_element_datatype.native_type, script_type); + + return array; +} + +Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNode *p_variable) { + Variant result = Variant(); + + if (p_variable->initializer) { + bool is_initializer_value_reduced = false; + Variant initializer_value = make_expression_reduced_value(p_variable->initializer, is_initializer_value_reduced); + if (is_initializer_value_reduced) { + result = initializer_value; + } + } else { + GDScriptParser::DataType datatype = p_variable->get_datatype(); + if (datatype.is_hard_type() && datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) { + if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) { + result = make_array_from_element_datatype(datatype.get_container_element_type()); + } else { + VariantInternal::initialize(&result, datatype.builtin_type); + } + } } - p_dictionary->is_constant = true; - p_dictionary->reduced_value = dict; + + return result; } GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { @@ -4437,14 +4505,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ } if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) { // Check the element type. - if (p_target.has_container_element_type()) { - if (!p_source.has_container_element_type()) { - // TODO: Maybe this is valid but unsafe? - // Variant array can't be appended to typed array. - valid = false; - } else { - valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), p_allow_implicit_conversion); - } + if (p_target.has_container_element_type() && p_source.has_container_element_type()) { + valid = p_target.get_container_element_type() == p_source.get_container_element_type(); } } return valid; @@ -4546,7 +4608,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ return ClassDB::is_parent_class(src_native, GDScript::get_class_static()); } while (src_class != nullptr) { - if (src_class->fqcn == p_target.class_type->fqcn) { + if (src_class == p_target.class_type || src_class->fqcn == p_target.class_type->fqcn) { return true; } src_class = src_class->base_type.class_type; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index cd2c4c6569..613399fb40 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -102,10 +102,13 @@ class GDScriptAnalyzer { void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); - void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const); - void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const); + Variant make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced); + Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced); + Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced); + Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced); // Helpers. + Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr); GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type); GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const; @@ -117,7 +120,7 @@ class GDScriptAnalyzer { GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source); GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source); void update_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast = false); - void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal); + void update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type); bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr); void mark_node_unsafe(const GDScriptParser::Node *p_node); @@ -125,7 +128,7 @@ class GDScriptAnalyzer { void mark_lambda_use_self(); bool class_exists(const StringName &p_class) const; Ref<GDScriptParserRef> get_parser_for(const String &p_path); - static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); + void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); #ifdef DEBUG_ENABLED bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context); #endif @@ -137,6 +140,8 @@ public: Error resolve_dependencies(); Error analyze(); + Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable); + GDScriptAnalyzer(GDScriptParser *p_parser); }; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index e19dda090e..ec7a2b0f1c 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -826,9 +826,13 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta switch (p_target.type.kind) { case GDScriptDataType::BUILTIN: { if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { + const GDScriptDataType &element_type = p_target.type.get_container_element_type(); append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY); append(p_target); append(p_source); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); + append(element_type.native_type); } else { append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); append(p_target); @@ -868,9 +872,13 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) { if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { + const GDScriptDataType &element_type = p_target.type.get_container_element_type(); append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY); append(p_target); append(p_source); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); + append(element_type.native_type); } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) { // Need conversion. append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); @@ -1326,14 +1334,7 @@ void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_tar append(p_arguments[i]); } append(get_call_target(p_target)); - if (p_element_type.script_type) { - Variant script_type = Ref<Script>(p_element_type.script_type); - int addr = get_constant_pos(script_type); - addr |= GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS; - append(addr); - } else { - append(Address()); // null. - } + append(get_constant_pos(p_element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); append(p_arguments.size()); append(p_element_type.builtin_type); append(p_element_type.native_type); @@ -1608,14 +1609,10 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) { // Typed array. const GDScriptDataType &element_type = function->return_type.get_container_element_type(); - - Variant script = element_type.script_type; - int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); - append(script_idx); - append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); append(element_type.native_type); } else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) { // Add conversion. @@ -1636,15 +1633,10 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { case GDScriptDataType::BUILTIN: { if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) { const GDScriptDataType &element_type = function->return_type.get_container_element_type(); - - Variant script = function->return_type.script_type; - int script_idx = get_constant_pos(script); - script_idx |= (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); - append(script_idx); - append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); append(element_type.native_type); } else { append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index c78dd1528f..373d8a3efd 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -196,7 +196,11 @@ static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataTyp } } -static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { +static bool _can_use_ptrcall(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { + if (p_method->is_vararg()) { + // ptrcall won't work with vararg methods. + return false; + } if (p_method->get_argument_count() != p_arguments.size()) { // ptrcall won't work with default arguments. return false; @@ -563,7 +567,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code self.mode = GDScriptCodeGenerator::Address::SELF; MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name); - if (_have_exact_arguments(method, arguments)) { + if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. gen->write_call_ptrcall(result, self, method, arguments); } else { @@ -613,7 +617,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { MethodBind *method = ClassDB::get_method(class_name, call->function_name); - if (_have_exact_arguments(method, arguments)) { + if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. gen->write_call_ptrcall(result, base, method, arguments); } else { @@ -1900,14 +1904,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui bool initialized = false; if (lv->initializer != nullptr) { - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) { - if (local_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>()); - } - } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, err, lv->initializer); if (err) { return err; @@ -2048,14 +2044,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ // Emit proper line change. codegen.generator->write_newline(field->initializer->start_line); - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - if (field_type.has_type && field_type.builtin_type == Variant::ARRAY) { - if (field_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>()); - } - } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true); if (r_error) { memdelete(codegen.generator); @@ -2096,17 +2084,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ return nullptr; } GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name]; - - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - GDScriptDataType par_type = dst_addr.type; - if (par_type.has_type && par_type.builtin_type == Variant::ARRAY) { - if (par_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_addr, par_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(dst_addr, Vector<GDScriptCodeGenerator::Address>()); - } - } - codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign); if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 06c66b155f..bb08b017c9 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -51,46 +51,8 @@ static HashMap<StringName, Variant::Type> builtin_types; Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { if (builtin_types.is_empty()) { - builtin_types["bool"] = Variant::BOOL; - builtin_types["int"] = Variant::INT; - builtin_types["float"] = Variant::FLOAT; - builtin_types["String"] = Variant::STRING; - builtin_types["Vector2"] = Variant::VECTOR2; - builtin_types["Vector2i"] = Variant::VECTOR2I; - builtin_types["Rect2"] = Variant::RECT2; - builtin_types["Rect2i"] = Variant::RECT2I; - builtin_types["Transform2D"] = Variant::TRANSFORM2D; - builtin_types["Vector3"] = Variant::VECTOR3; - builtin_types["Vector3i"] = Variant::VECTOR3I; - builtin_types["Vector4"] = Variant::VECTOR4; - builtin_types["Vector4i"] = Variant::VECTOR4I; - builtin_types["AABB"] = Variant::AABB; - builtin_types["Plane"] = Variant::PLANE; - builtin_types["Quaternion"] = Variant::QUATERNION; - builtin_types["Basis"] = Variant::BASIS; - builtin_types["Transform3D"] = Variant::TRANSFORM3D; - builtin_types["Projection"] = Variant::PROJECTION; - builtin_types["Color"] = Variant::COLOR; - builtin_types["RID"] = Variant::RID; - builtin_types["Object"] = Variant::OBJECT; - builtin_types["StringName"] = Variant::STRING_NAME; - builtin_types["NodePath"] = Variant::NODE_PATH; - builtin_types["Dictionary"] = Variant::DICTIONARY; - builtin_types["Callable"] = Variant::CALLABLE; - builtin_types["Signal"] = Variant::SIGNAL; - builtin_types["Array"] = Variant::ARRAY; - builtin_types["PackedByteArray"] = Variant::PACKED_BYTE_ARRAY; - builtin_types["PackedInt32Array"] = Variant::PACKED_INT32_ARRAY; - builtin_types["PackedInt64Array"] = Variant::PACKED_INT64_ARRAY; - builtin_types["PackedFloat32Array"] = Variant::PACKED_FLOAT32_ARRAY; - builtin_types["PackedFloat64Array"] = Variant::PACKED_FLOAT64_ARRAY; - builtin_types["PackedStringArray"] = Variant::PACKED_STRING_ARRAY; - builtin_types["PackedVector2Array"] = Variant::PACKED_VECTOR2_ARRAY; - builtin_types["PackedVector3Array"] = Variant::PACKED_VECTOR3_ARRAY; - builtin_types["PackedColorArray"] = Variant::PACKED_COLOR_ARRAY; - // NIL is not here, hence the -1. - if (builtin_types.size() != Variant::VARIANT_MAX - 1) { - ERR_PRINT("Outdated parser: amount of built-in types don't match the amount of types in Variant."); + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i; } } @@ -982,14 +944,14 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var // Run with a loop because order doesn't matter. for (int i = 0; i < 2; i++) { - if (function->name == "set") { + if (function->name == SNAME("set")) { if (setter_used) { push_error(R"(Properties can only have one setter.)"); } else { parse_property_setter(property); setter_used = true; } - } else if (function->name == "get") { + } else if (function->name == SNAME("get")) { if (getter_used) { push_error(R"(Properties can only have one getter.)"); } else { @@ -1906,10 +1868,8 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { return match; } -#ifdef DEBUG_ENABLED bool all_have_return = true; bool have_wildcard = false; -#endif while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) { MatchBranchNode *branch = parse_match_branch(); @@ -1922,21 +1882,19 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { if (have_wildcard && !branch->patterns.is_empty()) { push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN); } +#endif have_wildcard = have_wildcard || branch->has_wildcard; all_have_return = all_have_return && branch->block->has_return; -#endif match->branches.push_back(branch); } complete_extents(match); consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)"); -#ifdef DEBUG_ENABLED if (all_have_return && have_wildcard) { current_suite->has_return = true; } -#endif return match; } @@ -2925,7 +2883,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre // Arguments. CompletionType ct = COMPLETION_CALL_ARGUMENTS; - if (call->function_name == "load") { + if (call->function_name == SNAME("load")) { ct = COMPLETION_RESOURCE_PATH; } push_completion_call(call); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 74e12d0b5e..bb4549c15c 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -190,7 +190,7 @@ public: case SCRIPT: return script_type == p_other.script_type; case CLASS: - return class_type == p_other.class_type; + return class_type == p_other.class_type || class_type->fqcn == p_other.class_type->fqcn; case RESOLVING: case UNRESOLVED: break; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 4ea4438b5e..e18a4a6190 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -47,6 +47,16 @@ static String _get_script_name(const Ref<Script> p_script) { } } +static String _get_element_type(Variant::Type builtin_type, const StringName &native_type, const Ref<Script> &script_type) { + if (script_type.is_valid() && script_type->is_valid()) { + return _get_script_name(script_type); + } else if (native_type != StringName()) { + return native_type.operator String(); + } else { + return Variant::get_type_name(builtin_type); + } +} + static String _get_var_type(const Variant *p_var) { String basestr; @@ -75,15 +85,8 @@ static String _get_var_type(const Variant *p_var) { basestr = "Array"; const Array *p_array = VariantInternal::get_array(p_var); Variant::Type builtin_type = (Variant::Type)p_array->get_typed_builtin(); - StringName native_type = p_array->get_typed_class_name(); - Ref<Script> script_type = p_array->get_typed_script(); - - if (script_type.is_valid() && script_type->is_valid()) { - basestr += "[" + _get_script_name(script_type) + "]"; - } else if (native_type != StringName()) { - basestr += "[" + native_type.operator String() + "]"; - } else if (builtin_type != Variant::NIL) { - basestr += "[" + Variant::get_type_name(builtin_type) + "]"; + if (builtin_type != Variant::NIL) { + basestr += "[" + _get_element_type(builtin_type, p_array->get_typed_class_name(), p_array->get_typed_script()) + "]"; } } else { basestr = Variant::get_type_name(p_var->get_type()); @@ -101,10 +104,7 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT // Typed array. if (p_data_type.has_container_element_type()) { const GDScriptDataType &element_type = p_data_type.get_container_element_type(); - array.set_typed( - element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT, - element_type.native_type, - element_type.script_type); + array.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type); } return array; @@ -131,6 +131,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const #ifdef DEBUG_ENABLED if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) { err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class."; + } else if (p_err.expected == Variant::ARRAY && argptrs[errorarg]->get_type() == p_err.expected) { + err_text = "Invalid type in " + p_where + ". The array of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") does not have the same element type as the expected typed array argument."; } else #endif // DEBUG_ENABLED { @@ -518,7 +520,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (!argument_types[i].is_type(*p_args[i], true)) { r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + r_err.expected = argument_types[i].builtin_type; return _get_default_variant_for_data_type(return_type); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { @@ -1174,27 +1176,37 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) { - CHECK_SPACE(3); + CHECK_SPACE(6); GET_VARIANT_PTR(dst, 0); GET_VARIANT_PTR(src, 1); - Array *dst_arr = VariantInternal::get_array(dst); + GET_VARIANT_PTR(script_type, 2); + Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 4]; + int native_type_idx = _code_ptr[ip + 5]; + GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count); + const StringName native_type = _global_names_ptr[native_type_idx]; if (src->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED - err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + - "' to a variable of type '" + +"'."; -#endif + err_text = vformat(R"(Trying to assign a value of type "%s" to a variable of type "Array[%s]".)", + _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } - if (!dst_arr->typed_assign(*src)) { + + Array *array = VariantInternal::get_array(src); + + if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) { #ifdef DEBUG_ENABLED - err_text = "Trying to assign a typed array with an array of different type.'"; -#endif + err_text = vformat(R"(Trying to assign an array of type "%s" to a variable of type "Array[%s]".)", + _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } - ip += 3; + *dst = *src; + + ip += 6; } DISPATCH_OPCODE; @@ -1469,9 +1481,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const StringName native_type = _global_names_ptr[native_type_idx]; Array array; - array.set_typed(builtin_type, native_type, *script_type); array.resize(argc); - for (int i = 0; i < argc; i++) { array[i] = *(instruction_args[i]); } @@ -1479,7 +1489,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_INSTRUCTION_ARG(dst, argc); *dst = Variant(); // Clear potential previous typed array. - *dst = array; + *dst = Array(array, builtin_type, native_type, *script_type); ip += 4; } @@ -2486,30 +2496,25 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "Array[%s]".)", - Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type)); -#endif + err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Array[%s]".)", + _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } - Array array; - array.set_typed(builtin_type, native_type, *script_type); + Array *array = VariantInternal::get_array(r); + if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) { #ifdef DEBUG_ENABLED - bool valid = array.typed_assign(*VariantInternal::get_array(r)); -#else - array.typed_assign(*VariantInternal::get_array(r)); + err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)", + _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); #endif // DEBUG_ENABLED - - // Assign the return value anyway since we want it to be the valid type. - retvalue = array; - -#ifdef DEBUG_ENABLED - if (!valid) { - err_text = "Trying to return a typed array with an array of different type.'"; OPCODE_BREAK; } + retvalue = *array; + +#ifdef DEBUG_ENABLED exit_ok = true; #endif // DEBUG_ENABLED OPCODE_BREAK; diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index d2c8b5c317..5b8af0ff34 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -132,9 +132,10 @@ void finish_language() { StringName GDScriptTestRunner::test_function_name; -GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language) { +GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames) { test_function_name = StaticCString::create("test"); do_init_languages = p_init_language; + print_filenames = p_print_filenames; source_dir = p_source_dir; if (!source_dir.ends_with("/")) { @@ -194,6 +195,9 @@ int GDScriptTestRunner::run_tests() { int failed = 0; for (int i = 0; i < tests.size(); i++) { GDScriptTest test = tests[i]; + if (print_filenames) { + print_line(test.get_source_relative_filepath()); + } GDScriptTest::TestResult result = test.run_test(); String expected = FileAccess::get_file_as_string(test.get_output_file()); @@ -225,8 +229,13 @@ bool GDScriptTestRunner::generate_outputs() { } for (int i = 0; i < tests.size(); i++) { - OS::get_singleton()->print("."); GDScriptTest test = tests[i]; + if (print_filenames) { + print_line(test.get_source_relative_filepath()); + } else { + OS::get_singleton()->print("."); + } + bool result = test.generate_output(); if (!result) { @@ -337,15 +346,10 @@ GDScriptTest::GDScriptTest(const String &p_source_path, const String &p_output_p void GDScriptTestRunner::handle_cmdline() { List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); - // TODO: this could likely be ported to use test commands: - // https://github.com/godotengine/godot/pull/41355 - // Currently requires to startup the whole engine, which is slow. - String test_cmd = "--gdscript-test"; - String gen_cmd = "--gdscript-generate-tests"; for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { String &cmd = E->get(); - if (cmd == test_cmd || cmd == gen_cmd) { + if (cmd == "--gdscript-generate-tests") { if (E->next() == nullptr) { ERR_PRINT("Needed a path for the test files."); exit(-1); @@ -353,14 +357,10 @@ void GDScriptTestRunner::handle_cmdline() { const String &path = E->next()->get(); - GDScriptTestRunner runner(path, false); - int failed = 0; - if (cmd == test_cmd) { - failed = runner.run_tests(); - } else { - bool completed = runner.generate_outputs(); - failed = completed ? 0 : -1; - } + GDScriptTestRunner runner(path, false, cmdline_args.find("--print-filenames") != nullptr); + + bool completed = runner.generate_outputs(); + int failed = completed ? 0 : -1; exit(failed); } } diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index b097f1b485..60b48c6a57 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -92,6 +92,7 @@ public: bool generate_output(); const String &get_source_file() const { return source_file; } + const String get_source_relative_filepath() const { return source_file.trim_prefix(base_dir); } const String &get_output_file() const { return output_file; } GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir); @@ -105,6 +106,7 @@ class GDScriptTestRunner { bool is_generating = false; bool do_init_languages = false; + bool print_filenames; // Whether filenames should be printed when generated/running tests bool make_tests(); bool make_tests_for_dir(const String &p_dir); @@ -117,7 +119,7 @@ public: int run_tests(); bool generate_outputs(); - GDScriptTestRunner(const String &p_source_dir, bool p_init_language); + GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames = false); ~GDScriptTestRunner(); }; diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index aed0ac2baf..e27b6218f1 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -41,7 +41,8 @@ TEST_SUITE("[Modules][GDScript]") { // Allow the tests to fail, but do not ignore errors during development. // Update the scripts and expected output as needed. TEST_CASE("Script compilation and runtime") { - GDScriptTestRunner runner("modules/gdscript/tests/scripts", true); + bool print_filenames = OS::get_singleton()->get_cmdline_args().find("--print-filenames") != nullptr; + GDScriptTestRunner runner("modules/gdscript/tests/scripts", true, print_filenames); int fail_count = runner.run_tests(); INFO("Make sure `*.out` files have expected results."); REQUIRE_MESSAGE(fail_count == 0, "All GDScript tests should pass."); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out index 6f7f0783f0..015ad756f8 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot get index "true" from "[0, 1]". +Invalid index type "bool" for a base of type "Array". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd new file mode 100644 index 0000000000..ce50cccb3c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd @@ -0,0 +1,4 @@ +func test(): + var differently: Array[float] = [1.0] + var typed: Array[int] = differently + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out new file mode 100644 index 0000000000..c6d39781ee --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a value of type Array[float] to variable "typed" with specified type Array[int]. diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd index 9f86d0531c..9f86d0531c 100644 --- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out new file mode 100644 index 0000000000..8530783673 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot include a value of type "String" as "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd new file mode 100644 index 0000000000..25cde1d40b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd @@ -0,0 +1,4 @@ +func test(): + var unconvertable := 1 + var typed: Array[Object] = [unconvertable] + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out new file mode 100644 index 0000000000..dfe3443761 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot have an element of type "int" in an array of type "Array[Object]". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd new file mode 100644 index 0000000000..1a90bd121e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd @@ -0,0 +1,7 @@ +func expect_typed(typed: Array[int]): + print(typed.size()) + +func test(): + var differently: Array[float] = [1.0] + expect_typed(differently) + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out new file mode 100644 index 0000000000..297e1283e8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid argument for "expect_typed()" function: argument 1 should be "Array[int]" but is "Array[float]". diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out index 082e3ade19..2729c5b6c7 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out @@ -2,7 +2,7 @@ GDTEST_OK [0] 0 [1] -2 +0 [2] 2 ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd new file mode 100644 index 0000000000..e1e6134fd4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd @@ -0,0 +1,204 @@ +class A: pass +class B extends A: pass + +enum E { E0 = 391 } + +func floats_identity(floats: Array[float]): return floats + +class Members: + var one: Array[int] = [104] + var two: Array[int] = one + + func check_passing() -> bool: + assert(str(one) == '[104]') + assert(str(two) == '[104]') + two.push_back(582) + assert(str(one) == '[104, 582]') + assert(str(two) == '[104, 582]') + two = [486] + assert(str(one) == '[104, 582]') + assert(str(two) == '[486]') + return true + + +@warning_ignore("unsafe_method_access") +@warning_ignore("assert_always_true") +@warning_ignore("return_value_discarded") +func test(): + var untyped_basic = [459] + assert(str(untyped_basic) == '[459]') + assert(untyped_basic.get_typed_builtin() == TYPE_NIL) + + var inferred_basic := [366] + assert(str(inferred_basic) == '[366]') + assert(inferred_basic.get_typed_builtin() == TYPE_NIL) + + var typed_basic: Array = [521] + assert(str(typed_basic) == '[521]') + assert(typed_basic.get_typed_builtin() == TYPE_NIL) + + + var empty_floats: Array[float] = [] + assert(str(empty_floats) == '[]') + assert(empty_floats.get_typed_builtin() == TYPE_FLOAT) + + untyped_basic = empty_floats + assert(untyped_basic.get_typed_builtin() == TYPE_FLOAT) + + inferred_basic = empty_floats + assert(inferred_basic.get_typed_builtin() == TYPE_FLOAT) + + typed_basic = empty_floats + assert(typed_basic.get_typed_builtin() == TYPE_FLOAT) + + empty_floats.push_back(705.0) + untyped_basic.push_back(430.0) + inferred_basic.push_back(263.0) + typed_basic.push_back(518.0) + assert(str(empty_floats) == '[705, 430, 263, 518]') + assert(str(untyped_basic) == '[705, 430, 263, 518]') + assert(str(inferred_basic) == '[705, 430, 263, 518]') + assert(str(typed_basic) == '[705, 430, 263, 518]') + + + const constant_float := 950.0 + const constant_int := 170 + var typed_float := 954.0 + var filled_floats: Array[float] = [constant_float, constant_int, typed_float, empty_floats[1] + empty_floats[2]] + assert(str(filled_floats) == '[950, 170, 954, 693]') + assert(filled_floats.get_typed_builtin() == TYPE_FLOAT) + + var casted_floats := [empty_floats[2] * 2] as Array[float] + assert(str(casted_floats) == '[526]') + assert(casted_floats.get_typed_builtin() == TYPE_FLOAT) + + var returned_floats = (func () -> Array[float]: return [554]).call() + assert(str(returned_floats) == '[554]') + assert(returned_floats.get_typed_builtin() == TYPE_FLOAT) + + var passed_floats = floats_identity([663.0 if randf() > 0.5 else 663.0]) + assert(str(passed_floats) == '[663]') + assert(passed_floats.get_typed_builtin() == TYPE_FLOAT) + + var default_floats = (func (floats: Array[float] = [364.0]): return floats).call() + assert(str(default_floats) == '[364]') + assert(default_floats.get_typed_builtin() == TYPE_FLOAT) + + var typed_int := 556 + var converted_floats: Array[float] = [typed_int] + assert(str(converted_floats) == '[556]') + assert(converted_floats.get_typed_builtin() == TYPE_FLOAT) + + + const constant_basic = [228] + assert(str(constant_basic) == '[228]') + assert(constant_basic.get_typed_builtin() == TYPE_NIL) + + const constant_floats: Array[float] = [constant_float - constant_basic[0] - constant_int] + assert(str(constant_floats) == '[552]') + assert(constant_floats.get_typed_builtin() == TYPE_FLOAT) + + + var source_floats: Array[float] = [999.74] + untyped_basic = source_floats + var destination_floats: Array[float] = untyped_basic + destination_floats[0] -= 0.74 + assert(str(source_floats) == '[999]') + assert(str(untyped_basic) == '[999]') + assert(str(destination_floats) == '[999]') + assert(destination_floats.get_typed_builtin() == TYPE_FLOAT) + + + var duplicated_floats := empty_floats.duplicate().slice(2, 3) + duplicated_floats[0] *= 3 + assert(str(duplicated_floats) == '[789]') + assert(duplicated_floats.get_typed_builtin() == TYPE_FLOAT) + + + var b_objects: Array[B] = [B.new(), null] + assert(b_objects.size() == 2) + assert(b_objects.get_typed_builtin() == TYPE_OBJECT) + assert(b_objects.get_typed_script() == B) + + var a_objects: Array[A] = [A.new(), B.new(), null, b_objects[0]] + assert(a_objects.size() == 4) + assert(a_objects.get_typed_builtin() == TYPE_OBJECT) + assert(a_objects.get_typed_script() == A) + + var a_passed = (func check_a_passing(a_objects: Array[A]): return a_objects.size()).call(a_objects) + assert(a_passed == 4) + + var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects) + assert(b_passed == true) + + + var empty_strings: Array[String] = [] + var empty_bools: Array[bool] = [] + var empty_basic_one := [] + var empty_basic_two := [] + assert(empty_strings == empty_bools) + assert(empty_basic_one == empty_basic_two) + assert(empty_strings.hash() == empty_bools.hash()) + assert(empty_basic_one.hash() == empty_basic_two.hash()) + + + var assign_source: Array[int] = [527] + var assign_target: Array[int] = [] + assign_target.assign(assign_source) + assert(str(assign_source) == '[527]') + assert(str(assign_target) == '[527]') + assign_source.push_back(657) + assert(str(assign_source) == '[527, 657]') + assert(str(assign_target) == '[527]') + + + var defaults_passed = (func check_defaults_passing(one: Array[int] = [], two := one): + one.push_back(887) + two.push_back(198) + assert(str(one) == '[887, 198]') + assert(str(two) == '[887, 198]') + two = [130] + assert(str(one) == '[887, 198]') + assert(str(two) == '[130]') + return true + ).call() + assert(defaults_passed == true) + + + var members := Members.new() + var members_passed := members.check_passing() + assert(members_passed == true) + + + var resized_basic: Array = [] + resized_basic.resize(1) + assert(typeof(resized_basic[0]) == TYPE_NIL) + assert(resized_basic[0] == null) + + var resized_ints: Array[int] = [] + resized_ints.resize(1) + assert(typeof(resized_ints[0]) == TYPE_INT) + assert(resized_ints[0] == 0) + + var resized_arrays: Array[Array] = [] + resized_arrays.resize(1) + assert(typeof(resized_arrays[0]) == TYPE_ARRAY) + resized_arrays[0].resize(1) + resized_arrays[0][0] = 523 + assert(str(resized_arrays) == '[[523]]') + + var resized_objects: Array[Object] = [] + resized_objects.resize(1) + assert(typeof(resized_objects[0]) == TYPE_NIL) + assert(resized_objects[0] == null) + + + var typed_enums: Array[E] = [] + typed_enums.resize(1) + assert(str(typed_enums) == '[0]') + typed_enums[0] = E.E0 + assert(str(typed_enums) == '[391]') + assert(typed_enums.get_typed_builtin() == TYPE_INT) + + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd new file mode 100644 index 0000000000..d444250f1e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd @@ -0,0 +1,6 @@ +signal ok() + +@warning_ignore("return_value_discarded") +func test(): + ok.connect(func(): print('ok')) + emit_signal(&'ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/vararg_call.out b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out deleted file mode 100644 index ad2e6558d7..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int]. diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd new file mode 100644 index 0000000000..e9dbc1b640 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd @@ -0,0 +1,4 @@ +func test(): + var basic := [1] + var typed: Array[int] = basic + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out new file mode 100644 index 0000000000..bca700b4ec --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_assign_basic_to_typed.gd +>> 3 +>> Trying to assign an array of type "Array" to a variable of type "Array[int]". diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd new file mode 100644 index 0000000000..920352a6ea --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd @@ -0,0 +1,4 @@ +func test(): + var differently: Variant = [1.0] as Array[float] + var typed: Array[int] = differently + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out new file mode 100644 index 0000000000..402ab38fb3 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_assign_differently_typed.gd +>> 3 +>> Trying to assign an array of type "Array[float]" to a variable of type "Array[int]". diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd new file mode 100644 index 0000000000..e1fd0f7168 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd @@ -0,0 +1,7 @@ +func expect_typed(typed: Array[int]): + print(typed.size()) + +func test(): + var basic := [1] + expect_typed(basic) + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out new file mode 100644 index 0000000000..6f210e944e --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_pass_basic_to_typed.gd +>> 6 +>> Invalid type in function 'expect_typed' in base 'RefCounted ()'. The array of argument 1 (Array) does not have the same element type as the expected typed array argument. diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd new file mode 100644 index 0000000000..e2d2721e8c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd @@ -0,0 +1,7 @@ +func expect_typed(typed: Array[int]): + print(typed.size()) + +func test(): + var differently: Variant = [1.0] as Array[float] + expect_typed(differently) + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out new file mode 100644 index 0000000000..3cd4e25bd8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_pass_differently_to_typed.gd +>> 6 +>> Invalid type in function 'expect_typed' in base 'RefCounted ()'. The array of argument 1 (Array[float]) does not have the same element type as the expected typed array argument. diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd new file mode 100644 index 0000000000..ec444b4ffa --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd @@ -0,0 +1,6 @@ +func test(): + var untyped: Variant = 32 + var typed: Array[int] = [untyped] + assert(typed.get_typed_builtin() == TYPE_INT) + assert(str(typed) == '[32]') + print('ok') diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs index 150eb98fc7..350626389b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs @@ -6,13 +6,17 @@ using Godot.NativeInterop; namespace Godot { /// <summary> - /// The Rid type is used to access the unique integer ID of a resource. - /// They are opaque, which means they do not grant access to the associated - /// resource by themselves. They are used by and with the low-level Server - /// classes such as <see cref="RenderingServer"/>. + /// The RID type is used to access a low-level resource by its unique ID. + /// RIDs are opaque, which means they do not grant access to the resource + /// by themselves. They are used by the low-level server classes, such as + /// <see cref="DisplayServer"/>, <see cref="RenderingServer"/>, + /// <see cref="TextServer"/>, etc. + /// + /// A low-level resource may correspond to a high-level <see cref="Resource"/>, + /// such as <see cref="Texture"/> or <see cref="Mesh"/> /// </summary> [StructLayout(LayoutKind.Sequential)] - public readonly struct Rid + public readonly struct Rid : IEquatable<Rid> { private readonly ulong _id; // Default is 0 @@ -28,15 +32,73 @@ namespace Godot => _id = from is Resource res ? res.GetRid()._id : default; /// <summary> - /// Returns the ID of the referenced resource. + /// Returns the ID of the referenced low-level resource. /// </summary> /// <returns>The ID of the referenced resource.</returns> public ulong Id => _id; /// <summary> + /// Returns <see langword="true"/> if the <see cref="Rid"/> is not <c>0</c>. + /// </summary> + /// <returns>Whether or not the ID is valid.</returns> + public bool IsValid => _id != 0; + + /// <summary> + /// Returns <see langword="true"/> if both <see cref="Rid"/>s are equal, + /// which means they both refer to the same low-level resource. + /// </summary> + /// <param name="left">The left RID.</param> + /// <param name="right">The right RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public static bool operator ==(Rid left, Rid right) + { + return left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if the <see cref="Rid"/>s are not equal. + /// </summary> + /// <param name="left">The left RID.</param> + /// <param name="right">The right RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public static bool operator !=(Rid left, Rid right) + { + return !left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if this RID and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the color and the other object are equal.</returns> + public override readonly bool Equals(object obj) + { + return obj is Rid other && Equals(other); + } + + /// <summary> + /// Returns <see langword="true"/> if the RIDs are equal. + /// </summary> + /// <param name="other">The other RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public readonly bool Equals(Rid other) + { + return _id == other.Id; + } + + /// <summary> + /// Serves as the hash function for <see cref="Rid"/>. + /// </summary> + /// <returns>A hash code for this RID.</returns> + public override readonly int GetHashCode() + { + return HashCode.Combine(_id); + } + + /// <summary> /// Converts this <see cref="Rid"/> to a string. /// </summary> /// <returns>A string representation of this Rid.</returns> - public override string ToString() => $"Rid({Id})"; + public override string ToString() => $"RID({Id})"; } } diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 7251a4a9bd..f3cc469c9d 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -11,12 +11,33 @@ <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> </tutorials> <methods> + <method name="get_action_sets" qualifiers="const"> + <return type="Array" /> + <description> + Returns a list of action sets registered with Godot (loaded from the action map at runtime). + </description> + </method> <method name="get_available_display_refresh_rates" qualifiers="const"> <return type="Array" /> <description> Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized. </description> </method> + <method name="is_action_set_active" qualifiers="const"> + <return type="bool" /> + <param index="0" name="name" type="String" /> + <description> + Returns [code]true[/code] if the given action set is active. + </description> + </method> + <method name="set_action_set_active"> + <return type="void" /> + <param index="0" name="name" type="String" /> + <param index="1" name="active" type="bool" /> + <description> + Sets the given action set as active or inactive. + </description> + </method> </methods> <members> <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0"> diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index f9afdf2d4a..702e56b410 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -47,6 +47,10 @@ void OpenXRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate"); + ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active); + ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active); + ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets); + ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); } @@ -621,6 +625,38 @@ Array OpenXRInterface::get_available_display_refresh_rates() const { } } +bool OpenXRInterface::is_action_set_active(const String &p_action_set) const { + for (ActionSet *action_set : action_sets) { + if (action_set->action_set_name == p_action_set) { + return action_set->is_active; + } + } + + WARN_PRINT("OpenXR: Unknown action set " + p_action_set); + return false; +} + +void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) { + for (ActionSet *action_set : action_sets) { + if (action_set->action_set_name == p_action_set) { + action_set->is_active = p_active; + return; + } + } + + WARN_PRINT("OpenXR: Unknown action set " + p_action_set); +} + +Array OpenXRInterface::get_action_sets() const { + Array arr; + + for (ActionSet *action_set : action_sets) { + arr.push_back(action_set->action_set_name); + } + + return arr; +} + Size2 OpenXRInterface::get_render_target_size() { if (openxr_api == nullptr) { return Size2(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 2b43369523..cce329d8e6 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -123,6 +123,10 @@ public: void set_display_refresh_rate(float p_refresh_rate); Array get_available_display_refresh_rates() const; + bool is_action_set_active(const String &p_action_set) const; + void set_action_set_active(const String &p_action_set, bool p_active); + Array get_action_sets() const; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e53aef965a..9b474bf2ce 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -4068,7 +4068,6 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const { _THREAD_SAFE_METHOD_ - const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); @@ -5513,6 +5512,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star } bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { + _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -6569,6 +6569,7 @@ TextServerAdvanced::TextServerAdvanced() { } void TextServerAdvanced::_cleanup() { + _THREAD_SAFE_METHOD_ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) { const Vector<SystemFontCacheRec> &sysf_cache = E.value.var; for (const SystemFontCacheRec &F : sysf_cache) { diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index e07af8f169..3ec762a880 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -9,19 +9,4 @@ </description> <tutorials> </tutorials> - <methods> - <method name="get_file"> - <return type="String" /> - <description> - Returns the Ogg Theora video file handled by this [VideoStreamTheora]. - </description> - </method> - <method name="set_file"> - <return type="void" /> - <param index="0" name="file" type="String" /> - <description> - Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension. - </description> - </method> - </methods> </class> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index c600924a3e..b38f7225a2 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -574,25 +574,10 @@ bool VideoStreamPlaybackTheora::is_paused() const { return paused; } -void VideoStreamPlaybackTheora::set_loop(bool p_enable) { -} - -bool VideoStreamPlaybackTheora::has_loop() const { - return false; -} - double VideoStreamPlaybackTheora::get_length() const { return 0; } -String VideoStreamPlaybackTheora::get_stream_name() const { - return ""; -} - -int VideoStreamPlaybackTheora::get_loop_count() const { - return 0; -} - double VideoStreamPlaybackTheora::get_playback_position() const { return get_time(); } @@ -601,11 +586,6 @@ void VideoStreamPlaybackTheora::seek(double p_time) { WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDExtension-provided video streams)."); } -void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { - mix_callback = p_callback; - mix_udata = p_userdata; -} - int VideoStreamPlaybackTheora::get_channels() const { return vi.channels; } @@ -657,16 +637,9 @@ VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() { memdelete(thread_sem); #endif clear(); -} - -void VideoStreamTheora::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file); - ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamTheora::get_file); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); -} +}; -//////////// +void VideoStreamTheora::_bind_methods() {} Ref<Resource> ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index f047440df7..32adc28a88 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -99,8 +99,6 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback { Ref<ImageTexture> texture; - AudioMixCallback mix_callback = nullptr; - void *mix_udata = nullptr; bool paused = false; #ifdef THEORA_USE_THREAD_STREAMING @@ -133,15 +131,8 @@ public: virtual void set_paused(bool p_paused) override; virtual bool is_paused() const override; - virtual void set_loop(bool p_enable) override; - virtual bool has_loop() const override; - virtual double get_length() const override; - virtual String get_stream_name() const; - - virtual int get_loop_count() const; - virtual double get_playback_position() const override; virtual void seek(double p_time) override; @@ -150,7 +141,6 @@ public: virtual Ref<Texture2D> get_texture() const override; virtual void update(double p_delta) override; - virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override; virtual int get_channels() const override; virtual int get_mix_rate() const override; @@ -163,9 +153,6 @@ public: class VideoStreamTheora : public VideoStream { GDCLASS(VideoStreamTheora, VideoStream); - String file; - int audio_track; - protected: static void _bind_methods(); @@ -177,8 +164,6 @@ public: return pb; } - void set_file(const String &p_file) { file = p_file; } - String get_file() { return file; } void set_audio_track(int p_track) override { audio_track = p_track; } VideoStreamTheora() { audio_track = 0; } |