mirror of
https://github.com/godotengine/godot.git
synced 2025-10-20 16:33:30 +00:00
Add typed instructions to GDScript
- Typed assignment (built-in, native, and script). - Cast (built-in conversion; native and script checks). - Check type of functions arguments on call. - Check type of members on set.
This commit is contained in:
parent
743053734f
commit
4b18c4e448
4 changed files with 469 additions and 10 deletions
|
@ -200,6 +200,12 @@ static String _get_var_type(const Variant *p_type) {
|
|||
&&OPCODE_ASSIGN, \
|
||||
&&OPCODE_ASSIGN_TRUE, \
|
||||
&&OPCODE_ASSIGN_FALSE, \
|
||||
&&OPCODE_ASSIGN_TYPED_BUILTIN, \
|
||||
&&OPCODE_ASSIGN_TYPED_NATIVE, \
|
||||
&&OPCODE_ASSIGN_TYPED_SCRIPT, \
|
||||
&&OPCODE_CAST_TO_BUILTIN, \
|
||||
&&OPCODE_CAST_TO_NATIVE, \
|
||||
&&OPCODE_CAST_TO_SCRIPT, \
|
||||
&&OPCODE_CONSTRUCT, \
|
||||
&&OPCODE_CONSTRUCT_ARRAY, \
|
||||
&&OPCODE_CONSTRUCT_DICTIONARY, \
|
||||
|
@ -318,10 +324,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
if (_stack_size) {
|
||||
|
||||
stack = (Variant *)aptr;
|
||||
for (int i = 0; i < p_argcount; i++)
|
||||
memnew_placement(&stack[i], Variant(*p_args[i]));
|
||||
for (int i = p_argcount; i < _stack_size; i++)
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
if (!argument_types[i].has_type) {
|
||||
memnew_placement(&stack[i], Variant(*p_args[i]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!argument_types[i].is_type(*p_args[i], true)) {
|
||||
r_err.error = Variant::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;
|
||||
return Variant();
|
||||
}
|
||||
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
|
||||
Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
|
||||
memnew_placement(&stack[i], Variant(arg));
|
||||
} else {
|
||||
memnew_placement(&stack[i], Variant(*p_args[i]));
|
||||
}
|
||||
}
|
||||
for (int i = p_argcount; i < _stack_size; i++) {
|
||||
memnew_placement(&stack[i], Variant);
|
||||
}
|
||||
} else {
|
||||
stack = NULL;
|
||||
}
|
||||
|
@ -709,6 +733,199 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
|
||||
|
||||
CHECK_SPACE(4);
|
||||
Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
|
||||
GET_VARIANT_PTR(dst, 2);
|
||||
GET_VARIANT_PTR(src, 3);
|
||||
|
||||
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
|
||||
|
||||
if (src->get_type() != var_type) {
|
||||
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
|
||||
"' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
*dst = *src;
|
||||
|
||||
ip += 4;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
|
||||
|
||||
CHECK_SPACE(4);
|
||||
GET_VARIANT_PTR(type, 1);
|
||||
GET_VARIANT_PTR(dst, 2);
|
||||
GET_VARIANT_PTR(src, 3);
|
||||
|
||||
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
|
||||
GD_ERR_BREAK(!nc);
|
||||
Object *src_obj = src->operator Object *();
|
||||
GD_ERR_BREAK(!src_obj);
|
||||
|
||||
if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
|
||||
err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
|
||||
"' to a variable of type '" + nc->get_name() + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
*dst = *src;
|
||||
|
||||
ip += 4;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
|
||||
|
||||
CHECK_SPACE(4);
|
||||
GET_VARIANT_PTR(type, 1);
|
||||
GET_VARIANT_PTR(dst, 2);
|
||||
GET_VARIANT_PTR(src, 3);
|
||||
|
||||
Script *base_type = Object::cast_to<Script>(type->operator Object *());
|
||||
|
||||
GD_ERR_BREAK(!base_type);
|
||||
|
||||
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
|
||||
err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
|
||||
|
||||
ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
|
||||
if (!scr_inst) {
|
||||
err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
|
||||
"' to a variable of type '" + base_type->get_path().get_file() + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
|
||||
bool valid = false;
|
||||
|
||||
while (src_type) {
|
||||
if (src_type == base_type) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
src_type = src_type->get_base_script().ptr();
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
|
||||
"' to a variable of type '" + base_type->get_path().get_file() + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
*dst = *src;
|
||||
|
||||
ip += 4;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CAST_TO_BUILTIN) {
|
||||
|
||||
CHECK_SPACE(4);
|
||||
Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
|
||||
GET_VARIANT_PTR(src, 2);
|
||||
GET_VARIANT_PTR(dst, 3);
|
||||
|
||||
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
|
||||
|
||||
Variant::CallError err;
|
||||
*dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (err.error != Variant::CallError::CALL_OK) {
|
||||
err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
|
||||
ip += 4;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CAST_TO_NATIVE) {
|
||||
|
||||
CHECK_SPACE(4);
|
||||
GET_VARIANT_PTR(to_type, 1);
|
||||
GET_VARIANT_PTR(src, 2);
|
||||
GET_VARIANT_PTR(dst, 3);
|
||||
|
||||
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
|
||||
GD_ERR_BREAK(!nc);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
|
||||
err_text = "Invalid cast: can't convert a non-object value to an object type.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
Object *src_obj = src->operator Object *();
|
||||
|
||||
if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
|
||||
*dst = Variant(); // invalid cast, assign NULL
|
||||
} else {
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
ip += 4;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CAST_TO_SCRIPT) {
|
||||
|
||||
CHECK_SPACE(4);
|
||||
GET_VARIANT_PTR(to_type, 1);
|
||||
GET_VARIANT_PTR(src, 2);
|
||||
GET_VARIANT_PTR(dst, 3);
|
||||
|
||||
Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
|
||||
|
||||
GD_ERR_BREAK(!base_type);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
|
||||
err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool valid = false;
|
||||
|
||||
if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
|
||||
|
||||
ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
|
||||
|
||||
if (scr_inst) {
|
||||
|
||||
Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
|
||||
|
||||
while (src_type) {
|
||||
if (src_type == base_type) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
src_type = src_type->get_base_script().ptr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
*dst = *src; // Valid cast, copy the source object
|
||||
} else {
|
||||
*dst = Variant(); // invalid cast, assign NULL
|
||||
}
|
||||
|
||||
ip += 4;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CONSTRUCT) {
|
||||
|
||||
CHECK_SPACE(2);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue