GDScript: Don't try to parse constant scripts that aren't valid

Since it's likely that they won't parse correctly.
This commit is contained in:
George Marques 2020-08-31 09:53:02 -03:00
parent 7844775a76
commit 1ddb9b1a52
No known key found for this signature in database
GPG Key ID: 046BD46A3201E43D
2 changed files with 53 additions and 46 deletions

View File

@ -1455,7 +1455,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
bool compatible = true; bool compatible = true;
GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype(); GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible); op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
} }
if (compatible) { if (compatible) {
@ -1618,7 +1618,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
ERR_PRINT("Parser bug: unknown binary operation."); ERR_PRINT("Parser bug: unknown binary operation.");
} }
} }
p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value)); p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value, p_binary_op));
return; return;
} }
@ -1632,7 +1632,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
} else { } else {
if (p_binary_op->variant_op < Variant::OP_MAX) { if (p_binary_op->variant_op < Variant::OP_MAX) {
bool valid = false; bool valid = false;
result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid); result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op);
if (!valid) { if (!valid) {
push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
@ -2061,7 +2061,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (valid) { if (valid) {
p_identifier->is_constant = true; p_identifier->is_constant = true;
p_identifier->reduced_value = result; p_identifier->reduced_value = result;
p_identifier->set_datatype(type_from_variant(result)); p_identifier->set_datatype(type_from_variant(result, p_identifier));
} else { } else {
push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
} }
@ -2192,7 +2192,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (valid) { if (valid) {
p_identifier->is_constant = true; p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant; p_identifier->reduced_value = int_constant;
p_identifier->set_datatype(type_from_variant(int_constant)); p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
return; return;
} }
} }
@ -2292,7 +2292,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) { if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[name]; int idx = GDScriptLanguage::get_singleton()->get_global_map()[name];
Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx]; Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx];
p_identifier->set_datatype(type_from_variant(constant)); p_identifier->set_datatype(type_from_variant(constant, p_identifier));
p_identifier->is_constant = true; p_identifier->is_constant = true;
p_identifier->reduced_value = constant; p_identifier->reduced_value = constant;
return; return;
@ -2300,7 +2300,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) { if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) {
Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name]; Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name];
p_identifier->set_datatype(type_from_variant(constant)); p_identifier->set_datatype(type_from_variant(constant, p_identifier));
p_identifier->is_constant = true; p_identifier->is_constant = true;
p_identifier->reduced_value = constant; p_identifier->reduced_value = constant;
return; return;
@ -2322,7 +2322,7 @@ void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
p_literal->reduced_value = p_literal->value; p_literal->reduced_value = p_literal->value;
p_literal->is_constant = true; p_literal->is_constant = true;
p_literal->set_datatype(type_from_variant(p_literal->reduced_value)); p_literal->set_datatype(type_from_variant(p_literal->reduced_value, p_literal));
} }
void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
@ -2359,7 +2359,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
p_preload->is_constant = true; p_preload->is_constant = true;
p_preload->reduced_value = p_preload->resource; p_preload->reduced_value = p_preload->resource;
p_preload->set_datatype(type_from_variant(p_preload->reduced_value)); p_preload->set_datatype(type_from_variant(p_preload->reduced_value, p_preload));
} }
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) { void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
@ -2408,7 +2408,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
} else { } else {
p_subscript->is_constant = true; p_subscript->is_constant = true;
p_subscript->reduced_value = value; p_subscript->reduced_value = value;
result_type = type_from_variant(value); result_type = type_from_variant(value, p_subscript);
} }
result_type.kind = GDScriptParser::DataType::VARIANT; result_type.kind = GDScriptParser::DataType::VARIANT;
} else { } else {
@ -2448,7 +2448,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
} else { } else {
p_subscript->is_constant = true; p_subscript->is_constant = true;
p_subscript->reduced_value = value; p_subscript->reduced_value = value;
result_type = type_from_variant(value); result_type = type_from_variant(value, p_subscript);
} }
result_type.kind = GDScriptParser::DataType::VARIANT; result_type.kind = GDScriptParser::DataType::VARIANT;
} else { } else {
@ -2672,13 +2672,13 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
if (p_unary_op->operand->is_constant) { if (p_unary_op->operand->is_constant) {
p_unary_op->is_constant = true; p_unary_op->is_constant = true;
p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant()); p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant());
result = type_from_variant(p_unary_op->reduced_value); result = type_from_variant(p_unary_op->reduced_value, p_unary_op);
} else if (p_unary_op->operand->get_datatype().is_variant()) { } else if (p_unary_op->operand->get_datatype().is_variant()) {
result.kind = GDScriptParser::DataType::VARIANT; result.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_unary_op); mark_node_unsafe(p_unary_op);
} else { } else {
bool valid = false; bool valid = false;
result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid); result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
if (!valid) { if (!valid) {
push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand); push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
@ -2688,7 +2688,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result); p_unary_op->set_datatype(result);
} }
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value) { GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result; GDScriptParser::DataType result;
result.is_constant = true; result.is_constant = true;
result.kind = GDScriptParser::DataType::BUILTIN; result.kind = GDScriptParser::DataType::BUILTIN;
@ -2710,37 +2710,44 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
scr = obj->get_script(); scr = obj->get_script();
} }
if (scr.is_valid()) { if (scr.is_valid()) {
result.script_type = scr; if (scr->is_valid()) {
result.script_path = scr->get_path(); result.script_type = scr;
Ref<GDScript> gds = scr; result.script_path = scr->get_path();
if (gds.is_valid()) { Ref<GDScript> gds = scr;
result.kind = GDScriptParser::DataType::CLASS; if (gds.is_valid()) {
// This might be an inner class, so we want to get the parser for the root. result.kind = GDScriptParser::DataType::CLASS;
// But still get the inner class from that tree. // This might be an inner class, so we want to get the parser for the root.
GDScript *current = gds.ptr(); // But still get the inner class from that tree.
List<StringName> class_chain; GDScript *current = gds.ptr();
while (current->_owner) { List<StringName> class_chain;
// Push to front so it's in reverse. while (current->_owner) {
class_chain.push_front(current->name); // Push to front so it's in reverse.
current = current->_owner; class_chain.push_front(current->name);
current = current->_owner;
}
Ref<GDScriptParserRef> ref = get_parser_for(current->path);
ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
GDScriptParser::ClassNode *found = ref->get_parser()->head;
// It should be okay to assume this exists, since we have a complete script already.
for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
found = found->get_member(E->get()).m_class;
}
result.class_type = found;
result.script_path = ref->get_parser()->script_path;
} else {
result.kind = GDScriptParser::DataType::SCRIPT;
} }
result.native_type = scr->get_instance_base_type();
Ref<GDScriptParserRef> ref = get_parser_for(current->path);
ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
GDScriptParser::ClassNode *found = ref->get_parser()->head;
// It should be okay to assume this exists, since we have a complete script already.
for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
found = found->get_member(E->get()).m_class;
}
result.class_type = found;
result.script_path = ref->get_parser()->script_path;
} else { } else {
result.kind = GDScriptParser::DataType::SCRIPT; push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source);
result.kind = GDScriptParser::DataType::VARIANT;
result.type_source = GDScriptParser::DataType::UNDETECTED;
result.is_meta_type = false;
} }
result.native_type = scr->get_instance_base_type();
} else { } else {
result.kind = GDScriptParser::DataType::NATIVE; result.kind = GDScriptParser::DataType::NATIVE;
if (result.native_type == GDScriptNativeClass::get_class_static()) { if (result.native_type == GDScriptNativeClass::get_class_static()) {
@ -2996,7 +3003,7 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
} }
#endif #endif
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid) { GDScriptParser::DataType GDScriptAnalyzer::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) {
// This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations. // This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
GDScriptParser::DataType result; GDScriptParser::DataType result;
@ -3069,7 +3076,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
Variant::evaluate(p_operation, a, b, ret, r_valid); Variant::evaluate(p_operation, a, b, ret, r_valid);
if (r_valid) { if (r_valid) {
return type_from_variant(ret); return type_from_variant(ret, p_source);
} }
return result; return result;

View File

@ -90,7 +90,7 @@ class GDScriptAnalyzer {
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
// Helpers. // Helpers.
GDScriptParser::DataType type_from_variant(const Variant &p_value); GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const; GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name); GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name);
@ -98,7 +98,7 @@ class GDScriptAnalyzer {
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call); bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call); bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid); 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);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const; bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
void push_error(const String &p_message, const GDScriptParser::Node *p_origin); void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node); void mark_node_unsafe(const GDScriptParser::Node *p_node);