GDScript: Allow using self in lambdas
This commit is contained in:
parent
690fefe43e
commit
01d13ab2c1
|
@ -262,6 +262,7 @@ class GDScriptInstance : public ScriptInstance {
|
|||
friend class GDScript;
|
||||
friend class GDScriptFunction;
|
||||
friend class GDScriptLambdaCallable;
|
||||
friend class GDScriptLambdaSelfCallable;
|
||||
friend class GDScriptCompiler;
|
||||
friend struct GDScriptUtilityFunctionsDefinitions;
|
||||
|
||||
|
|
|
@ -2498,12 +2498,17 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
}
|
||||
|
||||
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);
|
||||
// Get the parent function above any lambda.
|
||||
GDScriptParser::FunctionNode *parent_function = parser->current_function;
|
||||
while (parent_function->source_lambda) {
|
||||
parent_function = parent_function->source_lambda->parent_function;
|
||||
}
|
||||
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
|
||||
} else if (!is_self && base_type.is_meta_type && !is_static) {
|
||||
base_type.is_meta_type = false; // For `to_string()`.
|
||||
push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call);
|
||||
} 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);
|
||||
} else if (is_self && !is_static) {
|
||||
mark_lambda_use_self();
|
||||
}
|
||||
|
||||
call_type = return_type;
|
||||
|
@ -2636,10 +2641,10 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
|
|||
|
||||
if (!ClassDB::is_parent_class(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);
|
||||
} 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);
|
||||
}
|
||||
|
||||
mark_lambda_use_self();
|
||||
|
||||
p_get_node->set_datatype(result);
|
||||
}
|
||||
|
||||
|
@ -2854,21 +2859,25 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
MethodBind *getter = ClassDB::get_method(native, getter_name);
|
||||
if (getter != nullptr) {
|
||||
p_identifier->set_datatype(type_from_property(getter->get_return_info()));
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ClassDB::get_method_info(native, name, &method_info)) {
|
||||
// Method is callable.
|
||||
p_identifier->set_datatype(make_callable_type(method_info));
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
|
||||
return;
|
||||
}
|
||||
if (ClassDB::get_signal(native, name, &method_info)) {
|
||||
// Signal is a type too.
|
||||
p_identifier->set_datatype(make_signal_type(method_info));
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
|
||||
return;
|
||||
}
|
||||
if (ClassDB::has_enum(native, name)) {
|
||||
p_identifier->set_datatype(make_native_enum_type(native, name));
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
|
||||
return;
|
||||
}
|
||||
bool valid = false;
|
||||
|
@ -2877,6 +2886,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
p_identifier->is_constant = true;
|
||||
p_identifier->reduced_value = int_constant;
|
||||
p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2927,7 +2937,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
|
||||
found_source = true;
|
||||
break;
|
||||
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
|
||||
mark_lambda_use_self();
|
||||
break;
|
||||
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
||||
mark_lambda_use_self();
|
||||
p_identifier->variable_source->usages++;
|
||||
[[fallthrough]];
|
||||
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
||||
|
@ -2958,18 +2972,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
}
|
||||
|
||||
if (found_source) {
|
||||
// If the identifier is local, check if it's any kind of capture by comparing their source function.
|
||||
// Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference.
|
||||
if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) {
|
||||
return;
|
||||
if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
|
||||
// Get the parent function above any lambda.
|
||||
GDScriptParser::FunctionNode *parent_function = parser->current_function;
|
||||
while (parent_function->source_lambda) {
|
||||
parent_function = parent_function->source_lambda->parent_function;
|
||||
}
|
||||
push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier);
|
||||
}
|
||||
|
||||
GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
|
||||
while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
|
||||
function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
|
||||
function_test->source_lambda->captures.push_back(p_identifier);
|
||||
function_test = function_test->source_lambda->parent_function;
|
||||
if (!lambda_stack.is_empty()) {
|
||||
// If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
|
||||
if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
|
||||
mark_lambda_use_self();
|
||||
return; // No need to capture.
|
||||
}
|
||||
// If the identifier is local, check if it's any kind of capture by comparing their source function.
|
||||
// Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done.
|
||||
if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) {
|
||||
return;
|
||||
}
|
||||
|
||||
GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
|
||||
// Make sure we aren't capturing variable in the same lambda.
|
||||
// This also add captures for nested lambdas.
|
||||
while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
|
||||
function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
|
||||
function_test->source_lambda->captures.push_back(p_identifier);
|
||||
function_test = function_test->source_lambda->parent_function;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3149,6 +3182,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
|
|||
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
|
||||
p_self->is_constant = false;
|
||||
p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype()));
|
||||
mark_lambda_use_self();
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
|
||||
|
@ -4121,6 +4155,12 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::mark_lambda_use_self() {
|
||||
for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
|
||||
lambda->use_self = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
|
||||
return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class GDScriptAnalyzer {
|
|||
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
|
||||
|
||||
const GDScriptParser::EnumNode *current_enum = nullptr;
|
||||
List<const GDScriptParser::LambdaNode *> lambda_stack;
|
||||
List<GDScriptParser::LambdaNode *> lambda_stack;
|
||||
|
||||
// Tests for detecting invalid overloading of script members
|
||||
static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node);
|
||||
|
@ -115,6 +115,7 @@ class GDScriptAnalyzer {
|
|||
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
|
||||
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
|
||||
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
||||
void mark_lambda_use_self();
|
||||
bool class_exists(const StringName &p_class) const;
|
||||
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
|
@ -1211,8 +1211,8 @@ void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_targ
|
|||
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());
|
||||
void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) {
|
||||
append(p_use_self ? GDScriptFunction::OPCODE_CREATE_SELF_LAMBDA : GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
|
||||
for (int i = 0; i < p_captures.size(); i++) {
|
||||
append(p_captures[i]);
|
||||
}
|
||||
|
|
|
@ -470,7 +470,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_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_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) override;
|
||||
virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) 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_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
|
||||
|
|
|
@ -131,7 +131,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_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_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) = 0;
|
||||
virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) = 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_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
|
||||
|
|
|
@ -1197,7 +1197,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
return GDScriptCodeGenerator::Address();
|
||||
}
|
||||
|
||||
gen->write_lambda(result, function, captures);
|
||||
gen->write_lambda(result, function, captures, lambda->use_self);
|
||||
|
||||
for (int i = 0; i < captures.size(); i++) {
|
||||
if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
|
||||
|
|
|
@ -792,6 +792,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
|
||||
incr = 3 + captures_count;
|
||||
} break;
|
||||
case OPCODE_CREATE_SELF_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 self 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: {
|
||||
text += "jump ";
|
||||
text += itos(_code_ptr[ip + 1]);
|
||||
|
|
|
@ -299,6 +299,7 @@ public:
|
|||
OPCODE_AWAIT,
|
||||
OPCODE_AWAIT_RESUME,
|
||||
OPCODE_CREATE_LAMBDA,
|
||||
OPCODE_CREATE_SELF_LAMBDA,
|
||||
OPCODE_JUMP,
|
||||
OPCODE_JUMP_IF,
|
||||
OPCODE_JUMP_IF_NOT,
|
||||
|
|
|
@ -93,3 +93,81 @@ GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptF
|
|||
|
||||
h = (uint32_t)hash_djb2_one_64((uint64_t)this);
|
||||
}
|
||||
|
||||
bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
// Lambda callables are only compared by reference.
|
||||
return p_a == p_b;
|
||||
}
|
||||
|
||||
bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
// Lambda callables are only compared by reference.
|
||||
return p_a < p_b;
|
||||
}
|
||||
|
||||
uint32_t GDScriptLambdaSelfCallable::hash() const {
|
||||
return h;
|
||||
}
|
||||
|
||||
String GDScriptLambdaSelfCallable::get_as_text() const {
|
||||
if (function->get_name() != StringName()) {
|
||||
return function->get_name().operator String() + "(lambda)";
|
||||
}
|
||||
return "(anonymous lambda)";
|
||||
}
|
||||
|
||||
CallableCustom::CompareEqualFunc GDScriptLambdaSelfCallable::get_compare_equal_func() const {
|
||||
return compare_equal;
|
||||
}
|
||||
|
||||
CallableCustom::CompareLessFunc GDScriptLambdaSelfCallable::get_compare_less_func() const {
|
||||
return compare_less;
|
||||
}
|
||||
|
||||
ObjectID GDScriptLambdaSelfCallable::get_object() const {
|
||||
return object->get_instance_id();
|
||||
}
|
||||
|
||||
void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (object->get_script_instance() == nullptr || object->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
|
||||
ERR_PRINT("Trying to call a lambda with an invalid instance.");
|
||||
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
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(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
|
||||
r_call_error.argument -= captures_amount;
|
||||
} else {
|
||||
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error);
|
||||
}
|
||||
}
|
||||
|
||||
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
|
||||
reference = p_self;
|
||||
object = p_self.ptr();
|
||||
function = p_function;
|
||||
captures = p_captures;
|
||||
|
||||
h = (uint32_t)hash_djb2_one_64((uint64_t)this);
|
||||
}
|
||||
|
||||
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
|
||||
object = p_self;
|
||||
function = p_function;
|
||||
captures = p_captures;
|
||||
|
||||
h = (uint32_t)hash_djb2_one_64((uint64_t)this);
|
||||
}
|
||||
|
|
|
@ -62,4 +62,29 @@ public:
|
|||
virtual ~GDScriptLambdaCallable() = default;
|
||||
};
|
||||
|
||||
// Lambda callable that references a particular object, so it can use `self` in the body.
|
||||
class GDScriptLambdaSelfCallable : public CallableCustom {
|
||||
GDScriptFunction *function = nullptr;
|
||||
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
|
||||
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
|
||||
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;
|
||||
|
||||
GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
||||
GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
|
||||
virtual ~GDScriptLambdaSelfCallable() = default;
|
||||
};
|
||||
|
||||
#endif // GDSCRIPT_LAMBDA_CALLABLE
|
||||
|
|
|
@ -2200,9 +2200,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
|
|||
if (current_function && current_function->is_static) {
|
||||
push_error(R"(Cannot use "self" inside a static function.)");
|
||||
}
|
||||
if (in_lambda) {
|
||||
push_error(R"(Cannot use "self" inside a lambda.)");
|
||||
}
|
||||
SelfNode *self = alloc_node<SelfNode>();
|
||||
self->current_class = current_class;
|
||||
return self;
|
||||
|
|
|
@ -767,6 +767,7 @@ public:
|
|||
LOCAL_BIND, // Pattern bind.
|
||||
MEMBER_VARIABLE,
|
||||
MEMBER_CONSTANT,
|
||||
INHERITED_VARIABLE,
|
||||
};
|
||||
Source source = UNDEFINED_SOURCE;
|
||||
|
||||
|
@ -800,6 +801,7 @@ public:
|
|||
FunctionNode *parent_function = nullptr;
|
||||
Vector<IdentifierNode *> captures;
|
||||
Map<StringName, int> captures_indices;
|
||||
bool use_self = false;
|
||||
|
||||
bool has_name() const {
|
||||
return function && function->identifier;
|
||||
|
|
|
@ -306,6 +306,7 @@ void (*type_init_function_table[])(Variant *) = {
|
|||
&&OPCODE_AWAIT, \
|
||||
&&OPCODE_AWAIT_RESUME, \
|
||||
&&OPCODE_CREATE_LAMBDA, \
|
||||
&&OPCODE_CREATE_SELF_LAMBDA, \
|
||||
&&OPCODE_JUMP, \
|
||||
&&OPCODE_JUMP_IF, \
|
||||
&&OPCODE_JUMP_IF_NOT, \
|
||||
|
@ -2277,6 +2278,41 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CREATE_SELF_LAMBDA) {
|
||||
CHECK_SPACE(2 + instr_arg_count);
|
||||
|
||||
GD_ERR_BREAK(p_instance == nullptr);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
GDScriptLambdaSelfCallable *callable;
|
||||
if (Object::cast_to<RefCounted>(p_instance->owner)) {
|
||||
callable = memnew(GDScriptLambdaSelfCallable(Ref<RefCounted>(Object::cast_to<RefCounted>(p_instance->owner)), lambda, captures));
|
||||
} else {
|
||||
callable = memnew(GDScriptLambdaSelfCallable(p_instance->owner, lambda, captures));
|
||||
}
|
||||
|
||||
GET_INSTRUCTION_ARG(result, captures_count);
|
||||
*result = Callable(callable);
|
||||
|
||||
ip += 3;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_JUMP) {
|
||||
CHECK_SPACE(2);
|
||||
int to = _code_ptr[ip + 1];
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
var member = "foo"
|
||||
|
||||
func bar():
|
||||
print("bar")
|
||||
|
||||
func test():
|
||||
var lambda1 = func():
|
||||
print(member)
|
||||
lambda1.call()
|
||||
|
||||
var lambda2 = func():
|
||||
var nested = func():
|
||||
print(member)
|
||||
nested.call()
|
||||
lambda2.call()
|
||||
|
||||
var lambda3 = func():
|
||||
bar()
|
||||
lambda3.call()
|
||||
|
||||
var lambda4 = func():
|
||||
return self
|
||||
print(lambda4.call() == self)
|
|
@ -0,0 +1,5 @@
|
|||
GDTEST_OK
|
||||
foo
|
||||
foo
|
||||
bar
|
||||
true
|
Loading…
Reference in New Issue