Merge pull request #43890 from vnen/gdscript-builtin-functions-refactor
GDScript: Refactor builtin functions
This commit is contained in:
commit
abfc528439
@ -2122,8 +2122,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
|
||||
w++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
|
||||
p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
|
||||
List<StringName> functions;
|
||||
GDScriptUtilityFunctions::get_function_list(&functions);
|
||||
|
||||
for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
|
||||
p_words->push_back(String(E->get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,8 +72,8 @@ class GDScript : public Script {
|
||||
friend class GDScriptFunction;
|
||||
friend class GDScriptAnalyzer;
|
||||
friend class GDScriptCompiler;
|
||||
friend class GDScriptFunctions;
|
||||
friend class GDScriptLanguage;
|
||||
friend struct GDScriptUtilityFunctionsDefinitions;
|
||||
|
||||
Ref<GDScriptNativeClass> native;
|
||||
Ref<GDScript> base;
|
||||
@ -270,8 +270,8 @@ public:
|
||||
class GDScriptInstance : public ScriptInstance {
|
||||
friend class GDScript;
|
||||
friend class GDScriptFunction;
|
||||
friend class GDScriptFunctions;
|
||||
friend class GDScriptCompiler;
|
||||
friend struct GDScriptUtilityFunctionsDefinitions;
|
||||
|
||||
ObjectID owner_id;
|
||||
Object *owner;
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "gdscript.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
// TODO: Move this to a central location (maybe core?).
|
||||
static HashMap<StringName, StringName> underscore_map;
|
||||
@ -72,6 +73,39 @@ static StringName get_real_class_name(const StringName &p_source) {
|
||||
return p_source;
|
||||
}
|
||||
|
||||
static MethodInfo info_from_utility_func(const StringName &p_function) {
|
||||
ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
|
||||
|
||||
MethodInfo info(p_function);
|
||||
|
||||
if (Variant::has_utility_function_return_value(p_function)) {
|
||||
info.return_val.type = Variant::get_utility_function_return_type(p_function);
|
||||
if (info.return_val.type == Variant::NIL) {
|
||||
info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
}
|
||||
}
|
||||
|
||||
if (Variant::is_utility_function_vararg(p_function)) {
|
||||
info.flags |= METHOD_FLAG_VARARG;
|
||||
} else {
|
||||
for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) {
|
||||
PropertyInfo pi;
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
pi.name = Variant::get_utility_function_argument_name(p_function, i);
|
||||
#else
|
||||
pi.name = "arg" + itos(i + 1);
|
||||
#endif
|
||||
pi.type = Variant::get_utility_function_argument_type(p_function, i);
|
||||
if (pi.type == Variant::NIL) {
|
||||
pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
}
|
||||
info.arguments.push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::cleanup() {
|
||||
underscore_map.clear();
|
||||
}
|
||||
@ -1701,7 +1735,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
|
||||
// Call to name directly.
|
||||
StringName function_name = p_call->function_name;
|
||||
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
|
||||
GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
|
||||
|
||||
if (builtin_type < Variant::VARIANT_MAX) {
|
||||
// Is a builtin constructor.
|
||||
@ -1843,10 +1876,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
|
||||
}
|
||||
p_call->set_datatype(call_type);
|
||||
return;
|
||||
} else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
|
||||
MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
|
||||
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
|
||||
MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
|
||||
|
||||
if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
|
||||
if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
|
||||
// Can call on compilation.
|
||||
Vector<const Variant *> args;
|
||||
for (int i = 0; i < p_call->arguments.size(); i++) {
|
||||
@ -1855,23 +1888,65 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
|
||||
|
||||
Variant value;
|
||||
Callable::CallError err;
|
||||
GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
|
||||
GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
|
||||
|
||||
switch (err.error) {
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
|
||||
PropertyInfo wrong_arg = function_info.arguments[err.argument];
|
||||
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
|
||||
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
|
||||
type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
|
||||
p_call->arguments[err.argument]);
|
||||
} break;
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
|
||||
push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
|
||||
push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
|
||||
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
break; // Can't happen in a builtin constructor.
|
||||
case Callable::CallError::CALL_OK:
|
||||
p_call->is_constant = true;
|
||||
p_call->reduced_value = value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
validate_call_arg(function_info, p_call);
|
||||
}
|
||||
p_call->set_datatype(type_from_property(function_info.return_val));
|
||||
return;
|
||||
} else if (Variant::has_utility_function(function_name)) {
|
||||
MethodInfo function_info = info_from_utility_func(function_name);
|
||||
|
||||
if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
|
||||
// Can call on compilation.
|
||||
Vector<const Variant *> args;
|
||||
for (int i = 0; i < p_call->arguments.size(); i++) {
|
||||
args.push_back(&(p_call->arguments[i]->reduced_value));
|
||||
}
|
||||
|
||||
Variant value;
|
||||
Callable::CallError err;
|
||||
Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
|
||||
|
||||
switch (err.error) {
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
|
||||
PropertyInfo wrong_arg = function_info.arguments[err.argument];
|
||||
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
|
||||
type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
|
||||
p_call->arguments[err.argument]);
|
||||
} break;
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
|
||||
break;
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
break; // Can't happen in a builtin constructor.
|
||||
@ -2385,7 +2460,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
||||
|
||||
// Not found.
|
||||
// Check if it's a builtin function.
|
||||
if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
|
||||
if (GDScriptUtilityFunctions::function_exists(name)) {
|
||||
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
|
||||
} else {
|
||||
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
|
||||
|
@ -283,6 +283,30 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
|
||||
function->_constructors_count = 0;
|
||||
}
|
||||
|
||||
if (utilities_map.size()) {
|
||||
function->utilities.resize(utilities_map.size());
|
||||
function->_utilities_ptr = function->utilities.ptr();
|
||||
function->_utilities_count = utilities_map.size();
|
||||
for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) {
|
||||
function->utilities.write[E->get()] = E->key();
|
||||
}
|
||||
} else {
|
||||
function->_utilities_ptr = nullptr;
|
||||
function->_utilities_count = 0;
|
||||
}
|
||||
|
||||
if (gds_utilities_map.size()) {
|
||||
function->gds_utilities.resize(gds_utilities_map.size());
|
||||
function->_gds_utilities_ptr = function->gds_utilities.ptr();
|
||||
function->_gds_utilities_count = gds_utilities_map.size();
|
||||
for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) {
|
||||
function->gds_utilities.write[E->get()] = E->key();
|
||||
}
|
||||
} else {
|
||||
function->_gds_utilities_ptr = nullptr;
|
||||
function->_gds_utilities_count = 0;
|
||||
}
|
||||
|
||||
if (method_bind_map.size()) {
|
||||
function->methods.resize(method_bind_map.size());
|
||||
function->_methods_ptr = function->methods.ptrw();
|
||||
@ -704,8 +728,8 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const
|
||||
append(p_function_name);
|
||||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
|
||||
append(GDScriptFunction::OPCODE_CALL_BUILT_IN, 1 + p_arguments.size());
|
||||
void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
|
||||
append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
|
||||
for (int i = 0; i < p_arguments.size(); i++) {
|
||||
append(p_arguments[i]);
|
||||
}
|
||||
@ -714,6 +738,41 @@ void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDSc
|
||||
append(p_function);
|
||||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
|
||||
bool is_validated = true;
|
||||
if (Variant::is_utility_function_vararg(p_function)) {
|
||||
is_validated = true; // Vararg works fine with any argument, since they can be any type.
|
||||
} else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) {
|
||||
bool all_types_exact = true;
|
||||
for (int i = 0; i < p_arguments.size(); i++) {
|
||||
if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_utility_function_argument_type(p_function, i))) {
|
||||
all_types_exact = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
is_validated = all_types_exact;
|
||||
}
|
||||
|
||||
if (is_validated) {
|
||||
append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
|
||||
for (int i = 0; i < p_arguments.size(); i++) {
|
||||
append(p_arguments[i]);
|
||||
}
|
||||
append(p_target);
|
||||
append(p_arguments.size());
|
||||
append(Variant::get_validated_utility_function(p_function));
|
||||
} else {
|
||||
append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
|
||||
for (int i = 0; i < p_arguments.size(); i++) {
|
||||
append(p_arguments[i]);
|
||||
}
|
||||
append(p_target);
|
||||
append(p_arguments.size());
|
||||
append(p_function);
|
||||
}
|
||||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
|
||||
bool is_validated = false;
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "gdscript_codegen.h"
|
||||
|
||||
#include "gdscript_function.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
||||
bool ended = false;
|
||||
@ -76,6 +77,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
||||
Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
|
||||
Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
|
||||
Map<Variant::ValidatedConstructor, int> constructors_map;
|
||||
Map<Variant::ValidatedUtilityFunction, int> utilities_map;
|
||||
Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
|
||||
Map<MethodBind *, int> method_bind_map;
|
||||
|
||||
// Lists since these can be nested.
|
||||
@ -241,6 +244,24 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
||||
return pos;
|
||||
}
|
||||
|
||||
int get_utility_pos(const Variant::ValidatedUtilityFunction p_utility) {
|
||||
if (utilities_map.has(p_utility)) {
|
||||
return utilities_map[p_utility];
|
||||
}
|
||||
int pos = utilities_map.size();
|
||||
utilities_map[p_utility] = pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
int get_gds_utility_pos(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
|
||||
if (gds_utilities_map.has(p_gds_utility)) {
|
||||
return gds_utilities_map[p_gds_utility];
|
||||
}
|
||||
int pos = gds_utilities_map.size();
|
||||
gds_utilities_map[p_gds_utility] = pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
int get_method_bind_pos(MethodBind *p_method) {
|
||||
if (method_bind_map.has(p_method)) {
|
||||
return method_bind_map[p_method];
|
||||
@ -346,6 +367,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
||||
opcodes.push_back(get_constructor_pos(p_constructor));
|
||||
}
|
||||
|
||||
void append(const Variant::ValidatedUtilityFunction p_utility) {
|
||||
opcodes.push_back(get_utility_pos(p_utility));
|
||||
}
|
||||
|
||||
void append(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
|
||||
opcodes.push_back(get_gds_utility_pos(p_gds_utility));
|
||||
}
|
||||
|
||||
void append(MethodBind *p_method) {
|
||||
opcodes.push_back(get_method_bind_pos(p_method));
|
||||
}
|
||||
@ -406,7 +435,8 @@ public:
|
||||
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "core/string/string_name.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "gdscript_function.h"
|
||||
#include "gdscript_functions.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
class GDScriptCodeGenerator {
|
||||
public:
|
||||
@ -127,7 +127,8 @@ public:
|
||||
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "gdscript.h"
|
||||
#include "gdscript_byte_codegen.h"
|
||||
#include "gdscript_cache.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
|
||||
if (codegen.function_node && codegen.function_node->is_static) {
|
||||
@ -456,15 +457,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
||||
arguments.push_back(arg);
|
||||
}
|
||||
|
||||
if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
|
||||
if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
|
||||
// Construct a built-in type.
|
||||
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
|
||||
|
||||
gen->write_construct(result, vtype, arguments);
|
||||
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
|
||||
// Built-in function.
|
||||
GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
|
||||
gen->write_call_builtin(result, func, arguments);
|
||||
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
|
||||
// Variant utility function.
|
||||
gen->write_call_utility(result, call->function_name, arguments);
|
||||
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
|
||||
// GDScript utility function.
|
||||
gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
|
||||
} else {
|
||||
// Regular function.
|
||||
const GDScriptParser::ExpressionNode *callee = call->callee;
|
||||
@ -1135,7 +1138,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
|
||||
// Evaluate expression type.
|
||||
Vector<GDScriptCodeGenerator::Address> typeof_args;
|
||||
typeof_args.push_back(expr_addr);
|
||||
codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
|
||||
codegen.generator->write_call_utility(result_addr, "typeof", typeof_args);
|
||||
|
||||
// Check type equality.
|
||||
codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
|
||||
@ -1199,7 +1202,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
|
||||
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
|
||||
Vector<GDScriptCodeGenerator::Address> len_args;
|
||||
len_args.push_back(p_value_addr);
|
||||
codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
|
||||
codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args);
|
||||
|
||||
// Test length compatibility.
|
||||
temp_type.builtin_type = Variant::BOOL;
|
||||
@ -1253,7 +1256,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
|
||||
// Also get type of element.
|
||||
Vector<GDScriptCodeGenerator::Address> typeof_args;
|
||||
typeof_args.push_back(element_addr);
|
||||
codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
|
||||
codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
|
||||
|
||||
// Try the pattern inside the element.
|
||||
test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
|
||||
@ -1298,7 +1301,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
|
||||
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
|
||||
Vector<GDScriptCodeGenerator::Address> func_args;
|
||||
func_args.push_back(p_value_addr);
|
||||
codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
|
||||
codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args);
|
||||
|
||||
// Test length compatibility.
|
||||
temp_type.builtin_type = Variant::BOOL;
|
||||
@ -1367,7 +1370,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
|
||||
// Also get type of value.
|
||||
func_args.clear();
|
||||
func_args.push_back(element_addr);
|
||||
codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
|
||||
codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
|
||||
|
||||
// Try the pattern inside the value.
|
||||
test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
|
||||
@ -1500,7 +1503,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
|
||||
|
||||
Vector<GDScriptCodeGenerator::Address> typeof_args;
|
||||
typeof_args.push_back(value);
|
||||
gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
|
||||
gen->write_call_utility(type, "typeof", typeof_args);
|
||||
|
||||
// Now we can actually start testing.
|
||||
// For each branch.
|
||||
|
@ -34,7 +34,6 @@
|
||||
|
||||
#include "core/string/string_builder.h"
|
||||
#include "gdscript.h"
|
||||
#include "gdscript_functions.h"
|
||||
|
||||
static String _get_variant_string(const Variant &p_variant) {
|
||||
String txt;
|
||||
@ -607,13 +606,49 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
||||
|
||||
incr = 5 + argc;
|
||||
} break;
|
||||
case OPCODE_CALL_BUILT_IN: {
|
||||
text += "call-built-in ";
|
||||
case OPCODE_CALL_UTILITY: {
|
||||
text += "call-utility ";
|
||||
|
||||
int argc = _code_ptr[ip + 1 + instr_var_args];
|
||||
text += DADDR(1 + argc) + " = ";
|
||||
|
||||
text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args]));
|
||||
text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
|
||||
text += "(";
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0)
|
||||
text += ", ";
|
||||
text += DADDR(1 + i);
|
||||
}
|
||||
text += ")";
|
||||
|
||||
incr = 4 + argc;
|
||||
} break;
|
||||
case OPCODE_CALL_UTILITY_VALIDATED: {
|
||||
text += "call-utility ";
|
||||
|
||||
int argc = _code_ptr[ip + 1 + instr_var_args];
|
||||
text += DADDR(1 + argc) + " = ";
|
||||
|
||||
text += "<unkown function>";
|
||||
text += "(";
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0)
|
||||
text += ", ";
|
||||
text += DADDR(1 + i);
|
||||
}
|
||||
text += ")";
|
||||
|
||||
incr = 4 + argc;
|
||||
} break;
|
||||
case OPCODE_CALL_GDSCRIPT_UTILITY: {
|
||||
text += "call-gscript-utility ";
|
||||
|
||||
int argc = _code_ptr[ip + 1 + instr_var_args];
|
||||
text += DADDR(1 + argc) + " = ";
|
||||
|
||||
text += "<unknown function>";
|
||||
text += "(";
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "gdscript_compiler.h"
|
||||
#include "gdscript_parser.h"
|
||||
#include "gdscript_tokenizer.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/config/project_settings.h"
|
||||
@ -411,11 +412,14 @@ void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) con
|
||||
}
|
||||
|
||||
void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
|
||||
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
|
||||
p_functions->push_back(GDScriptFunctions::get_info(GDScriptFunctions::Function(i)));
|
||||
List<StringName> functions;
|
||||
GDScriptUtilityFunctions::get_function_list(&functions);
|
||||
|
||||
for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
|
||||
p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E->get()));
|
||||
}
|
||||
|
||||
//not really "functions", but..
|
||||
// Not really "functions", but show in documentation.
|
||||
{
|
||||
MethodInfo mi;
|
||||
mi.name = "preload";
|
||||
@ -1030,9 +1034,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
|
||||
_find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
|
||||
MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
|
||||
ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
|
||||
List<StringName> functions;
|
||||
GDScriptUtilityFunctions::get_function_list(&functions);
|
||||
|
||||
for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
|
||||
MethodInfo function = GDScriptUtilityFunctions::get_function_info(E->get());
|
||||
ScriptCodeCompletionOption option(String(E->get()), ScriptCodeCompletionOption::KIND_FUNCTION);
|
||||
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
|
||||
option.insert_text += "(";
|
||||
} else {
|
||||
@ -1288,8 +1295,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
|
||||
r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name);
|
||||
found = true;
|
||||
break;
|
||||
} else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
|
||||
MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
|
||||
} else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
|
||||
MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name);
|
||||
r_type = _type_from_property(mi.return_val);
|
||||
found = true;
|
||||
break;
|
||||
@ -2342,8 +2349,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
|
||||
|
||||
GDScriptCompletionIdentifier connect_base;
|
||||
|
||||
if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
|
||||
MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
|
||||
if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
|
||||
MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
|
||||
r_arghint = _make_arguments_hint(info, p_argidx);
|
||||
return;
|
||||
} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
|
||||
@ -2971,13 +2978,11 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
|
||||
if (GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)) == p_symbol) {
|
||||
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
|
||||
r_result.class_name = "@GDScript";
|
||||
r_result.class_member = p_symbol;
|
||||
return OK;
|
||||
}
|
||||
if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
|
||||
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
|
||||
r_result.class_name = "@GDScript";
|
||||
r_result.class_member = p_symbol;
|
||||
return OK;
|
||||
}
|
||||
|
||||
if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "core/templates/pair.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
class GDScriptInstance;
|
||||
class GDScript;
|
||||
@ -190,7 +191,9 @@ public:
|
||||
OPCODE_CALL,
|
||||
OPCODE_CALL_RETURN,
|
||||
OPCODE_CALL_ASYNC,
|
||||
OPCODE_CALL_BUILT_IN,
|
||||
OPCODE_CALL_UTILITY,
|
||||
OPCODE_CALL_UTILITY_VALIDATED,
|
||||
OPCODE_CALL_GDSCRIPT_UTILITY,
|
||||
OPCODE_CALL_BUILTIN_TYPE_VALIDATED,
|
||||
OPCODE_CALL_SELF_BASE,
|
||||
OPCODE_CALL_METHOD_BIND,
|
||||
@ -344,6 +347,10 @@ private:
|
||||
const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
|
||||
int _constructors_count = 0;
|
||||
const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
|
||||
int _utilities_count = 0;
|
||||
const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
|
||||
int _gds_utilities_count = 0;
|
||||
const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
|
||||
int _methods_count = 0;
|
||||
MethodBind **_methods_ptr = nullptr;
|
||||
const int *_code_ptr = nullptr;
|
||||
@ -372,6 +379,8 @@ private:
|
||||
Vector<Variant::ValidatedIndexedGetter> indexed_getters;
|
||||
Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
|
||||
Vector<Variant::ValidatedConstructor> constructors;
|
||||
Vector<Variant::ValidatedUtilityFunction> utilities;
|
||||
Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
|
||||
Vector<MethodBind *> methods;
|
||||
Vector<int> code;
|
||||
Vector<GDScriptDataType> argument_types;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -98,15 +98,6 @@ void GDScriptParser::cleanup() {
|
||||
builtin_types.clear();
|
||||
}
|
||||
|
||||
GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) {
|
||||
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
|
||||
if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) {
|
||||
return GDScriptFunctions::Function(i);
|
||||
}
|
||||
}
|
||||
return GDScriptFunctions::FUNC_MAX;
|
||||
}
|
||||
|
||||
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
|
||||
List<StringName> keys;
|
||||
valid_annotations.get_key_list(&keys);
|
||||
@ -2553,7 +2544,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
|
||||
|
||||
// Arguments.
|
||||
CompletionType ct = COMPLETION_CALL_ARGUMENTS;
|
||||
if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) {
|
||||
if (call->function_name == "load") {
|
||||
ct = COMPLETION_RESOURCE_PATH;
|
||||
}
|
||||
push_completion_call(call);
|
||||
|
@ -43,7 +43,6 @@
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "gdscript_cache.h"
|
||||
#include "gdscript_functions.h"
|
||||
#include "gdscript_tokenizer.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
@ -1314,7 +1313,6 @@ public:
|
||||
ClassNode *get_tree() const { return head; }
|
||||
bool is_tool() const { return _is_tool; }
|
||||
static Variant::Type get_builtin_type(const StringName &p_type);
|
||||
static GDScriptFunctions::Function get_builtin_function(const StringName &p_name);
|
||||
|
||||
CompletionContext get_completion_context() const { return completion_context; }
|
||||
CompletionCall get_completion_call() const { return completion_call; }
|
||||
|
718
modules/gdscript/gdscript_utility_functions.cpp
Normal file
718
modules/gdscript/gdscript_utility_functions.cpp
Normal file
@ -0,0 +1,718 @@
|
||||
/*************************************************************************/
|
||||
/* gdscript_utility_functions.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/object/method_bind.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/templates/oa_hash_map.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "gdscript.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
#define VALIDATE_ARG_COUNT(m_count) \
|
||||
if (p_arg_count < m_count) { \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
|
||||
r_error.argument = m_count; \
|
||||
r_error.expected = m_count; \
|
||||
*r_ret = Variant(); \
|
||||
return; \
|
||||
} \
|
||||
if (p_arg_count > m_count) { \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
|
||||
r_error.argument = m_count; \
|
||||
r_error.expected = m_count; \
|
||||
*r_ret = Variant(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define VALIDATE_ARG_INT(m_arg) \
|
||||
if (p_args[m_arg]->get_type() != Variant::INT) { \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
|
||||
r_error.argument = m_arg; \
|
||||
r_error.expected = Variant::INT; \
|
||||
*r_ret = Variant(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define VALIDATE_ARG_NUM(m_arg) \
|
||||
if (!p_args[m_arg]->is_num()) { \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
|
||||
r_error.argument = m_arg; \
|
||||
r_error.expected = Variant::FLOAT; \
|
||||
*r_ret = Variant(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define VALIDATE_ARG_COUNT(m_count)
|
||||
#define VALIDATE_ARG_INT(m_arg)
|
||||
#define VALIDATE_ARG_NUM(m_arg)
|
||||
|
||||
#endif
|
||||
|
||||
struct GDScriptUtilityFunctionsDefinitions {
|
||||
static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(2);
|
||||
VALIDATE_ARG_INT(1);
|
||||
int type = *p_args[1];
|
||||
if (type < 0 || type >= Variant::VARIANT_MAX) {
|
||||
*r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::INT;
|
||||
return;
|
||||
|
||||
} else {
|
||||
Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
*r_ret = ClassDB::class_exists(*p_args[0]);
|
||||
}
|
||||
|
||||
static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
VALIDATE_ARG_INT(0);
|
||||
char32_t result[2] = { *p_args[0], 0 };
|
||||
*r_ret = String(result);
|
||||
}
|
||||
|
||||
static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
if (p_arg_count < 1) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.argument = 1;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
|
||||
String str;
|
||||
for (int i = 0; i < p_arg_count; i++) {
|
||||
String os = p_args[i]->operator String();
|
||||
|
||||
if (i == 0) {
|
||||
str = os;
|
||||
} else {
|
||||
str += os;
|
||||
}
|
||||
}
|
||||
*r_ret = str;
|
||||
}
|
||||
|
||||
static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
switch (p_arg_count) {
|
||||
case 0: {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.argument = 1;
|
||||
r_error.expected = 1;
|
||||
*r_ret = Variant();
|
||||
} break;
|
||||
case 1: {
|
||||
VALIDATE_ARG_NUM(0);
|
||||
int count = *p_args[0];
|
||||
Array arr;
|
||||
if (count <= 0) {
|
||||
*r_ret = arr;
|
||||
return;
|
||||
}
|
||||
Error err = arr.resize(count);
|
||||
if (err != OK) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
arr[i] = i;
|
||||
}
|
||||
|
||||
*r_ret = arr;
|
||||
} break;
|
||||
case 2: {
|
||||
VALIDATE_ARG_NUM(0);
|
||||
VALIDATE_ARG_NUM(1);
|
||||
|
||||
int from = *p_args[0];
|
||||
int to = *p_args[1];
|
||||
|
||||
Array arr;
|
||||
if (from >= to) {
|
||||
*r_ret = arr;
|
||||
return;
|
||||
}
|
||||
Error err = arr.resize(to - from);
|
||||
if (err != OK) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
for (int i = from; i < to; i++) {
|
||||
arr[i - from] = i;
|
||||
}
|
||||
*r_ret = arr;
|
||||
} break;
|
||||
case 3: {
|
||||
VALIDATE_ARG_NUM(0);
|
||||
VALIDATE_ARG_NUM(1);
|
||||
VALIDATE_ARG_NUM(2);
|
||||
|
||||
int from = *p_args[0];
|
||||
int to = *p_args[1];
|
||||
int incr = *p_args[2];
|
||||
if (incr == 0) {
|
||||
*r_ret = RTR("Step argument is zero!");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
return;
|
||||
}
|
||||
|
||||
Array arr;
|
||||
if (from >= to && incr > 0) {
|
||||
*r_ret = arr;
|
||||
return;
|
||||
}
|
||||
if (from <= to && incr < 0) {
|
||||
*r_ret = arr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate how many.
|
||||
int count = 0;
|
||||
if (incr > 0) {
|
||||
count = ((to - from - 1) / incr) + 1;
|
||||
} else {
|
||||
count = ((from - to - 1) / -incr) + 1;
|
||||
}
|
||||
|
||||
Error err = arr.resize(count);
|
||||
|
||||
if (err != OK) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
|
||||
if (incr > 0) {
|
||||
int idx = 0;
|
||||
for (int i = from; i < to; i += incr) {
|
||||
arr[idx++] = i;
|
||||
}
|
||||
} else {
|
||||
int idx = 0;
|
||||
for (int i = from; i > to; i += incr) {
|
||||
arr[idx++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
*r_ret = arr;
|
||||
} break;
|
||||
default: {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
r_error.argument = 3;
|
||||
r_error.expected = 3;
|
||||
*r_ret = Variant();
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
if (p_args[0]->get_type() != Variant::STRING) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::STRING;
|
||||
*r_ret = Variant();
|
||||
} else {
|
||||
*r_ret = ResourceLoader::load(*p_args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
|
||||
if (p_args[0]->get_type() == Variant::NIL) {
|
||||
*r_ret = Variant();
|
||||
} else if (p_args[0]->get_type() != Variant::OBJECT) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
*r_ret = Variant();
|
||||
} else {
|
||||
Object *obj = *p_args[0];
|
||||
if (!obj) {
|
||||
*r_ret = Variant();
|
||||
|
||||
} else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::DICTIONARY;
|
||||
*r_ret = RTR("Not a script with an instance");
|
||||
return;
|
||||
} else {
|
||||
GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
|
||||
Ref<GDScript> base = ins->get_script();
|
||||
if (base.is_null()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::DICTIONARY;
|
||||
*r_ret = RTR("Not based on a script");
|
||||
return;
|
||||
}
|
||||
|
||||
GDScript *p = base.ptr();
|
||||
Vector<StringName> sname;
|
||||
|
||||
while (p->_owner) {
|
||||
sname.push_back(p->name);
|
||||
p = p->_owner;
|
||||
}
|
||||
sname.invert();
|
||||
|
||||
if (!p->path.is_resource_file()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::DICTIONARY;
|
||||
*r_ret = Variant();
|
||||
|
||||
*r_ret = RTR("Not based on a resource file");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NodePath cp(sname, Vector<StringName>(), false);
|
||||
|
||||
Dictionary d;
|
||||
d["@subpath"] = cp;
|
||||
d["@path"] = p->get_path();
|
||||
|
||||
for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
|
||||
if (!d.has(E->key())) {
|
||||
d[E->key()] = ins->members[E->get().index];
|
||||
}
|
||||
}
|
||||
*r_ret = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
|
||||
if (p_args[0]->get_type() != Variant::DICTIONARY) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::DICTIONARY;
|
||||
*r_ret = Variant();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary d = *p_args[0];
|
||||
|
||||
if (!d.has("@path")) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::OBJECT;
|
||||
*r_ret = RTR("Invalid instance dictionary format (missing @path)");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Script> scr = ResourceLoader::load(d["@path"]);
|
||||
if (!scr.is_valid()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::OBJECT;
|
||||
*r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<GDScript> gdscr = scr;
|
||||
|
||||
if (!gdscr.is_valid()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::OBJECT;
|
||||
*r_ret = Variant();
|
||||
*r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
|
||||
return;
|
||||
}
|
||||
|
||||
NodePath sub;
|
||||
if (d.has("@subpath")) {
|
||||
sub = d["@subpath"];
|
||||
}
|
||||
|
||||
for (int i = 0; i < sub.get_name_count(); i++) {
|
||||
gdscr = gdscr->subclasses[sub.get_name(i)];
|
||||
if (!gdscr.is_valid()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::OBJECT;
|
||||
*r_ret = Variant();
|
||||
*r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
*r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
|
||||
|
||||
if (r_error.error != Callable::CallError::CALL_OK) {
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
|
||||
GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
|
||||
Ref<GDScript> gd_ref = ins->get_script();
|
||||
|
||||
for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
|
||||
if (d.has(E->key())) {
|
||||
ins->members.write[E->get().index] = d[E->key()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
if (p_arg_count < 3) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.argument = 3;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
if (p_arg_count > 4) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
r_error.argument = 4;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
|
||||
VALIDATE_ARG_INT(0);
|
||||
VALIDATE_ARG_INT(1);
|
||||
VALIDATE_ARG_INT(2);
|
||||
|
||||
Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f);
|
||||
|
||||
if (p_arg_count == 4) {
|
||||
VALIDATE_ARG_INT(3);
|
||||
color.a = (int64_t)*p_args[3] / 255.0f;
|
||||
}
|
||||
|
||||
*r_ret = color;
|
||||
}
|
||||
|
||||
static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
String str;
|
||||
for (int i = 0; i < p_arg_count; i++) {
|
||||
str += p_args[i]->operator String();
|
||||
}
|
||||
|
||||
ScriptLanguage *script = GDScriptLanguage::get_singleton();
|
||||
if (script->debug_get_stack_level_count() > 0) {
|
||||
str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
|
||||
}
|
||||
|
||||
print_line(str);
|
||||
*r_ret = Variant();
|
||||
}
|
||||
|
||||
static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(0);
|
||||
|
||||
ScriptLanguage *script = GDScriptLanguage::get_singleton();
|
||||
for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
|
||||
print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
|
||||
};
|
||||
}
|
||||
|
||||
static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(0);
|
||||
|
||||
ScriptLanguage *script = GDScriptLanguage::get_singleton();
|
||||
Array ret;
|
||||
for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
|
||||
Dictionary frame;
|
||||
frame["source"] = script->debug_get_stack_level_source(i);
|
||||
frame["function"] = script->debug_get_stack_level_function(i);
|
||||
frame["line"] = script->debug_get_stack_level_line(i);
|
||||
ret.push_back(frame);
|
||||
};
|
||||
*r_ret = ret;
|
||||
}
|
||||
|
||||
static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
switch (p_args[0]->get_type()) {
|
||||
case Variant::STRING: {
|
||||
String d = *p_args[0];
|
||||
*r_ret = d.length();
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
Dictionary d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
Array d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_BYTE_ARRAY: {
|
||||
Vector<uint8_t> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_INT32_ARRAY: {
|
||||
Vector<int32_t> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_INT64_ARRAY: {
|
||||
Vector<int64_t> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_FLOAT32_ARRAY: {
|
||||
Vector<float> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_FLOAT64_ARRAY: {
|
||||
Vector<double> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_STRING_ARRAY: {
|
||||
Vector<String> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_VECTOR2_ARRAY: {
|
||||
Vector<Vector2> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_VECTOR3_ARRAY: {
|
||||
Vector<Vector3> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
case Variant::PACKED_COLOR_ARRAY: {
|
||||
Vector<Color> d = *p_args[0];
|
||||
*r_ret = d.size();
|
||||
} break;
|
||||
default: {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::NIL;
|
||||
*r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GDScriptUtilityFunctionInfo {
|
||||
GDScriptUtilityFunctions::FunctionPtr function;
|
||||
MethodInfo info;
|
||||
bool is_constant = false;
|
||||
};
|
||||
|
||||
static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table;
|
||||
static List<StringName> utility_function_name_table;
|
||||
|
||||
static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
|
||||
StringName sname(p_name);
|
||||
|
||||
ERR_FAIL_COND(utility_function_table.has(sname));
|
||||
|
||||
GDScriptUtilityFunctionInfo function;
|
||||
function.function = p_function;
|
||||
function.info = p_method_info;
|
||||
function.is_constant = p_is_const;
|
||||
|
||||
utility_function_table.insert(sname, function);
|
||||
utility_function_name_table.push_back(sname);
|
||||
}
|
||||
|
||||
#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
} \
|
||||
MethodInfo info = MethodInfo(name, __VA_ARGS__); \
|
||||
info.return_val.type = m_return_type; \
|
||||
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
|
||||
}
|
||||
|
||||
#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
} \
|
||||
MethodInfo info = MethodInfo(name); \
|
||||
info.return_val.type = m_return_type; \
|
||||
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
|
||||
}
|
||||
|
||||
#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
} \
|
||||
MethodInfo info = MethodInfo(name); \
|
||||
info.return_val.type = m_return_type; \
|
||||
info.flags |= METHOD_FLAG_VARARG; \
|
||||
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
|
||||
}
|
||||
|
||||
#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
} \
|
||||
MethodInfo info = MethodInfo(name, __VA_ARGS__); \
|
||||
info.return_val.type = Variant::NIL; \
|
||||
info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \
|
||||
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
|
||||
}
|
||||
|
||||
#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
} \
|
||||
MethodInfo info = MethodInfo(name, __VA_ARGS__); \
|
||||
info.return_val.type = Variant::OBJECT; \
|
||||
info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \
|
||||
info.return_val.class_name = m_return_type; \
|
||||
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
|
||||
}
|
||||
|
||||
#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
} \
|
||||
MethodInfo info = MethodInfo(name, __VA_ARGS__); \
|
||||
info.return_val.type = m_return_type; \
|
||||
info.default_arguments.push_back(m_default); \
|
||||
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
|
||||
}
|
||||
|
||||
#define ARG(m_name, m_type) \
|
||||
PropertyInfo(m_type, m_name)
|
||||
|
||||
#define VARARG(m_name) \
|
||||
PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
|
||||
|
||||
void GDScriptUtilityFunctions::register_functions() {
|
||||
REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
|
||||
REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME));
|
||||
REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT));
|
||||
REGISTER_VARARG_FUNC(str, true, Variant::STRING);
|
||||
REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
|
||||
REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
|
||||
REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
|
||||
REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
|
||||
REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
|
||||
REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
|
||||
REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
|
||||
REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY);
|
||||
REGISTER_FUNC(len, true, Variant::INT, VARARG("var"));
|
||||
}
|
||||
|
||||
void GDScriptUtilityFunctions::unregister_functions() {
|
||||
utility_function_name_table.clear();
|
||||
utility_function_table.clear();
|
||||
}
|
||||
|
||||
GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, nullptr);
|
||||
return info->function;
|
||||
}
|
||||
|
||||
bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, false);
|
||||
return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
|
||||
}
|
||||
|
||||
Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, Variant::NIL);
|
||||
return info->info.return_val.type;
|
||||
}
|
||||
|
||||
StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, StringName());
|
||||
return info->info.return_val.class_name;
|
||||
}
|
||||
|
||||
Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, Variant::NIL);
|
||||
ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL);
|
||||
return info->info.arguments[p_arg].type;
|
||||
}
|
||||
|
||||
int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, 0);
|
||||
return info->info.arguments.size();
|
||||
}
|
||||
|
||||
bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, false);
|
||||
return (bool)(info->info.flags & METHOD_FLAG_VARARG);
|
||||
}
|
||||
|
||||
bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, false);
|
||||
return info->is_constant;
|
||||
}
|
||||
|
||||
bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) {
|
||||
return utility_function_table.has(p_function);
|
||||
}
|
||||
|
||||
void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) {
|
||||
for (const List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) {
|
||||
r_functions->push_back(E->get());
|
||||
}
|
||||
}
|
||||
|
||||
MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) {
|
||||
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
|
||||
ERR_FAIL_COND_V(!info, MethodInfo());
|
||||
return info->info;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*************************************************************************/
|
||||
/* gdscript_functions.h */
|
||||
/* gdscript_utility_functions.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@ -28,110 +28,31 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef GDSCRIPT_FUNCTIONS_H
|
||||
#define GDSCRIPT_FUNCTIONS_H
|
||||
#ifndef GDSCRIPT_UTILITY_FUNCTIONS_H
|
||||
#define GDSCRIPT_UTILITY_FUNCTIONS_H
|
||||
|
||||
#include "core/string/string_name.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class GDScriptFunctions {
|
||||
class GDScriptUtilityFunctions {
|
||||
public:
|
||||
enum Function {
|
||||
MATH_SIN,
|
||||
MATH_COS,
|
||||
MATH_TAN,
|
||||
MATH_SINH,
|
||||
MATH_COSH,
|
||||
MATH_TANH,
|
||||
MATH_ASIN,
|
||||
MATH_ACOS,
|
||||
MATH_ATAN,
|
||||
MATH_ATAN2,
|
||||
MATH_SQRT,
|
||||
MATH_FMOD,
|
||||
MATH_FPOSMOD,
|
||||
MATH_POSMOD,
|
||||
MATH_FLOOR,
|
||||
MATH_CEIL,
|
||||
MATH_ROUND,
|
||||
MATH_ABS,
|
||||
MATH_SIGN,
|
||||
MATH_POW,
|
||||
MATH_LOG,
|
||||
MATH_EXP,
|
||||
MATH_ISNAN,
|
||||
MATH_ISINF,
|
||||
MATH_ISEQUALAPPROX,
|
||||
MATH_ISZEROAPPROX,
|
||||
MATH_EASE,
|
||||
MATH_STEP_DECIMALS,
|
||||
MATH_STEPIFY,
|
||||
MATH_LERP,
|
||||
MATH_LERP_ANGLE,
|
||||
MATH_INVERSE_LERP,
|
||||
MATH_RANGE_LERP,
|
||||
MATH_SMOOTHSTEP,
|
||||
MATH_MOVE_TOWARD,
|
||||
MATH_DECTIME,
|
||||
MATH_RANDOMIZE,
|
||||
MATH_RANDI,
|
||||
MATH_RANDF,
|
||||
MATH_RANDF_RANGE,
|
||||
MATH_RANDI_RANGE,
|
||||
MATH_SEED,
|
||||
MATH_RANDSEED,
|
||||
MATH_DEG2RAD,
|
||||
MATH_RAD2DEG,
|
||||
MATH_LINEAR2DB,
|
||||
MATH_DB2LINEAR,
|
||||
MATH_POLAR2CARTESIAN,
|
||||
MATH_CARTESIAN2POLAR,
|
||||
MATH_WRAP,
|
||||
MATH_WRAPF,
|
||||
LOGIC_MAX,
|
||||
LOGIC_MIN,
|
||||
LOGIC_CLAMP,
|
||||
LOGIC_NEAREST_PO2,
|
||||
OBJ_WEAKREF,
|
||||
TYPE_CONVERT,
|
||||
TYPE_OF,
|
||||
TYPE_EXISTS,
|
||||
TEXT_CHAR,
|
||||
TEXT_ORD,
|
||||
TEXT_STR,
|
||||
TEXT_PRINT,
|
||||
TEXT_PRINT_TABBED,
|
||||
TEXT_PRINT_SPACED,
|
||||
TEXT_PRINTERR,
|
||||
TEXT_PRINTRAW,
|
||||
TEXT_PRINT_DEBUG,
|
||||
PUSH_ERROR,
|
||||
PUSH_WARNING,
|
||||
VAR_TO_STR,
|
||||
STR_TO_VAR,
|
||||
VAR_TO_BYTES,
|
||||
BYTES_TO_VAR,
|
||||
GEN_RANGE,
|
||||
RESOURCE_LOAD,
|
||||
INST2DICT,
|
||||
DICT2INST,
|
||||
VALIDATE_JSON,
|
||||
PARSE_JSON,
|
||||
TO_JSON,
|
||||
HASH,
|
||||
COLOR8,
|
||||
COLORN,
|
||||
PRINT_STACK,
|
||||
GET_STACK,
|
||||
INSTANCE_FROM_ID,
|
||||
LEN,
|
||||
IS_INSTANCE_VALID,
|
||||
FUNC_MAX
|
||||
};
|
||||
typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
|
||||
|
||||
static const char *get_func_name(Function p_func);
|
||||
static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error);
|
||||
static bool is_deterministic(Function p_func);
|
||||
static MethodInfo get_info(Function p_func);
|
||||
static FunctionPtr get_function(const StringName &p_function);
|
||||
static bool has_function_return_value(const StringName &p_function);
|
||||
static Variant::Type get_function_return_type(const StringName &p_function);
|
||||
static StringName get_function_return_class(const StringName &p_function);
|
||||
static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
|
||||
static int get_function_argument_count(const StringName &p_function, int p_arg);
|
||||
static bool is_function_vararg(const StringName &p_function);
|
||||
static bool is_function_constant(const StringName &p_function);
|
||||
|
||||
static bool function_exists(const StringName &p_function);
|
||||
static void get_function_list(List<StringName> *r_functions);
|
||||
static MethodInfo get_function_info(const StringName &p_function);
|
||||
|
||||
static void register_functions();
|
||||
static void unregister_functions();
|
||||
};
|
||||
|
||||
#endif // GDSCRIPT_FUNCTIONS_H
|
||||
#endif // GDSCRIPT_UTILITY_FUNCTIONS_H
|
@ -33,7 +33,6 @@
|
||||
#include "core/core_string_names.h"
|
||||
#include "core/os/os.h"
|
||||
#include "gdscript.h"
|
||||
#include "gdscript_functions.h"
|
||||
|
||||
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
|
||||
int address = p_address & ADDR_MASK;
|
||||
@ -220,7 +219,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
|
||||
&&OPCODE_CALL, \
|
||||
&&OPCODE_CALL_RETURN, \
|
||||
&&OPCODE_CALL_ASYNC, \
|
||||
&&OPCODE_CALL_BUILT_IN, \
|
||||
&&OPCODE_CALL_UTILITY, \
|
||||
&&OPCODE_CALL_UTILITY_VALIDATED, \
|
||||
&&OPCODE_CALL_GDSCRIPT_UTILITY, \
|
||||
&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
|
||||
&&OPCODE_CALL_SELF_BASE, \
|
||||
&&OPCODE_CALL_METHOD_BIND, \
|
||||
@ -1749,7 +1750,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CALL_BUILT_IN) {
|
||||
OPCODE(OPCODE_CALL_UTILITY) {
|
||||
CHECK_SPACE(3 + instr_arg_count);
|
||||
|
||||
ip += instr_arg_count;
|
||||
@ -1757,22 +1758,80 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||
int argc = _code_ptr[ip + 1];
|
||||
GD_ERR_BREAK(argc < 0);
|
||||
|
||||
GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]);
|
||||
GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _global_names_count);
|
||||
StringName function = _global_names_ptr[_code_ptr[ip + 2]];
|
||||
|
||||
Variant **argptrs = instruction_args;
|
||||
|
||||
GET_INSTRUCTION_ARG(dst, argc);
|
||||
|
||||
Callable::CallError err;
|
||||
GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
|
||||
Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (err.error != Callable::CallError::CALL_OK) {
|
||||
String methodstr = GDScriptFunctions::get_func_name(func);
|
||||
String methodstr = function;
|
||||
if (dst->get_type() == Variant::STRING) {
|
||||
//call provided error string
|
||||
err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
|
||||
// Call provided error string.
|
||||
err_text = "Error calling utility function '" + methodstr + "': " + String(*dst);
|
||||
} else {
|
||||
err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
|
||||
err_text = _get_call_error(err, "utility function '" + methodstr + "'", (const Variant **)argptrs);
|
||||
}
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
ip += 3;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CALL_UTILITY_VALIDATED) {
|
||||
CHECK_SPACE(3 + instr_arg_count);
|
||||
|
||||
ip += instr_arg_count;
|
||||
|
||||
int argc = _code_ptr[ip + 1];
|
||||
GD_ERR_BREAK(argc < 0);
|
||||
|
||||
GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _utilities_count);
|
||||
Variant::ValidatedUtilityFunction function = _utilities_ptr[_code_ptr[ip + 2]];
|
||||
|
||||
Variant **argptrs = instruction_args;
|
||||
|
||||
GET_INSTRUCTION_ARG(dst, argc);
|
||||
|
||||
function(dst, (const Variant **)argptrs, argc);
|
||||
|
||||
ip += 3;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) {
|
||||
CHECK_SPACE(3 + instr_arg_count);
|
||||
|
||||
ip += instr_arg_count;
|
||||
|
||||
int argc = _code_ptr[ip + 1];
|
||||
GD_ERR_BREAK(argc < 0);
|
||||
|
||||
GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _gds_utilities_count);
|
||||
GDScriptUtilityFunctions::FunctionPtr function = _gds_utilities_ptr[_code_ptr[ip + 2]];
|
||||
|
||||
Variant **argptrs = instruction_args;
|
||||
|
||||
GET_INSTRUCTION_ARG(dst, argc);
|
||||
|
||||
Callable::CallError err;
|
||||
function(dst, (const Variant **)argptrs, argc, err);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (err.error != Callable::CallError::CALL_OK) {
|
||||
// TODO: Add this information in debug.
|
||||
String methodstr = "<unkown function>";
|
||||
if (dst->get_type() == Variant::STRING) {
|
||||
// Call provided error string.
|
||||
err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst);
|
||||
} else {
|
||||
err_text = _get_call_error(err, "GDScript utility function '" + methodstr + "'", (const Variant **)argptrs);
|
||||
}
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "gdscript_analyzer.h"
|
||||
#include "gdscript_cache.h"
|
||||
#include "gdscript_tokenizer.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
#ifdef TESTS_ENABLED
|
||||
#include "tests/test_gdscript.h"
|
||||
@ -130,6 +131,8 @@ void register_gdscript_types() {
|
||||
gdscript_translation_parser_plugin.instance();
|
||||
EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
GDScriptUtilityFunctions::register_functions();
|
||||
}
|
||||
|
||||
void unregister_gdscript_types() {
|
||||
@ -156,6 +159,7 @@ void unregister_gdscript_types() {
|
||||
|
||||
GDScriptParser::cleanup();
|
||||
GDScriptAnalyzer::cleanup();
|
||||
GDScriptUtilityFunctions::unregister_functions();
|
||||
}
|
||||
|
||||
#ifdef TESTS_ENABLED
|
||||
|
Loading…
Reference in New Issue
Block a user