You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-17 14:11:06 +00:00
GDScript: Implement lambdas compilation and runtime
This commit is contained in:
@@ -270,6 +270,7 @@ public:
|
|||||||
class GDScriptInstance : public ScriptInstance {
|
class GDScriptInstance : public ScriptInstance {
|
||||||
friend class GDScript;
|
friend class GDScript;
|
||||||
friend class GDScriptFunction;
|
friend class GDScriptFunction;
|
||||||
|
friend class GDScriptLambdaCallable;
|
||||||
friend class GDScriptCompiler;
|
friend class GDScriptCompiler;
|
||||||
friend struct GDScriptUtilityFunctionsDefinitions;
|
friend struct GDScriptUtilityFunctionsDefinitions;
|
||||||
|
|
||||||
|
|||||||
@@ -2101,6 +2101,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
|
|||||||
|
|
||||||
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
|
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
|
||||||
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
|
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
|
||||||
|
} else if (is_self && !is_static && !lambda_stack.is_empty()) {
|
||||||
|
push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call->callee);
|
||||||
}
|
}
|
||||||
|
|
||||||
call_type = return_type;
|
call_type = return_type;
|
||||||
@@ -2223,6 +2225,8 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
|
|||||||
|
|
||||||
if (!ClassDB::is_parent_class(GDScriptParser::get_real_class_name(parser->current_class->base_type.native_type), result.native_type)) {
|
if (!ClassDB::is_parent_class(GDScriptParser::get_real_class_name(parser->current_class->base_type.native_type), result.native_type)) {
|
||||||
push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
|
push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
|
||||||
|
} else if (!lambda_stack.is_empty()) {
|
||||||
|
push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
p_get_node->set_datatype(result);
|
p_get_node->set_datatype(result);
|
||||||
@@ -2614,6 +2618,30 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
|
|||||||
|
|
||||||
resolve_suite(p_lambda->function->body);
|
resolve_suite(p_lambda->function->body);
|
||||||
|
|
||||||
|
int captures_amount = p_lambda->captures.size();
|
||||||
|
if (captures_amount > 0) {
|
||||||
|
// Create space for lambda parameters.
|
||||||
|
// At the beginning to not mess with optional parameters.
|
||||||
|
int param_count = p_lambda->function->parameters.size();
|
||||||
|
p_lambda->function->parameters.resize(param_count + captures_amount);
|
||||||
|
for (int i = param_count - 1; i >= 0; i--) {
|
||||||
|
p_lambda->function->parameters.write[i + captures_amount] = p_lambda->function->parameters[i];
|
||||||
|
p_lambda->function->parameters_indices[p_lambda->function->parameters[i]->identifier->name] = i + captures_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add captures as extra parameters at the beginning.
|
||||||
|
for (int i = 0; i < p_lambda->captures.size(); i++) {
|
||||||
|
GDScriptParser::IdentifierNode *capture = p_lambda->captures[i];
|
||||||
|
GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>();
|
||||||
|
capture_param->identifier = capture;
|
||||||
|
capture_param->usages = capture->usages;
|
||||||
|
capture_param->set_datatype(capture->get_datatype());
|
||||||
|
|
||||||
|
p_lambda->function->parameters.write[i] = capture_param;
|
||||||
|
p_lambda->function->parameters_indices[capture->name] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lambda_stack.pop_back();
|
lambda_stack.pop_back();
|
||||||
parser->current_function = previous_function;
|
parser->current_function = previous_function;
|
||||||
}
|
}
|
||||||
@@ -3577,13 +3605,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GDScriptParser::LambdaNode *GDScriptAnalyzer::get_current_lambda() const {
|
|
||||||
if (lambda_stack.size()) {
|
|
||||||
return lambda_stack.back()->get();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error GDScriptAnalyzer::resolve_inheritance() {
|
Error GDScriptAnalyzer::resolve_inheritance() {
|
||||||
return resolve_inheritance(parser->head);
|
return resolve_inheritance(parser->head);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ class GDScriptAnalyzer {
|
|||||||
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
||||||
bool class_exists(const StringName &p_class) const;
|
bool class_exists(const StringName &p_class) const;
|
||||||
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
|
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
|
||||||
const GDScriptParser::LambdaNode *get_current_lambda() const;
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
|
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -383,6 +383,18 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
|
|||||||
function->_methods_count = 0;
|
function->_methods_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lambdas_map.size()) {
|
||||||
|
function->lambdas.resize(lambdas_map.size());
|
||||||
|
function->_lambdas_ptr = function->lambdas.ptrw();
|
||||||
|
function->_lambdas_count = lambdas_map.size();
|
||||||
|
for (const Map<GDScriptFunction *, int>::Element *E = lambdas_map.front(); E; E = E->next()) {
|
||||||
|
function->lambdas.write[E->get()] = E->key();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
function->_lambdas_ptr = nullptr;
|
||||||
|
function->_lambdas_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (debug_stack) {
|
if (debug_stack) {
|
||||||
function->stack_debug = stack_debug;
|
function->stack_debug = stack_debug;
|
||||||
}
|
}
|
||||||
@@ -1118,6 +1130,17 @@ void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_targ
|
|||||||
append(p_function_name);
|
append(p_function_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) {
|
||||||
|
append(GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
|
||||||
|
for (int i = 0; i < p_captures.size(); i++) {
|
||||||
|
append(p_captures[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(p_target);
|
||||||
|
append(p_captures.size());
|
||||||
|
append(p_function);
|
||||||
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
|
void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
|
||||||
// Try to find an appropriate constructor.
|
// Try to find an appropriate constructor.
|
||||||
bool all_have_type = true;
|
bool all_have_type = true;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
|||||||
Map<Variant::ValidatedUtilityFunction, int> utilities_map;
|
Map<Variant::ValidatedUtilityFunction, int> utilities_map;
|
||||||
Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
|
Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
|
||||||
Map<MethodBind *, int> method_bind_map;
|
Map<MethodBind *, int> method_bind_map;
|
||||||
|
Map<GDScriptFunction *, int> lambdas_map;
|
||||||
|
|
||||||
// Lists since these can be nested.
|
// Lists since these can be nested.
|
||||||
List<int> if_jmp_addrs;
|
List<int> if_jmp_addrs;
|
||||||
@@ -293,6 +294,15 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get_lambda_function_pos(GDScriptFunction *p_lambda_function) {
|
||||||
|
if (lambdas_map.has(p_lambda_function)) {
|
||||||
|
return lambdas_map[p_lambda_function];
|
||||||
|
}
|
||||||
|
int pos = lambdas_map.size();
|
||||||
|
lambdas_map[p_lambda_function] = pos;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
void alloc_ptrcall(int p_params) {
|
void alloc_ptrcall(int p_params) {
|
||||||
if (p_params >= ptrcall_max) {
|
if (p_params >= ptrcall_max) {
|
||||||
ptrcall_max = p_params;
|
ptrcall_max = p_params;
|
||||||
@@ -386,6 +396,10 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
|
|||||||
opcodes.push_back(get_method_bind_pos(p_method));
|
opcodes.push_back(get_method_bind_pos(p_method));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void append(GDScriptFunction *p_lambda_function) {
|
||||||
|
opcodes.push_back(get_lambda_function_pos(p_lambda_function));
|
||||||
|
}
|
||||||
|
|
||||||
void patch_jump(int p_address) {
|
void patch_jump(int p_address) {
|
||||||
opcodes.write[p_address] = opcodes.size();
|
opcodes.write[p_address] = opcodes.size();
|
||||||
}
|
}
|
||||||
@@ -452,6 +466,7 @@ public:
|
|||||||
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
||||||
virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
||||||
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
|
||||||
|
virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) override;
|
||||||
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
|
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
|
||||||
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
|
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
|
||||||
virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
|
virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ public:
|
|||||||
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
||||||
virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
||||||
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
|
||||||
|
virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) = 0;
|
||||||
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
|
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
|
||||||
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
|
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
|
||||||
virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
|
virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
|
||||||
|
|||||||
@@ -1091,6 +1091,34 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||||||
}
|
}
|
||||||
return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
|
return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
|
||||||
} break;
|
} break;
|
||||||
|
case GDScriptParser::Node::LAMBDA: {
|
||||||
|
const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression);
|
||||||
|
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype()));
|
||||||
|
|
||||||
|
Vector<GDScriptCodeGenerator::Address> captures;
|
||||||
|
captures.resize(lambda->captures.size());
|
||||||
|
for (int i = 0; i < lambda->captures.size(); i++) {
|
||||||
|
captures.write[i] = _parse_expression(codegen, r_error, lambda->captures[i]);
|
||||||
|
if (r_error) {
|
||||||
|
return GDScriptCodeGenerator::Address();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GDScriptFunction *function = _parse_function(r_error, codegen.script, codegen.class_node, lambda->function, false, true);
|
||||||
|
if (r_error) {
|
||||||
|
return GDScriptCodeGenerator::Address();
|
||||||
|
}
|
||||||
|
|
||||||
|
gen->write_lambda(result, function, captures);
|
||||||
|
|
||||||
|
for (int i = 0; i < captures.size(); i++) {
|
||||||
|
if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
|
||||||
|
gen->pop_temporary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
default: {
|
default: {
|
||||||
ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
|
ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
|
||||||
} break;
|
} break;
|
||||||
@@ -1804,8 +1832,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
|
GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready, bool p_for_lambda) {
|
||||||
Error error = OK;
|
r_error = OK;
|
||||||
CodeGen codegen;
|
CodeGen codegen;
|
||||||
codegen.generator = memnew(GDScriptByteCodeGenerator);
|
codegen.generator = memnew(GDScriptByteCodeGenerator);
|
||||||
|
|
||||||
@@ -1822,7 +1850,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
return_type.builtin_type = Variant::NIL;
|
return_type.builtin_type = Variant::NIL;
|
||||||
|
|
||||||
if (p_func) {
|
if (p_func) {
|
||||||
|
if (p_func->identifier) {
|
||||||
func_name = p_func->identifier->name;
|
func_name = p_func->identifier->name;
|
||||||
|
} else {
|
||||||
|
func_name = "<anonymous lambda>";
|
||||||
|
}
|
||||||
is_static = p_func->is_static;
|
is_static = p_func->is_static;
|
||||||
rpc_mode = p_func->rpc_mode;
|
rpc_mode = p_func->rpc_mode;
|
||||||
return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
|
return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
|
||||||
@@ -1853,11 +1885,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse initializer if applies.
|
// Parse initializer if applies.
|
||||||
bool is_implicit_initializer = !p_for_ready && !p_func;
|
bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda;
|
||||||
bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
|
bool is_initializer = p_func && !p_for_lambda && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
|
||||||
bool is_for_ready = p_for_ready || (p_func && String(p_func->identifier->name) == "_ready");
|
bool is_for_ready = p_for_ready || (p_func && !p_for_lambda && String(p_func->identifier->name) == "_ready");
|
||||||
|
|
||||||
if (is_implicit_initializer || is_for_ready) {
|
if (!p_for_lambda && (is_implicit_initializer || is_for_ready)) {
|
||||||
// Initialize class fields.
|
// Initialize class fields.
|
||||||
for (int i = 0; i < p_class->members.size(); i++) {
|
for (int i = 0; i < p_class->members.size(); i++) {
|
||||||
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
|
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
|
||||||
@@ -1884,10 +1916,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
|
codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
|
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);
|
||||||
if (error) {
|
if (r_error) {
|
||||||
memdelete(codegen.generator);
|
memdelete(codegen.generator);
|
||||||
return error;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
codegen.generator->write_assign(dst_address, src_address);
|
codegen.generator->write_assign(dst_address, src_address);
|
||||||
@@ -1914,10 +1946,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
codegen.generator->start_parameters();
|
codegen.generator->start_parameters();
|
||||||
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
|
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
|
||||||
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
|
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
|
||||||
GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true);
|
GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value, true);
|
||||||
if (error) {
|
if (r_error) {
|
||||||
memdelete(codegen.generator);
|
memdelete(codegen.generator);
|
||||||
return error;
|
return nullptr;
|
||||||
}
|
}
|
||||||
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
|
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
|
||||||
codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
|
codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
|
||||||
@@ -1928,10 +1960,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
codegen.generator->end_parameters();
|
codegen.generator->end_parameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
Error err = _parse_block(codegen, p_func->body);
|
r_error = _parse_block(codegen, p_func->body);
|
||||||
if (err) {
|
if (r_error) {
|
||||||
memdelete(codegen.generator);
|
memdelete(codegen.generator);
|
||||||
return err;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1957,6 +1989,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
signature += "::" + String(func_name);
|
signature += "::" + String(func_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_for_lambda) {
|
||||||
|
signature += "(lambda)";
|
||||||
|
}
|
||||||
|
|
||||||
codegen.generator->set_signature(signature);
|
codegen.generator->set_signature(signature);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1964,8 +2000,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
if (p_func) {
|
if (p_func) {
|
||||||
codegen.generator->set_initial_line(p_func->start_line);
|
codegen.generator->set_initial_line(p_func->start_line);
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (!p_for_lambda) {
|
||||||
p_script->member_lines[func_name] = p_func->start_line;
|
p_script->member_lines[func_name] = p_func->start_line;
|
||||||
p_script->doc_functions[func_name] = p_func->doc_description;
|
p_script->doc_functions[func_name] = p_func->doc_description;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
codegen.generator->set_initial_line(0);
|
codegen.generator->set_initial_line(0);
|
||||||
@@ -1994,11 +2032,13 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!p_for_lambda) {
|
||||||
p_script->member_functions[func_name] = gd_function;
|
p_script->member_functions[func_name] = gd_function;
|
||||||
|
}
|
||||||
|
|
||||||
memdelete(codegen.generator);
|
memdelete(codegen.generator);
|
||||||
|
|
||||||
return OK;
|
return gd_function;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
|
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
|
||||||
@@ -2391,7 +2431,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
|
|||||||
if (!has_ready && function->identifier->name == "_ready") {
|
if (!has_ready && function->identifier->name == "_ready") {
|
||||||
has_ready = true;
|
has_ready = true;
|
||||||
}
|
}
|
||||||
Error err = _parse_function(p_script, p_class, function);
|
Error err = OK;
|
||||||
|
_parse_function(err, p_script, p_class, function);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -2416,7 +2457,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Create an implicit constructor in any case.
|
// Create an implicit constructor in any case.
|
||||||
Error err = _parse_function(p_script, p_class, nullptr);
|
Error err = OK;
|
||||||
|
_parse_function(err, p_script, p_class, nullptr);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -2424,7 +2466,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
|
|||||||
|
|
||||||
if (!has_ready && p_class->onready_used) {
|
if (!has_ready && p_class->onready_used) {
|
||||||
//create a _ready constructor
|
//create a _ready constructor
|
||||||
Error err = _parse_function(p_script, p_class, nullptr, true);
|
Error err = OK;
|
||||||
|
_parse_function(err, p_script, p_class, nullptr, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class GDScriptCompiler {
|
|||||||
GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
|
GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
|
||||||
void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
|
void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
|
||||||
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
|
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
|
||||||
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
|
GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
|
||||||
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
|
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
|
||||||
Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
||||||
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
|
||||||
|
|||||||
@@ -721,7 +721,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
text += "await ";
|
text += "await ";
|
||||||
text += DADDR(1);
|
text += DADDR(1);
|
||||||
|
|
||||||
incr += 2;
|
incr = 2;
|
||||||
} break;
|
} break;
|
||||||
case OPCODE_AWAIT_RESUME: {
|
case OPCODE_AWAIT_RESUME: {
|
||||||
text += "await resume ";
|
text += "await resume ";
|
||||||
@@ -729,6 +729,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||||||
|
|
||||||
incr = 2;
|
incr = 2;
|
||||||
} break;
|
} break;
|
||||||
|
case OPCODE_CREATE_LAMBDA: {
|
||||||
|
int captures_count = _code_ptr[ip + 1 + instr_var_args];
|
||||||
|
GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]];
|
||||||
|
|
||||||
|
text += DADDR(1 + captures_count);
|
||||||
|
text += "create lambda from ";
|
||||||
|
text += lambda->name.operator String();
|
||||||
|
text += "function, captures (";
|
||||||
|
|
||||||
|
for (int i = 0; i < captures_count; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
text += ", ";
|
||||||
|
}
|
||||||
|
text += DADDR(1 + i);
|
||||||
|
}
|
||||||
|
text += ")";
|
||||||
|
|
||||||
|
incr = 3 + captures_count;
|
||||||
|
} break;
|
||||||
case OPCODE_JUMP: {
|
case OPCODE_JUMP: {
|
||||||
text += "jump ";
|
text += "jump ";
|
||||||
text += itos(_code_ptr[ip + 1]);
|
text += itos(_code_ptr[ip + 1]);
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ GDScriptFunction::GDScriptFunction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GDScriptFunction::~GDScriptFunction() {
|
GDScriptFunction::~GDScriptFunction() {
|
||||||
|
for (int i = 0; i < lambdas.size(); i++) {
|
||||||
|
memdelete(lambdas[i]);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
|
||||||
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
|
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ public:
|
|||||||
OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,
|
OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,
|
||||||
OPCODE_AWAIT,
|
OPCODE_AWAIT,
|
||||||
OPCODE_AWAIT_RESUME,
|
OPCODE_AWAIT_RESUME,
|
||||||
|
OPCODE_CREATE_LAMBDA,
|
||||||
OPCODE_JUMP,
|
OPCODE_JUMP,
|
||||||
OPCODE_JUMP_IF,
|
OPCODE_JUMP_IF,
|
||||||
OPCODE_JUMP_IF_NOT,
|
OPCODE_JUMP_IF_NOT,
|
||||||
@@ -459,6 +460,8 @@ private:
|
|||||||
const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
|
const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
|
||||||
int _methods_count = 0;
|
int _methods_count = 0;
|
||||||
MethodBind **_methods_ptr = nullptr;
|
MethodBind **_methods_ptr = nullptr;
|
||||||
|
int _lambdas_count = 0;
|
||||||
|
GDScriptFunction **_lambdas_ptr = nullptr;
|
||||||
const int *_code_ptr = nullptr;
|
const int *_code_ptr = nullptr;
|
||||||
int _code_size = 0;
|
int _code_size = 0;
|
||||||
int _argument_count = 0;
|
int _argument_count = 0;
|
||||||
@@ -488,6 +491,7 @@ private:
|
|||||||
Vector<Variant::ValidatedUtilityFunction> utilities;
|
Vector<Variant::ValidatedUtilityFunction> utilities;
|
||||||
Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
|
Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
|
||||||
Vector<MethodBind *> methods;
|
Vector<MethodBind *> methods;
|
||||||
|
Vector<GDScriptFunction *> lambdas;
|
||||||
Vector<int> code;
|
Vector<int> code;
|
||||||
Vector<GDScriptDataType> argument_types;
|
Vector<GDScriptDataType> argument_types;
|
||||||
GDScriptDataType return_type;
|
GDScriptDataType return_type;
|
||||||
|
|||||||
95
modules/gdscript/gdscript_lambda_callable.cpp
Normal file
95
modules/gdscript/gdscript_lambda_callable.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*************************************************************************/
|
||||||
|
/* gdscript_lambda_callable.cpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 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_lambda_callable.h"
|
||||||
|
|
||||||
|
#include "core/templates/hashfuncs.h"
|
||||||
|
#include "gdscript.h"
|
||||||
|
|
||||||
|
bool GDScriptLambdaCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||||
|
// Lambda callables are only compared by reference.
|
||||||
|
return p_a == p_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||||
|
// Lambda callables are only compared by reference.
|
||||||
|
return p_a < p_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GDScriptLambdaCallable::hash() const {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
String GDScriptLambdaCallable::get_as_text() const {
|
||||||
|
if (function->get_name() != StringName()) {
|
||||||
|
return function->get_name().operator String() + "(lambda)";
|
||||||
|
}
|
||||||
|
return "(anonymous lambda)";
|
||||||
|
}
|
||||||
|
|
||||||
|
CallableCustom::CompareEqualFunc GDScriptLambdaCallable::get_compare_equal_func() const {
|
||||||
|
return compare_equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallableCustom::CompareLessFunc GDScriptLambdaCallable::get_compare_less_func() const {
|
||||||
|
return compare_less;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectID GDScriptLambdaCallable::get_object() const {
|
||||||
|
return script->get_instance_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
|
||||||
|
int captures_amount = captures.size();
|
||||||
|
|
||||||
|
if (captures_amount > 0) {
|
||||||
|
Vector<const Variant *> args;
|
||||||
|
args.resize(p_argcount + captures_amount);
|
||||||
|
for (int i = 0; i < captures_amount; i++) {
|
||||||
|
args.write[i] = &captures[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < p_argcount; i++) {
|
||||||
|
args.write[i + captures_amount] = p_arguments[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
|
||||||
|
r_call_error.argument -= captures_amount;
|
||||||
|
} else {
|
||||||
|
r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
|
||||||
|
script = p_script;
|
||||||
|
function = p_function;
|
||||||
|
captures = p_captures;
|
||||||
|
|
||||||
|
h = (uint32_t)hash_djb2_one_64((uint64_t)this);
|
||||||
|
}
|
||||||
65
modules/gdscript/gdscript_lambda_callable.h
Normal file
65
modules/gdscript/gdscript_lambda_callable.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*************************************************************************/
|
||||||
|
/* gdscript_lambda_callable.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GDSCRIPT_LAMBDA_CALLABLE
|
||||||
|
#define GDSCRIPT_LAMBDA_CALLABLE
|
||||||
|
|
||||||
|
#include "core/object/reference.h"
|
||||||
|
#include "core/templates/vector.h"
|
||||||
|
#include "core/variant/callable.h"
|
||||||
|
#include "core/variant/variant.h"
|
||||||
|
|
||||||
|
class GDScript;
|
||||||
|
class GDScriptFunction;
|
||||||
|
class GDScriptInstance;
|
||||||
|
|
||||||
|
class GDScriptLambdaCallable : public CallableCustom {
|
||||||
|
GDScriptFunction *function = nullptr;
|
||||||
|
Ref<GDScript> script;
|
||||||
|
uint32_t h;
|
||||||
|
|
||||||
|
Vector<Variant> captures;
|
||||||
|
|
||||||
|
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||||
|
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t hash() const override;
|
||||||
|
String get_as_text() const override;
|
||||||
|
CompareEqualFunc get_compare_equal_func() const override;
|
||||||
|
CompareLessFunc get_compare_less_func() const override;
|
||||||
|
ObjectID get_object() const override;
|
||||||
|
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
|
||||||
|
|
||||||
|
GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
||||||
|
virtual ~GDScriptLambdaCallable() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GDSCRIPT_LAMBDA_CALLABLE
|
||||||
@@ -4032,11 +4032,11 @@ void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
|
|||||||
void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
|
void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
|
||||||
print_function(p_lambda->function, "Lambda");
|
print_function(p_lambda->function, "Lambda");
|
||||||
push_text("| captures [ ");
|
push_text("| captures [ ");
|
||||||
for (const Map<StringName, IdentifierNode *>::Element *E = p_lambda->captures.front(); E; E = E->next()) {
|
for (int i = 0; i < p_lambda->captures.size(); i++) {
|
||||||
push_text(E->key().operator String());
|
if (i > 0) {
|
||||||
if (E->next()) {
|
|
||||||
push_text(" , ");
|
push_text(" , ");
|
||||||
}
|
}
|
||||||
|
push_text(p_lambda->captures[i]->name.operator String());
|
||||||
}
|
}
|
||||||
push_line(" ]");
|
push_line(" ]");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -796,7 +796,8 @@ public:
|
|||||||
struct LambdaNode : public ExpressionNode {
|
struct LambdaNode : public ExpressionNode {
|
||||||
FunctionNode *function = nullptr;
|
FunctionNode *function = nullptr;
|
||||||
FunctionNode *parent_function = nullptr;
|
FunctionNode *parent_function = nullptr;
|
||||||
Map<StringName, IdentifierNode *> captures;
|
Vector<IdentifierNode *> captures;
|
||||||
|
Map<StringName, int> captures_indices;
|
||||||
|
|
||||||
bool has_name() const {
|
bool has_name() const {
|
||||||
return function && function->identifier;
|
return function && function->identifier;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "core/core_string_names.h"
|
#include "core/core_string_names.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
#include "gdscript.h"
|
#include "gdscript.h"
|
||||||
|
#include "gdscript_lambda_callable.h"
|
||||||
|
|
||||||
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const {
|
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const {
|
||||||
int address = p_address & ADDR_MASK;
|
int address = p_address & ADDR_MASK;
|
||||||
@@ -232,6 +233,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
|
|||||||
&&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY, \
|
&&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY, \
|
||||||
&&OPCODE_AWAIT, \
|
&&OPCODE_AWAIT, \
|
||||||
&&OPCODE_AWAIT_RESUME, \
|
&&OPCODE_AWAIT_RESUME, \
|
||||||
|
&&OPCODE_CREATE_LAMBDA, \
|
||||||
&&OPCODE_JUMP, \
|
&&OPCODE_JUMP, \
|
||||||
&&OPCODE_JUMP_IF, \
|
&&OPCODE_JUMP_IF, \
|
||||||
&&OPCODE_JUMP_IF_NOT, \
|
&&OPCODE_JUMP_IF_NOT, \
|
||||||
@@ -1452,13 +1454,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
if (err.error != Callable::CallError::CALL_OK) {
|
if (err.error != Callable::CallError::CALL_OK) {
|
||||||
String methodstr = *methodname;
|
String methodstr = *methodname;
|
||||||
String basestr = _get_var_type(base);
|
String basestr = _get_var_type(base);
|
||||||
|
bool is_callable = false;
|
||||||
|
|
||||||
if (methodstr == "call") {
|
if (methodstr == "call") {
|
||||||
if (argc >= 1) {
|
if (argc >= 1 && base->get_type() != Variant::CALLABLE) {
|
||||||
methodstr = String(*argptrs[0]) + " (via call)";
|
methodstr = String(*argptrs[0]) + " (via call)";
|
||||||
if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
|
if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
|
||||||
err.argument += 1;
|
err.argument += 1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
methodstr = base->operator String() + " (Callable)";
|
||||||
|
is_callable = true;
|
||||||
}
|
}
|
||||||
} else if (methodstr == "free") {
|
} else if (methodstr == "free") {
|
||||||
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
|
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
|
||||||
@@ -1478,7 +1484,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
|
err_text = _get_call_error(err, "function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs);
|
||||||
OPCODE_BREAK;
|
OPCODE_BREAK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2057,6 +2063,34 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||||||
}
|
}
|
||||||
DISPATCH_OPCODE;
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
|
OPCODE(OPCODE_CREATE_LAMBDA) {
|
||||||
|
CHECK_SPACE(2 + instr_arg_count);
|
||||||
|
|
||||||
|
ip += instr_arg_count;
|
||||||
|
|
||||||
|
int captures_count = _code_ptr[ip + 1];
|
||||||
|
GD_ERR_BREAK(captures_count < 0);
|
||||||
|
|
||||||
|
int lambda_index = _code_ptr[ip + 2];
|
||||||
|
GD_ERR_BREAK(lambda_index < 0 || lambda_index >= _lambdas_count);
|
||||||
|
GDScriptFunction *lambda = _lambdas_ptr[lambda_index];
|
||||||
|
|
||||||
|
Vector<Variant> captures;
|
||||||
|
captures.resize(captures_count);
|
||||||
|
for (int i = 0; i < captures_count; i++) {
|
||||||
|
GET_INSTRUCTION_ARG(arg, i);
|
||||||
|
captures.write[i] = *arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
GDScriptLambdaCallable *callable = memnew(GDScriptLambdaCallable(Ref<GDScript>(script), lambda, captures));
|
||||||
|
|
||||||
|
GET_INSTRUCTION_ARG(result, captures_count);
|
||||||
|
*result = Callable(callable);
|
||||||
|
|
||||||
|
ip += 3;
|
||||||
|
}
|
||||||
|
DISPATCH_OPCODE;
|
||||||
|
|
||||||
OPCODE(OPCODE_JUMP) {
|
OPCODE(OPCODE_JUMP) {
|
||||||
CHECK_SPACE(2);
|
CHECK_SPACE(2);
|
||||||
int to = _code_ptr[ip + 1];
|
int to = _code_ptr[ip + 1];
|
||||||
|
|||||||
Reference in New Issue
Block a user