mirror of
https://github.com/Redot-Engine/redot-engine.git
synced 2025-12-06 15:21:56 -05:00
Compare commits
1 Commits
godot-3.6-
...
lambda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
971c36ab35 |
@@ -270,13 +270,34 @@ int GDCompiler::_parse_expression(CodeGen &codegen, const GDParser::Node *p_expr
|
||||
return idx | (GDFunction::ADDR_TYPE_GLOBAL << GDFunction::ADDR_BITS); //argument (stack root)
|
||||
}
|
||||
|
||||
//not found, error
|
||||
if (codegen.script->function_indices.find(identifier) >= 0) {
|
||||
int idx = codegen.script->function_indices.find(identifier);
|
||||
return idx | (GDFunction::ADDR_TYPE_FUNCTION << GDFunction::ADDR_BITS);
|
||||
}
|
||||
|
||||
//not found, error
|
||||
_set_error("Identifier not found: " + String(identifier), p_expression);
|
||||
|
||||
return -1;
|
||||
|
||||
} break;
|
||||
case GDParser::Node::TYPE_LAMBDA_FUNCTION: {
|
||||
const GDParser::LambdaFunctionNode *in = static_cast<const GDParser::LambdaFunctionNode *>(p_expression);
|
||||
if (codegen.script->function_indices.find(in->name) >= 0) {
|
||||
if (!function_variants.has(in->name)) {
|
||||
function_variants[in->name] = Vector<int>();
|
||||
}
|
||||
|
||||
Vector<int> &list = function_variants[in->name];
|
||||
for (int i = 0; i < in->require_keys.size(); ++i) {
|
||||
const StringName &key = in->require_keys[i];
|
||||
if (codegen.stack_identifiers.has(key))
|
||||
list.push_back(codegen.stack_identifiers[key]);
|
||||
}
|
||||
int idx = codegen.script->function_indices.find(in->name);
|
||||
return idx | (GDFunction::ADDR_TYPE_LAMBDA_FUNCTION << GDFunction::ADDR_BITS);
|
||||
}
|
||||
} break;
|
||||
case GDParser::Node::TYPE_CONSTANT: {
|
||||
//return constant
|
||||
const GDParser::ConstantNode *cn = static_cast<const GDParser::ConstantNode *>(p_expression);
|
||||
@@ -484,40 +505,71 @@ int GDCompiler::_parse_expression(CodeGen &codegen, const GDParser::Node *p_expr
|
||||
Vector<int> arguments;
|
||||
int slevel = p_stack_level;
|
||||
|
||||
for (int i = 0; i < on->arguments.size(); i++) {
|
||||
|
||||
int ret;
|
||||
|
||||
if (i == 0 && on->arguments[i]->type == GDParser::Node::TYPE_SELF && codegen.function_node && codegen.function_node->_static) {
|
||||
//static call to self
|
||||
ret = (GDFunction::ADDR_TYPE_CLASS << GDFunction::ADDR_BITS);
|
||||
} else if (i == 1) {
|
||||
|
||||
if (on->arguments[i]->type != GDParser::Node::TYPE_IDENTIFIER) {
|
||||
_set_error("Attempt to call a non-identifier.", on);
|
||||
return -1;
|
||||
}
|
||||
GDParser::IdentifierNode *id = static_cast<GDParser::IdentifierNode *>(on->arguments[i]);
|
||||
ret = codegen.get_name_map_pos(id->name);
|
||||
|
||||
} else {
|
||||
|
||||
ret = _parse_expression(codegen, on->arguments[i], slevel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & GDFunction::ADDR_TYPE_STACK << GDFunction::ADDR_BITS) {
|
||||
slevel++;
|
||||
codegen.alloc_stack(slevel);
|
||||
do {
|
||||
if (instance->type == GDParser::Node::TYPE_SELF) {
|
||||
const GDParser::SelfNode *self = static_cast<const GDParser::SelfNode *>(instance);
|
||||
if (self->implicit && on->arguments[1]->type == GDParser::Node::TYPE_IDENTIFIER) {
|
||||
GDParser::IdentifierNode *id = static_cast<GDParser::IdentifierNode *>(on->arguments[1]);
|
||||
if (codegen.stack_identifiers.has(id->name)) {
|
||||
int sv = _parse_expression(codegen, id, slevel);
|
||||
if (sv < 0)
|
||||
return sv;
|
||||
for (int i = 2; i < on->arguments.size(); i++) {
|
||||
int ret = _parse_expression(codegen, on->arguments[i], slevel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & GDFunction::ADDR_TYPE_STACK << GDFunction::ADDR_BITS) {
|
||||
slevel++;
|
||||
codegen.alloc_stack(slevel);
|
||||
}
|
||||
arguments.push_back(ret);
|
||||
}
|
||||
codegen.opcodes.push_back(p_root ? GDFunction::OPCODE_CALL_STACK : GDFunction::OPCODE_CALL_STACK_RETURN);
|
||||
codegen.opcodes.push_back(on->arguments.size() - 2);
|
||||
codegen.alloc_call(on->arguments.size() - 2);
|
||||
codegen.opcodes.push_back(sv);
|
||||
for (int i = 0, t = arguments.size(); i < t; i++)
|
||||
codegen.opcodes.push_back(arguments[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
arguments.push_back(ret);
|
||||
}
|
||||
|
||||
codegen.opcodes.push_back(p_root ? GDFunction::OPCODE_CALL : GDFunction::OPCODE_CALL_RETURN); // perform operator
|
||||
codegen.opcodes.push_back(on->arguments.size() - 2);
|
||||
codegen.alloc_call(on->arguments.size() - 2);
|
||||
for (int i = 0; i < arguments.size(); i++)
|
||||
codegen.opcodes.push_back(arguments[i]);
|
||||
for (int i = 0; i < on->arguments.size(); i++) {
|
||||
|
||||
int ret;
|
||||
|
||||
if (i == 0 && on->arguments[i]->type == GDParser::Node::TYPE_SELF && codegen.function_node && codegen.function_node->_static) {
|
||||
//static call to self
|
||||
ret = (GDFunction::ADDR_TYPE_CLASS << GDFunction::ADDR_BITS);
|
||||
} else if (i == 1) {
|
||||
|
||||
if (on->arguments[i]->type != GDParser::Node::TYPE_IDENTIFIER) {
|
||||
_set_error("Attempt to call a non-identifier.", on);
|
||||
return -1;
|
||||
}
|
||||
GDParser::IdentifierNode *id = static_cast<GDParser::IdentifierNode *>(on->arguments[i]);
|
||||
ret = codegen.get_name_map_pos(id->name);
|
||||
|
||||
} else {
|
||||
|
||||
ret = _parse_expression(codegen, on->arguments[i], slevel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & GDFunction::ADDR_TYPE_STACK << GDFunction::ADDR_BITS) {
|
||||
slevel++;
|
||||
codegen.alloc_stack(slevel);
|
||||
}
|
||||
}
|
||||
arguments.push_back(ret);
|
||||
}
|
||||
|
||||
codegen.opcodes.push_back(p_root ? GDFunction::OPCODE_CALL : GDFunction::OPCODE_CALL_RETURN); // perform operator
|
||||
codegen.opcodes.push_back(on->arguments.size() - 2);
|
||||
codegen.alloc_call(on->arguments.size() - 2);
|
||||
for (int i = 0; i < arguments.size(); i++)
|
||||
codegen.opcodes.push_back(arguments[i]);
|
||||
} while (false);
|
||||
}
|
||||
} break;
|
||||
case GDParser::OperatorNode::OP_YIELD: {
|
||||
@@ -1381,6 +1433,13 @@ Error GDCompiler::_parse_function(GDScript *p_script, const GDParser::ClassNode
|
||||
#endif
|
||||
}
|
||||
stack_level = p_func->arguments.size();
|
||||
if (p_func->type == GDParser::Node::TYPE_LAMBDA_FUNCTION) {
|
||||
const GDParser::LambdaFunctionNode *infunc = static_cast<const GDParser::LambdaFunctionNode *>(p_func);
|
||||
for (int i = 0; i < infunc->require_keys.size(); ++i) {
|
||||
codegen.add_stack_identifier(infunc->require_keys[i], stack_level + i);
|
||||
}
|
||||
stack_level += infunc->require_keys.size();
|
||||
}
|
||||
}
|
||||
|
||||
codegen.alloc_stack(stack_level);
|
||||
@@ -1464,6 +1523,10 @@ Error GDCompiler::_parse_function(GDScript *p_script, const GDParser::ClassNode
|
||||
gdfunc->rpc_mode = p_func->rpc_mode;
|
||||
}
|
||||
|
||||
if (function_variants.has(func_name)) {
|
||||
gdfunc->lambda_variants = function_variants[func_name];
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
gdfunc->arg_names = argnames;
|
||||
#endif
|
||||
@@ -1549,6 +1612,7 @@ Error GDCompiler::_parse_function(GDScript *p_script, const GDParser::ClassNode
|
||||
}
|
||||
#endif
|
||||
gdfunc->_script = p_script;
|
||||
gdfunc->_lambda = p_func ? (p_func->type == GDParser::Node::TYPE_LAMBDA_FUNCTION) : false;
|
||||
gdfunc->source = source;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
@@ -1735,6 +1799,7 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
|
||||
p_script->base = script;
|
||||
p_script->_base = p_script->base.ptr();
|
||||
p_script->member_indices = script->member_indices;
|
||||
p_script->function_indices = script->function_indices;
|
||||
|
||||
} else if (native.is_valid()) {
|
||||
|
||||
@@ -1877,6 +1942,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
|
||||
bool has_initializer = false;
|
||||
bool has_ready = false;
|
||||
|
||||
for (int i = 0; i < p_class->functions.size(); i++) {
|
||||
StringName name = p_class->functions[i]->name;
|
||||
p_script->function_indices.push_back(name);
|
||||
}
|
||||
for (int i = 0; i < p_class->functions.size(); i++) {
|
||||
|
||||
if (!has_initializer && p_class->functions[i]->name == "_init")
|
||||
@@ -1890,6 +1959,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
|
||||
|
||||
//parse static methods
|
||||
|
||||
for (int i = 0; i < p_class->static_functions.size(); i++) {
|
||||
StringName name = p_class->static_functions[i]->name;
|
||||
p_script->function_indices.push_back(name);
|
||||
}
|
||||
for (int i = 0; i < p_class->static_functions.size(); i++) {
|
||||
|
||||
Error err = _parse_function(p_script, p_class, p_class->static_functions[i]);
|
||||
@@ -2019,6 +2092,7 @@ Error GDCompiler::compile(const GDParser *p_parser, GDScript *p_script, bool p_k
|
||||
|
||||
Error err = _parse_class(p_script, NULL, static_cast<const GDParser::ClassNode *>(root), p_keep_state);
|
||||
|
||||
function_variants.clear();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -156,6 +156,8 @@ class GDCompiler {
|
||||
StringName source;
|
||||
String error;
|
||||
|
||||
Map<StringName, Vector<int> > function_variants;
|
||||
|
||||
public:
|
||||
Error compile(const GDParser *p_parser, GDScript *p_script, bool p_keep_state = false);
|
||||
|
||||
|
||||
@@ -59,6 +59,37 @@ Variant *GDFunction::_get_variant(int p_address, GDInstance *p_instance, GDScrip
|
||||
}
|
||||
return &p_instance->members[address];
|
||||
} break;
|
||||
case ADDR_TYPE_FUNCTION: {
|
||||
if (!p_instance) {
|
||||
r_error = "Cannot access member without instance.";
|
||||
return NULL;
|
||||
}
|
||||
ERR_FAIL_INDEX_V(address, p_script->function_indices.size(), NULL);
|
||||
|
||||
GDFunction *self_func = const_cast<GDFunction *>(this);
|
||||
Ref<GDFunctionObject> func = p_instance->get_function(p_script->function_indices[address]);
|
||||
if (func == NULL) {
|
||||
r_error = "Founction not found.";
|
||||
return NULL;
|
||||
}
|
||||
self_func->cache.push_back(Variant(func));
|
||||
return &self_func->cache[cache.size() - 1];
|
||||
} break;
|
||||
case ADDR_TYPE_LAMBDA_FUNCTION: {
|
||||
if (!p_instance) {
|
||||
r_error = "Cannot access member without instance.";
|
||||
return NULL;
|
||||
}
|
||||
ERR_FAIL_INDEX_V(address, p_script->function_indices.size(), NULL);
|
||||
GDFunction *self_func = const_cast<GDFunction *>(this);
|
||||
Ref<GDLambdaFunctionObject> func = p_instance->get_lambda_function(p_script->function_indices[address], p_stack, _stack_size);
|
||||
if (func == NULL) {
|
||||
r_error = "Lambda function not found.";
|
||||
return NULL;
|
||||
}
|
||||
self_func->cache.push_back(Variant(func));
|
||||
return &self_func->cache[cache.size() - 1];
|
||||
} break;
|
||||
case ADDR_TYPE_CLASS_CONSTANT: {
|
||||
|
||||
//todo change to index!
|
||||
@@ -160,7 +191,20 @@ static String _get_var_type(const Variant *p_type) {
|
||||
return basestr;
|
||||
}
|
||||
|
||||
Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state) {
|
||||
Variant GDFunction::call_method(Variant *p_self, const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) const {
|
||||
if (p_self->get_type() == Variant::OBJECT) {
|
||||
Object *target = *p_self;
|
||||
if (target && target->get_script_instance()) {
|
||||
GDInstance *instance = dynamic_cast<GDInstance *>(target->get_script_instance());
|
||||
if (instance) return instance->call_member(p_method, p_args, p_argcount, r_error);
|
||||
}
|
||||
}
|
||||
Variant ret;
|
||||
p_self->call_ptr(p_method, p_args, p_argcount, &ret, r_error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state, const Variant **p_requires_args) {
|
||||
|
||||
if (!_code_ptr) {
|
||||
|
||||
@@ -231,7 +275,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
|
||||
stack = (Variant *)aptr;
|
||||
for (int i = 0; i < p_argcount; i++)
|
||||
memnew_placement(&stack[i], Variant(*p_args[i]));
|
||||
for (int i = p_argcount; i < _stack_size; i++)
|
||||
if (p_requires_args)
|
||||
for (int i = 0, t = lambda_variants.size(); i < t; i++)
|
||||
memnew_placement(&stack[p_argcount + i], Variant(*p_requires_args[i]));
|
||||
else
|
||||
for (int i = 0, t = lambda_variants.size(); i < t; i++)
|
||||
memnew_placement(&stack[p_argcount + i], Variant);
|
||||
for (int i = p_argcount + lambda_variants.size(); i < _stack_size; i++)
|
||||
memnew_placement(&stack[i], Variant);
|
||||
} else {
|
||||
stack = NULL;
|
||||
@@ -505,25 +555,41 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
|
||||
const StringName *index = &_global_names_ptr[indexname];
|
||||
|
||||
bool valid;
|
||||
while (1) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
//allow better error message in cases where src and dst are the same stack position
|
||||
Variant ret = src->get_named(*index, &valid);
|
||||
//allow better error message in cases where src and dst are the same stack position
|
||||
Variant ret = src->get_named(*index, &valid);
|
||||
|
||||
#else
|
||||
*dst = src->get_named(*index, &valid);
|
||||
*dst = src->get_named(*index, &valid);
|
||||
#endif
|
||||
|
||||
if (!valid) {
|
||||
if (src->has_method(*index)) {
|
||||
err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' ?";
|
||||
} else {
|
||||
err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
|
||||
if (!valid) {
|
||||
if (src->has_method(*index)) {
|
||||
if (src->get_type() == Variant::OBJECT) {
|
||||
Object *object = src->operator Object *();
|
||||
if (object) {
|
||||
Ref<GDNativeFunctionObject> fun = memnew(GDNativeFunctionObject);
|
||||
fun->target_id = object->get_instance_ID();
|
||||
fun->method_name = *index;
|
||||
*dst = fun;
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
err_text = "Invalid get index '" + index->operator String() + "' (on base: '" +
|
||||
_get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' ?";
|
||||
} else {
|
||||
err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
*dst = ret;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
*dst = ret;
|
||||
#endif
|
||||
if (!valid) break;
|
||||
ip += 4;
|
||||
continue;
|
||||
}
|
||||
@@ -664,8 +730,51 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
|
||||
*dst = dict;
|
||||
|
||||
ip += 3 + argc * 2;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
case OPCODE_CALL_STACK_RETURN:
|
||||
case OPCODE_CALL_STACK: {
|
||||
CHECK_SPACE(3);
|
||||
bool call_ret = _code_ptr[ip] == OPCODE_CALL_STACK_RETURN;
|
||||
int argc = _code_ptr[ip + 1];
|
||||
GET_VARIANT_PTR(func, 2);
|
||||
if (func->get_type() != Variant::OBJECT) {
|
||||
err_text = "This is not a function object.";
|
||||
break;
|
||||
}
|
||||
GDFunctionObject *func_object = ((Object *)(*func))->cast_to<GDFunctionObject>();
|
||||
if (!func_object) {
|
||||
err_text = "This is not a function object.";
|
||||
break;
|
||||
}
|
||||
|
||||
ERR_BREAK(argc < 0);
|
||||
ip += 3;
|
||||
CHECK_SPACE(argc + 1);
|
||||
Variant **argptrs = call_args;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
GET_VARIANT_PTR(v, i);
|
||||
argptrs[i] = v;
|
||||
}
|
||||
Variant::CallError err;
|
||||
if (call_ret) {
|
||||
|
||||
GET_VARIANT_PTR(ret, argc);
|
||||
*ret = func_object->apply((const Variant **)argptrs, argc, err);
|
||||
} else {
|
||||
|
||||
func_object->apply((const Variant **)argptrs, argc, err);
|
||||
}
|
||||
if (err.error != Variant::CallError::CALL_OK) {
|
||||
|
||||
String methodstr = func_object->get_name();
|
||||
err_text = _get_call_error(err, "function object '" + methodstr + "'", (const Variant **)argptrs);
|
||||
break;
|
||||
}
|
||||
ip += argc + 1;
|
||||
}
|
||||
continue;
|
||||
case OPCODE_CALL_RETURN:
|
||||
case OPCODE_CALL: {
|
||||
|
||||
@@ -698,14 +807,61 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
|
||||
|
||||
#endif
|
||||
Variant::CallError err;
|
||||
if (call_ret) {
|
||||
while (true) {
|
||||
String methodstr = *methodname;
|
||||
if ((methodstr == "connect" ||
|
||||
methodstr == "disconnect" ||
|
||||
methodstr == "is_connected") &&
|
||||
argc > 1 &&
|
||||
argptrs[1]->get_type() == Variant::OBJECT) {
|
||||
GDFunctionObject *func_object = ((Object *)(*argptrs[1]))->cast_to<GDFunctionObject>();
|
||||
if (func_object && func_object->is_valid()) {
|
||||
Variant **argptr = (Variant **)memalloc(sizeof(Variant *) * (argc + 1));
|
||||
Variant target(func_object->get_owner());
|
||||
Variant fn(func_object->get_name());
|
||||
|
||||
GET_VARIANT_PTR(ret, argc);
|
||||
base->call_ptr(*methodname, (const Variant **)argptrs, argc, ret, err);
|
||||
} else {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i < 1) {
|
||||
argptr[i] = argptrs[i];
|
||||
} else if (i == 1) {
|
||||
argptr[i] = ⌖
|
||||
argptr[i + 1] = &fn;
|
||||
} else {
|
||||
argptr[i + 1] = argptrs[i];
|
||||
}
|
||||
}
|
||||
|
||||
base->call_ptr(*methodname, (const Variant **)argptrs, argc, NULL, err);
|
||||
if (call_ret) {
|
||||
|
||||
GET_VARIANT_PTR(ret, argc);
|
||||
*ret = base->call(*methodname, (const Variant **)argptr, argc + 1, err);
|
||||
} else {
|
||||
|
||||
base->call(*methodname, (const Variant **)argptr, argc + 1, err);
|
||||
}
|
||||
memfree(argptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (call_ret) {
|
||||
|
||||
GET_VARIANT_PTR(ret, argc);
|
||||
*ret = call_method(base, *methodname, (const Variant **)argptrs, argc, err);
|
||||
} else {
|
||||
|
||||
call_method(base, *methodname, (const Variant **)argptrs, argc, err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// if (call_ret) {
|
||||
|
||||
// GET_VARIANT_PTR(ret,argc);
|
||||
// base->call_ptr(*methodname,(const Variant**)argptrs,argc,ret,err);
|
||||
// } else {
|
||||
|
||||
// base->call_ptr(*methodname,(const Variant**)argptrs,argc,NULL,err);
|
||||
// }
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (GDScriptLanguage::get_singleton()->profiling) {
|
||||
function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
|
||||
@@ -1482,3 +1638,117 @@ GDFunctionState::~GDFunctionState() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
Object *GDFunctionObject::get_owner() const {
|
||||
|
||||
if (!is_valid()) {
|
||||
return NULL;
|
||||
}
|
||||
return instance->owner;
|
||||
}
|
||||
|
||||
Variant GDFunctionObject::apply(VARIANT_ARG_DECLARE) {
|
||||
VARIANT_ARGPTRS;
|
||||
int argc = 0;
|
||||
for (int i = 0; i < VARIANT_ARG_MAX; i++) {
|
||||
if (argptr[i]->get_type() == Variant::NIL)
|
||||
break;
|
||||
argc++;
|
||||
}
|
||||
|
||||
Variant::CallError error;
|
||||
Variant ret = apply(argptr, argc, error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant GDFunctionObject::_apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
return apply(p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
Variant GDFunctionObject::apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
|
||||
if (!is_valid()) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
return function->call(instance, p_args, p_argcount, r_error, NULL);
|
||||
}
|
||||
|
||||
Variant GDFunctionObject::applyv(const Array p_args) {
|
||||
const Variant **args = (const Variant **)memalloc(sizeof(Variant *) * p_args.size());
|
||||
for (int i = 0, t = p_args.size(); i < t; ++i) {
|
||||
args[i] = &p_args[i];
|
||||
}
|
||||
Variant::CallError err;
|
||||
Variant ret = apply((const Variant **)args, p_args.size(), err);
|
||||
memfree(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant GDFunctionObject::apply_with(Object *p_target, const Array p_args) {
|
||||
ERR_FAIL_COND_V(!p_target, Variant());
|
||||
ERR_FAIL_COND_V(!function, Variant());
|
||||
|
||||
Variant::CallError error;
|
||||
Array args(p_args);
|
||||
args.resize(5);
|
||||
Variant ret = p_target->call(get_name(), VARIANT_ARGS_FROM_ARRAY(args));
|
||||
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, Variant());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GDFunctionObject::_bind_methods() {
|
||||
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "apply", &GDFunctionObject::_apply, MethodInfo("apply"));
|
||||
ClassDB::bind_method(D_METHOD("applyv:Variant", "args:Array"), &GDFunctionObject::applyv, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("apply_with:Variant", "target:Object", "args:Array"), &GDFunctionObject::apply_with, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("is_valid"), &GDFunctionObject::is_valid);
|
||||
ClassDB::bind_method(D_METHOD("get_name"), &GDFunctionObject::get_name);
|
||||
ClassDB::bind_method(D_METHOD("get_owner:Object"), &GDFunctionObject::get_owner);
|
||||
}
|
||||
|
||||
Variant GDNativeFunctionObject::apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
if (!is_valid()) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
return ObjectDB::get_instance(target_id)->call(method_name, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
Variant GDNativeFunctionObject::apply_with(Object *p_target, const Array p_args) {
|
||||
ERR_FAIL_COND_V(!p_target, Variant());
|
||||
|
||||
Variant::CallError error;
|
||||
Variant ret = p_target->call(get_name(), VARIANT_ARGS_FROM_ARRAY(p_args));
|
||||
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, Variant());
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant GDLambdaFunctionObject::apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
if (!is_valid()) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
int t = variants.size();
|
||||
Variant **v_vars = (Variant **)memalloc(sizeof(Variant *) * function->lambda_variants.size());
|
||||
for (int i = 0; i < t && i < function->lambda_variants.size(); ++i) {
|
||||
v_vars[i] = &variants[i];
|
||||
}
|
||||
|
||||
Variant ret = function->call(instance, p_args, p_argcount, r_error, NULL, (const Variant **)v_vars);
|
||||
memfree(v_vars);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant GDLambdaFunctionObject::apply_with(Object *p_target, const Array p_args) {
|
||||
ERR_EXPLAIN("Lambda function don't support calling with target.");
|
||||
ERR_FAIL_V(Variant());
|
||||
}
|
||||
|
||||
GDLambdaFunctionObject::~GDLambdaFunctionObject() {
|
||||
if (instance) instance->remove_lambda_function(this);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
OPCODE_CALL_BUILT_IN,
|
||||
OPCODE_CALL_SELF,
|
||||
OPCODE_CALL_SELF_BASE,
|
||||
OPCODE_CALL_STACK,
|
||||
OPCODE_CALL_STACK_RETURN,
|
||||
OPCODE_YIELD,
|
||||
OPCODE_YIELD_SIGNAL,
|
||||
OPCODE_YIELD_RESUME,
|
||||
@@ -90,7 +92,9 @@ public:
|
||||
ADDR_TYPE_STACK = 5,
|
||||
ADDR_TYPE_STACK_VARIABLE = 6,
|
||||
ADDR_TYPE_GLOBAL = 7,
|
||||
ADDR_TYPE_NIL = 8
|
||||
ADDR_TYPE_NIL = 8,
|
||||
ADDR_TYPE_FUNCTION = 9,
|
||||
ADDR_TYPE_LAMBDA_FUNCTION = 10,
|
||||
};
|
||||
|
||||
enum RPCMode {
|
||||
@@ -111,6 +115,8 @@ public:
|
||||
|
||||
private:
|
||||
friend class GDCompiler;
|
||||
friend class GDInstance;
|
||||
friend class GDLambdaFunctionObject;
|
||||
|
||||
StringName source;
|
||||
|
||||
@@ -128,6 +134,7 @@ private:
|
||||
int _call_size;
|
||||
int _initial_line;
|
||||
bool _static;
|
||||
bool _lambda;
|
||||
ScriptInstance::RPCMode rpc_mode;
|
||||
|
||||
GDScript *_script;
|
||||
@@ -137,13 +144,16 @@ private:
|
||||
Vector<StringName> global_names;
|
||||
Vector<int> default_arguments;
|
||||
Vector<int> code;
|
||||
Vector<int> lambda_variants;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Vector<StringName> arg_names;
|
||||
#endif
|
||||
|
||||
Vector<Variant> cache;
|
||||
List<StackDebug> stack_debug;
|
||||
|
||||
_FORCE_INLINE_ Variant call_method(Variant *p_self, const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) const;
|
||||
_FORCE_INLINE_ Variant *_get_variant(int p_address, GDInstance *p_instance, GDScript *p_script, Variant &self, Variant *p_stack, String &r_error) const;
|
||||
_FORCE_INLINE_ String _get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const;
|
||||
|
||||
@@ -217,7 +227,7 @@ public:
|
||||
return default_arguments[p_idx];
|
||||
}
|
||||
|
||||
Variant call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state = NULL);
|
||||
Variant call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state = NULL, const Variant **p_requires_args = NULL);
|
||||
|
||||
_FORCE_INLINE_ ScriptInstance::RPCMode get_rpc_mode() const { return rpc_mode; }
|
||||
GDFunction();
|
||||
@@ -242,4 +252,59 @@ public:
|
||||
~GDFunctionState();
|
||||
};
|
||||
|
||||
class GDFunctionObject : public Reference {
|
||||
GDCLASS(GDFunctionObject, Reference);
|
||||
|
||||
protected:
|
||||
GDFunction *function;
|
||||
GDInstance *instance;
|
||||
friend class GDInstance;
|
||||
friend class GDFunction;
|
||||
friend class GDSignalObject;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ virtual bool is_valid() const { return instance && function; }
|
||||
virtual Object *get_owner() const;
|
||||
|
||||
_FORCE_INLINE_ virtual StringName get_name() const { return function->get_name(); }
|
||||
virtual Variant applyv(const Array p_args);
|
||||
Variant _apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
virtual Variant apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
Variant apply(VARIANT_ARG_LIST);
|
||||
virtual Variant apply_with(Object *p_target, const Array p_args);
|
||||
GDFunctionObject() { instance = NULL, function = NULL; }
|
||||
// ~GDFunctoionObject();
|
||||
};
|
||||
|
||||
class GDNativeFunctionObject : public GDFunctionObject {
|
||||
GDCLASS(GDNativeFunctionObject, GDFunctionObject);
|
||||
|
||||
friend class GDFunction;
|
||||
friend class GDInstance;
|
||||
ObjectID target_id;
|
||||
StringName method_name;
|
||||
|
||||
public:
|
||||
virtual Object *get_owner() const { return (target_id == 0 ? NULL : ObjectDB::get_instance(target_id)); }
|
||||
_FORCE_INLINE_ virtual bool is_valid() const { return target_id != 0 && ObjectDB::get_instance(target_id); }
|
||||
|
||||
_FORCE_INLINE_ virtual StringName get_name() const { return method_name; }
|
||||
virtual Variant apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
virtual Variant apply_with(Object *p_target, const Array p_args);
|
||||
GDNativeFunctionObject() { target_id = 0; }
|
||||
};
|
||||
|
||||
class GDLambdaFunctionObject : public GDFunctionObject {
|
||||
GDCLASS(GDLambdaFunctionObject, GDFunctionObject);
|
||||
friend class GDInstance;
|
||||
Vector<Variant> variants;
|
||||
|
||||
public:
|
||||
virtual Variant apply(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
virtual Variant apply_with(Object *p_target, const Array p_args);
|
||||
|
||||
~GDLambdaFunctionObject();
|
||||
};
|
||||
|
||||
#endif // GD_FUNCTION_H
|
||||
|
||||
@@ -58,7 +58,7 @@ bool GDParser::_end_statement() {
|
||||
return true; //will be handled properly
|
||||
}
|
||||
|
||||
return false;
|
||||
return _after_function;
|
||||
}
|
||||
|
||||
bool GDParser::_enter_indent_block(BlockNode *p_block) {
|
||||
@@ -122,7 +122,6 @@ bool GDParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_s
|
||||
int argidx = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_CURSOR) {
|
||||
_make_completable_call(argidx);
|
||||
completion_node = p_parent;
|
||||
@@ -142,6 +141,9 @@ bool GDParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_s
|
||||
|
||||
p_args.push_back(arg);
|
||||
|
||||
while (tokenizer->get_token() == GDTokenizer::TK_NEWLINE) {
|
||||
tokenizer->advance();
|
||||
}
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
tokenizer->advance();
|
||||
break;
|
||||
@@ -238,6 +240,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
|
||||
}
|
||||
}
|
||||
|
||||
GDTokenizer::Token last_token = tokenizer->get_token();
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PARENTHESIS_OPEN) {
|
||||
//subexpression ()
|
||||
tokenizer->advance();
|
||||
@@ -364,6 +367,32 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
|
||||
constant->value = Math_PI;
|
||||
tokenizer->advance();
|
||||
expr = constant;
|
||||
} else if (tokenizer->get_token() == GDTokenizer::TK_PR_FUNCTION) {
|
||||
StringName method_name;
|
||||
StringName current_name;
|
||||
bool has_id = false;
|
||||
Node *node = _parse_function(current_class, current_function, &method_name, &has_id, ¤t_name);
|
||||
if (!node) {
|
||||
_set_error("Parse function error");
|
||||
return NULL;
|
||||
}
|
||||
if (has_id) {
|
||||
current_block->variables.push_back(current_name);
|
||||
current_block->variable_lines.push_back(tokenizer->get_token_line());
|
||||
|
||||
LocalVarNode *lv = alloc_node<LocalVarNode>();
|
||||
lv->name = current_name;
|
||||
current_block->statements.push_back(lv);
|
||||
OperatorNode *op = alloc_node<OperatorNode>();
|
||||
op->op = OperatorNode::OP_ASSIGN;
|
||||
|
||||
IdentifierNode *id1 = alloc_node<IdentifierNode>();
|
||||
id1->name = current_name;
|
||||
op->arguments.push_back(id1);
|
||||
op->arguments.push_back(node);
|
||||
current_block->statements.push_back(op);
|
||||
}
|
||||
expr = node;
|
||||
} else if (tokenizer->get_token() == GDTokenizer::TK_CONST_INF) {
|
||||
|
||||
//constant defined by tokenizer
|
||||
@@ -592,12 +621,16 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
|
||||
} else {
|
||||
|
||||
SelfNode *self = alloc_node<SelfNode>();
|
||||
self->implicit = true;
|
||||
op->arguments.push_back(self);
|
||||
|
||||
StringName identifier;
|
||||
if (_get_completable_identifier(COMPLETION_FUNCTION, identifier)) {
|
||||
}
|
||||
|
||||
if (current_block && current_block->outer_stack(identifier) && current_function) {
|
||||
if (current_function->type == Node::TYPE_LAMBDA_FUNCTION) static_cast<LambdaFunctionNode *>(current_function)->insert_require(identifier);
|
||||
}
|
||||
IdentifierNode *id = alloc_node<IdentifierNode>();
|
||||
id->name = identifier;
|
||||
op->arguments.push_back(id);
|
||||
@@ -646,6 +679,10 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
|
||||
IdentifierNode *id = alloc_node<IdentifierNode>();
|
||||
id->name = identifier;
|
||||
expr = id;
|
||||
|
||||
if (current_block && current_block->outer_stack(identifier) && current_function) {
|
||||
if (current_function->type == Node::TYPE_LAMBDA_FUNCTION) static_cast<LambdaFunctionNode *>(current_function)->insert_require(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (tokenizer->get_token() == GDTokenizer::TK_OP_ADD || tokenizer->get_token() == GDTokenizer::TK_OP_SUB || tokenizer->get_token() == GDTokenizer::TK_OP_NOT || tokenizer->get_token() == GDTokenizer::TK_OP_BIT_INVERT) {
|
||||
@@ -896,6 +933,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
|
||||
ERR_FAIL_COND_V(!expr, NULL);
|
||||
}
|
||||
|
||||
_after_function = last_token == GDTokenizer::TK_PR_FUNCTION;
|
||||
/******************/
|
||||
/* Parse Indexing */
|
||||
/******************/
|
||||
@@ -1004,12 +1042,13 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
|
||||
/* Parse Operator */
|
||||
/******************/
|
||||
|
||||
if (parenthesis > 0) {
|
||||
//remove empty space (only allowed if inside parenthesis
|
||||
while (tokenizer->get_token() == GDTokenizer::TK_NEWLINE) {
|
||||
tokenizer->advance();
|
||||
}
|
||||
}
|
||||
// moved to _parse_arguments
|
||||
// if (parenthesis>0) {
|
||||
// //remove empty space (only allowed if inside parenthesis
|
||||
// while(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
|
||||
// tokenizer->advance();
|
||||
// }
|
||||
// }
|
||||
|
||||
Expression e;
|
||||
e.is_op = false;
|
||||
@@ -1935,7 +1974,6 @@ void GDParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode
|
||||
branch->body->parent_block = p_block;
|
||||
p_block->sub_blocks.push_back(branch->body);
|
||||
current_block = branch->body;
|
||||
|
||||
_parse_block(branch->body, p_static);
|
||||
|
||||
current_block = p_block;
|
||||
@@ -2639,6 +2677,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) {
|
||||
cf_for->arguments.push_back(container);
|
||||
|
||||
cf_for->body = alloc_node<BlockNode>();
|
||||
cf_for->body->arguments.push_back(id->name);
|
||||
cf_for->body->parent_block = p_block;
|
||||
p_block->sub_blocks.push_back(cf_for->body);
|
||||
|
||||
@@ -2785,8 +2824,35 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) {
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
case GDTokenizer::TK_PR_FUNCTION: {
|
||||
StringName method_name;
|
||||
StringName current_name;
|
||||
bool has_id = false;
|
||||
Node *node = _parse_function(current_class, current_function, &method_name, &has_id, ¤t_name);
|
||||
if (!node) {
|
||||
_set_error("Parse function error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_id) {
|
||||
p_block->variables.push_back(current_name);
|
||||
p_block->variable_lines.push_back(tokenizer->get_token_line());
|
||||
|
||||
LocalVarNode *lv = alloc_node<LocalVarNode>();
|
||||
lv->name = current_name;
|
||||
p_block->statements.push_back(lv);
|
||||
|
||||
OperatorNode *op = alloc_node<OperatorNode>();
|
||||
op->op = OperatorNode::OP_ASSIGN;
|
||||
|
||||
IdentifierNode *id1 = alloc_node<IdentifierNode>();
|
||||
id1->name = current_name;
|
||||
op->arguments.push_back(id1);
|
||||
op->arguments.push_back(node);
|
||||
p_block->statements.push_back(op);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
Node *expression = _parse_and_reduce_expression(p_block, p_static, false, true);
|
||||
if (!expression) {
|
||||
if (_recover_from_completion()) {
|
||||
@@ -2815,6 +2881,224 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) {
|
||||
}
|
||||
}
|
||||
|
||||
GDParser::Node *GDParser::_parse_function(ClassNode *p_class, FunctionNode *p_func, StringName *method_name, bool *has_identifier, StringName *identifier) {
|
||||
bool _static = p_func ? p_func->_static : tokenizer->get_token(-1) == GDTokenizer::TK_PR_STATIC;
|
||||
|
||||
tokenizer->advance();
|
||||
StringName name;
|
||||
|
||||
if (p_func) {
|
||||
String current_name = String(p_func->name);
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER) {
|
||||
StringName mn = tokenizer->get_token_identifier();
|
||||
name = StringName("@" + current_name + "@" + mn + "@" + itos(tokenizer->get_token_line(-1)) + "@" + self_path);
|
||||
if (has_identifier) *has_identifier = true;
|
||||
if (identifier) *identifier = mn;
|
||||
tokenizer->advance();
|
||||
} else {
|
||||
name = StringName("@" + current_name + "@" + itos(tokenizer->get_token_line(-1)) + self_path);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (_get_completable_identifier(COMPLETION_VIRTUAL_FUNC, name)) {
|
||||
}
|
||||
}
|
||||
|
||||
if (name == StringName()) {
|
||||
|
||||
_set_error("Expected identifier after 'func' (syntax: 'func <identifier>([arguments]):' ).");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_class->functions.size(); i++) {
|
||||
if (p_class->functions[i]->name == name) {
|
||||
_set_error("Function '" + String(name) + "' already exists in this class (at line: " + itos(p_class->functions[i]->line) + ").");
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < p_class->static_functions.size(); i++) {
|
||||
if (p_class->static_functions[i]->name == name) {
|
||||
_set_error("Function '" + String(name) + "' already exists in this class (at line: " + itos(p_class->static_functions[i]->line) + ").");
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_OPEN) {
|
||||
|
||||
_set_error("Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' ).");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
|
||||
Vector<StringName> arguments;
|
||||
Vector<Node *> default_values;
|
||||
|
||||
int fnline = tokenizer->get_token_line();
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
//has arguments
|
||||
bool defaulting = false;
|
||||
while (true) {
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PR_VAR) {
|
||||
|
||||
tokenizer->advance(); //var before the identifier is allowed
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) {
|
||||
|
||||
_set_error("Expected identifier for argument.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StringName argname = tokenizer->get_token_identifier();
|
||||
arguments.push_back(argname);
|
||||
|
||||
tokenizer->advance();
|
||||
|
||||
if (defaulting && tokenizer->get_token() != GDTokenizer::TK_OP_ASSIGN) {
|
||||
|
||||
_set_error("Default parameter expected.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//tokenizer->advance();
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_OP_ASSIGN) {
|
||||
defaulting = true;
|
||||
tokenizer->advance(1);
|
||||
Node *defval = NULL;
|
||||
|
||||
defval = _parse_and_reduce_expression(p_class, _static);
|
||||
if (!defval || error_set)
|
||||
return NULL;
|
||||
|
||||
OperatorNode *on = alloc_node<OperatorNode>();
|
||||
on->op = OperatorNode::OP_ASSIGN;
|
||||
|
||||
IdentifierNode *in = alloc_node<IdentifierNode>();
|
||||
in->name = argname;
|
||||
|
||||
on->arguments.push_back(in);
|
||||
on->arguments.push_back(defval);
|
||||
/* no ..
|
||||
if (defval->type!=Node::TYPE_CONSTANT) {
|
||||
|
||||
_set_error("default argument must be constant");
|
||||
}
|
||||
*/
|
||||
default_values.push_back(on);
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
|
||||
tokenizer->advance();
|
||||
continue;
|
||||
} else if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
|
||||
_set_error("Expected ',' or ')'.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
|
||||
BlockNode *block = alloc_node<BlockNode>();
|
||||
block->parent_class = p_class;
|
||||
block->arguments = arguments;
|
||||
|
||||
if (name == "_init") {
|
||||
|
||||
if (p_class->extends_used) {
|
||||
|
||||
OperatorNode *cparent = alloc_node<OperatorNode>();
|
||||
cparent->op = OperatorNode::OP_PARENT_CALL;
|
||||
block->statements.push_back(cparent);
|
||||
|
||||
IdentifierNode *id = alloc_node<IdentifierNode>();
|
||||
id->name = "_init";
|
||||
cparent->arguments.push_back(id);
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PERIOD) {
|
||||
tokenizer->advance();
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_OPEN) {
|
||||
_set_error("expected '(' for parent constructor arguments.");
|
||||
}
|
||||
tokenizer->advance();
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
//has arguments
|
||||
while (true) {
|
||||
|
||||
Node *arg = _parse_and_reduce_expression(p_class, _static);
|
||||
cparent->arguments.push_back(arg);
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
|
||||
tokenizer->advance();
|
||||
continue;
|
||||
} else if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
|
||||
_set_error("Expected ',' or ')'.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PERIOD) {
|
||||
|
||||
_set_error("Parent constructor call found for a class without inheritance.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_enter_indent_block(block)) {
|
||||
|
||||
_set_error("Indented block expected.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FunctionNode *function;
|
||||
if (p_func && current_block) {
|
||||
block->parent_block = current_block;
|
||||
LambdaFunctionNode *f = alloc_node<LambdaFunctionNode>();
|
||||
f->parent = p_func;
|
||||
function = f;
|
||||
} else {
|
||||
function = alloc_node<FunctionNode>();
|
||||
}
|
||||
function->name = name;
|
||||
function->arguments = arguments;
|
||||
function->default_values = default_values;
|
||||
function->_static = _static;
|
||||
function->line = fnline;
|
||||
|
||||
if (_static)
|
||||
p_class->static_functions.push_back(function);
|
||||
else
|
||||
p_class->functions.push_back(function);
|
||||
|
||||
FunctionNode *pre_func = current_function;
|
||||
BlockNode *pre_block = current_block;
|
||||
current_function = function;
|
||||
function->body = block;
|
||||
current_block = block;
|
||||
_parse_block(block, _static);
|
||||
current_function = pre_func;
|
||||
current_block = pre_func ? pre_block : NULL;
|
||||
|
||||
if (method_name != NULL) *method_name = name;
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
bool GDParser::_parse_newline() {
|
||||
|
||||
if (tokenizer->get_token(1) != GDTokenizer::TK_EOF && tokenizer->get_token(1) != GDTokenizer::TK_NEWLINE) {
|
||||
@@ -3027,211 +3311,8 @@ void GDParser::_parse_class(ClassNode *p_class) {
|
||||
}; //fallthrough to function
|
||||
case GDTokenizer::TK_PR_FUNCTION: {
|
||||
|
||||
bool _static = false;
|
||||
pending_newline = -1;
|
||||
|
||||
if (tokenizer->get_token(-1) == GDTokenizer::TK_PR_STATIC) {
|
||||
|
||||
_static = true;
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
StringName name;
|
||||
|
||||
if (_get_completable_identifier(COMPLETION_VIRTUAL_FUNC, name)) {
|
||||
}
|
||||
|
||||
if (name == StringName()) {
|
||||
|
||||
_set_error("Expected identifier after 'func' (syntax: 'func <identifier>([arguments]):' ).");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_class->functions.size(); i++) {
|
||||
if (p_class->functions[i]->name == name) {
|
||||
_set_error("Function '" + String(name) + "' already exists in this class (at line: " + itos(p_class->functions[i]->line) + ").");
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < p_class->static_functions.size(); i++) {
|
||||
if (p_class->static_functions[i]->name == name) {
|
||||
_set_error("Function '" + String(name) + "' already exists in this class (at line: " + itos(p_class->static_functions[i]->line) + ").");
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_OPEN) {
|
||||
|
||||
_set_error("Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' ).");
|
||||
return;
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
|
||||
Vector<StringName> arguments;
|
||||
Vector<Node *> default_values;
|
||||
|
||||
int fnline = tokenizer->get_token_line();
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
//has arguments
|
||||
bool defaulting = false;
|
||||
while (true) {
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_NEWLINE) {
|
||||
tokenizer->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PR_VAR) {
|
||||
|
||||
tokenizer->advance(); //var before the identifier is allowed
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) {
|
||||
|
||||
_set_error("Expected identifier for argument.");
|
||||
return;
|
||||
}
|
||||
|
||||
StringName argname = tokenizer->get_token_identifier();
|
||||
arguments.push_back(argname);
|
||||
|
||||
tokenizer->advance();
|
||||
|
||||
if (defaulting && tokenizer->get_token() != GDTokenizer::TK_OP_ASSIGN) {
|
||||
|
||||
_set_error("Default parameter expected.");
|
||||
return;
|
||||
}
|
||||
|
||||
//tokenizer->advance();
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_OP_ASSIGN) {
|
||||
defaulting = true;
|
||||
tokenizer->advance(1);
|
||||
Node *defval = NULL;
|
||||
|
||||
defval = _parse_and_reduce_expression(p_class, _static);
|
||||
if (!defval || error_set)
|
||||
return;
|
||||
|
||||
OperatorNode *on = alloc_node<OperatorNode>();
|
||||
on->op = OperatorNode::OP_ASSIGN;
|
||||
|
||||
IdentifierNode *in = alloc_node<IdentifierNode>();
|
||||
in->name = argname;
|
||||
|
||||
on->arguments.push_back(in);
|
||||
on->arguments.push_back(defval);
|
||||
/* no ..
|
||||
if (defval->type!=Node::TYPE_CONSTANT) {
|
||||
|
||||
_set_error("default argument must be constant");
|
||||
}
|
||||
*/
|
||||
default_values.push_back(on);
|
||||
}
|
||||
|
||||
while (tokenizer->get_token() == GDTokenizer::TK_NEWLINE) {
|
||||
tokenizer->advance();
|
||||
}
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
|
||||
tokenizer->advance();
|
||||
continue;
|
||||
} else if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
|
||||
_set_error("Expected ',' or ')'.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
|
||||
BlockNode *block = alloc_node<BlockNode>();
|
||||
block->parent_class = p_class;
|
||||
|
||||
if (name == "_init") {
|
||||
|
||||
if (p_class->extends_used) {
|
||||
|
||||
OperatorNode *cparent = alloc_node<OperatorNode>();
|
||||
cparent->op = OperatorNode::OP_PARENT_CALL;
|
||||
block->statements.push_back(cparent);
|
||||
|
||||
IdentifierNode *id = alloc_node<IdentifierNode>();
|
||||
id->name = "_init";
|
||||
cparent->arguments.push_back(id);
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PERIOD) {
|
||||
tokenizer->advance();
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_OPEN) {
|
||||
_set_error("expected '(' for parent constructor arguments.");
|
||||
}
|
||||
tokenizer->advance();
|
||||
|
||||
if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
//has arguments
|
||||
parenthesis++;
|
||||
while (true) {
|
||||
|
||||
Node *arg = _parse_and_reduce_expression(p_class, _static);
|
||||
cparent->arguments.push_back(arg);
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
|
||||
tokenizer->advance();
|
||||
continue;
|
||||
} else if (tokenizer->get_token() != GDTokenizer::TK_PARENTHESIS_CLOSE) {
|
||||
|
||||
_set_error("Expected ',' or ')'.");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
parenthesis--;
|
||||
}
|
||||
|
||||
tokenizer->advance();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (tokenizer->get_token() == GDTokenizer::TK_PERIOD) {
|
||||
|
||||
_set_error("Parent constructor call found for a class without inheritance.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_enter_indent_block(block)) {
|
||||
|
||||
_set_error("Indented block expected.");
|
||||
return;
|
||||
}
|
||||
|
||||
FunctionNode *function = alloc_node<FunctionNode>();
|
||||
function->name = name;
|
||||
function->arguments = arguments;
|
||||
function->default_values = default_values;
|
||||
function->_static = _static;
|
||||
function->line = fnline;
|
||||
|
||||
function->rpc_mode = rpc_mode;
|
||||
rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
|
||||
|
||||
if (_static)
|
||||
p_class->static_functions.push_back(function);
|
||||
else
|
||||
p_class->functions.push_back(function);
|
||||
|
||||
current_function = function;
|
||||
function->body = block;
|
||||
current_block = block;
|
||||
_parse_block(block, _static);
|
||||
current_block = NULL;
|
||||
_parse_function(p_class);
|
||||
|
||||
//arguments
|
||||
} break;
|
||||
@@ -4363,6 +4444,7 @@ GDParser::GDParser() {
|
||||
list = NULL;
|
||||
tokenizer = NULL;
|
||||
pending_newline = -1;
|
||||
_after_function = false;
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
enum Type {
|
||||
TYPE_CLASS,
|
||||
TYPE_FUNCTION,
|
||||
TYPE_LAMBDA_FUNCTION,
|
||||
TYPE_BUILT_IN_FUNCTION,
|
||||
TYPE_BLOCK,
|
||||
TYPE_IDENTIFIER,
|
||||
@@ -136,18 +137,54 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct LambdaFunctionNode : public FunctionNode {
|
||||
|
||||
FunctionNode *parent;
|
||||
Vector<StringName> require_keys;
|
||||
|
||||
void insert_require(StringName p_key) {
|
||||
if (body->variables.find(p_key) < 0 && body->arguments.find(p_key) < 0) {
|
||||
if (require_keys.find(p_key) < 0) {
|
||||
require_keys.push_back(p_key);
|
||||
} else if (parent->type == Node::TYPE_LAMBDA_FUNCTION) {
|
||||
LambdaFunctionNode *func = static_cast<LambdaFunctionNode *>(parent);
|
||||
func->insert_require(p_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LambdaFunctionNode() {
|
||||
type = TYPE_LAMBDA_FUNCTION;
|
||||
_static = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockNode : public Node {
|
||||
|
||||
ClassNode *parent_class;
|
||||
BlockNode *parent_block;
|
||||
Map<StringName, int> locals;
|
||||
List<Node *> statements;
|
||||
Vector<StringName> arguments;
|
||||
Vector<StringName> variables;
|
||||
Vector<int> variable_lines;
|
||||
|
||||
//the following is useful for code completion
|
||||
List<BlockNode *> sub_blocks;
|
||||
int end_line;
|
||||
|
||||
_FORCE_INLINE_ bool has_identifier(const StringName &identifier) const {
|
||||
return variables.find(identifier) >= 0 ? true : (parent_block ? parent_block->has_identifier(identifier) : false);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_stack_argument(const StringName &identifier) {
|
||||
return arguments.find(identifier) >= 0 ? true : parent_block ? parent_block->is_stack_argument(identifier) : false;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool outer_stack(const StringName &identifier) const {
|
||||
return arguments.find(identifier) >= 0 || variables.find(identifier) >= 0 ? false : (parent_block ? parent_block->has_identifier(identifier) || parent_block->is_stack_argument(identifier) : false);
|
||||
}
|
||||
|
||||
BlockNode() {
|
||||
type = TYPE_BLOCK;
|
||||
end_line = -1;
|
||||
@@ -206,7 +243,11 @@ public:
|
||||
};
|
||||
|
||||
struct SelfNode : public Node {
|
||||
SelfNode() { type = TYPE_SELF; }
|
||||
bool implicit;
|
||||
SelfNode() {
|
||||
type = TYPE_SELF;
|
||||
implicit = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct OperatorNode : public Node {
|
||||
@@ -459,6 +500,8 @@ private:
|
||||
|
||||
int pending_newline;
|
||||
|
||||
bool _after_function;
|
||||
|
||||
List<int> tab_level;
|
||||
|
||||
String base_path;
|
||||
@@ -494,6 +537,7 @@ private:
|
||||
bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false);
|
||||
bool _enter_indent_block(BlockNode *p_block = NULL);
|
||||
bool _parse_newline();
|
||||
Node *_parse_function(ClassNode *p_class, FunctionNode *p_func = NULL, StringName *method_name = NULL, bool *has_identifier = NULL, StringName *identifier = NULL);
|
||||
Node *_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign = false, bool p_parsing_constant = false);
|
||||
Node *_reduce_expression(Node *p_node, bool p_to_const = false);
|
||||
Node *_parse_and_reduce_expression(Node *p_parent, bool p_static, bool p_reduce_const = false, bool p_allow_assign = false);
|
||||
|
||||
@@ -496,20 +496,15 @@ bool GDScript::_update_exports() {
|
||||
}
|
||||
}
|
||||
|
||||
if (path != get_path()) {
|
||||
Ref<GDScript> bf = ResourceLoader::load(path);
|
||||
|
||||
Ref<GDScript> bf = ResourceLoader::load(path);
|
||||
if (bf.is_valid()) {
|
||||
|
||||
if (bf.is_valid()) {
|
||||
//print_line("parent is: "+bf->get_path());
|
||||
base_cache = bf;
|
||||
bf->inheriters_cache.insert(get_instance_ID());
|
||||
|
||||
//print_line("parent is: "+bf->get_path());
|
||||
base_cache = bf;
|
||||
bf->inheriters_cache.insert(get_instance_ID());
|
||||
|
||||
//bf->_update_exports(p_instances,true,false);
|
||||
}
|
||||
} else {
|
||||
ERR_PRINT(("Path extending itself in " + path).utf8().get_data());
|
||||
//bf->_update_exports(p_instances,true,false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1004,7 +999,6 @@ bool GDInstance::set(const StringName &p_name, const Variant &p_value) {
|
||||
}
|
||||
|
||||
bool GDInstance::get(const StringName &p_name, Variant &r_ret) const {
|
||||
|
||||
const GDScript *sptr = script.ptr();
|
||||
while (sptr) {
|
||||
|
||||
@@ -1053,26 +1047,61 @@ bool GDInstance::get(const StringName &p_name, Variant &r_ret) const {
|
||||
}
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
|
||||
{
|
||||
Ref<GDFunctionObject> func = const_cast<GDInstance *>(this)->get_function(p_name);
|
||||
if (func != NULL) {
|
||||
r_ret = Variant(func);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant::Type GDInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
|
||||
|
||||
Ref<GDFunctionObject> GDInstance::get_function(StringName p_name) {
|
||||
const GDScript *sptr = script.ptr();
|
||||
while (sptr) {
|
||||
const Map<StringName, Ref<GDFunctionObject> >::Element *E = functions.find(p_name);
|
||||
if (E) {
|
||||
return E->get();
|
||||
} else {
|
||||
const Map<StringName, GDFunction *>::Element *E_ = sptr->member_functions.find(p_name);
|
||||
if (E_) {
|
||||
const GDFunction *gdfunc = E_->get();
|
||||
if (gdfunc->_lambda) return NULL;
|
||||
Ref<GDFunctionObject> func = memnew(GDFunctionObject);
|
||||
func->instance = const_cast<GDInstance *>(this);
|
||||
func->function = const_cast<GDFunction *>(gdfunc);
|
||||
functions.insert(p_name, Variant(func));
|
||||
return functions[p_name];
|
||||
}
|
||||
}
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sptr->member_info.has(p_name)) {
|
||||
if (r_is_valid)
|
||||
*r_is_valid = true;
|
||||
return sptr->member_info[p_name].type;
|
||||
Ref<GDLambdaFunctionObject> GDInstance::get_lambda_function(StringName p_name, Variant *p_stack, int p_stack_size) {
|
||||
const GDScript *sptr = script.ptr();
|
||||
while (sptr) {
|
||||
const Map<StringName, GDFunction *>::Element *E_ = sptr->member_functions.find(p_name);
|
||||
if (E_) {
|
||||
Ref<GDLambdaFunctionObject> func = memnew(GDLambdaFunctionObject);
|
||||
func->instance = const_cast<GDInstance *>(this);
|
||||
const GDFunction *gdfunc = E_->get();
|
||||
func->function = const_cast<GDFunction *>(gdfunc);
|
||||
|
||||
for (int i = 0; i < gdfunc->lambda_variants.size(); ++i) {
|
||||
int idx = gdfunc->lambda_variants[i];
|
||||
if (p_stack_size <= idx) return NULL;
|
||||
func->variants.push_back(Variant(p_stack[idx]));
|
||||
}
|
||||
lambda_functions.push_back(func.ptr());
|
||||
return Variant(func);
|
||||
}
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
|
||||
if (r_is_valid)
|
||||
*r_is_valid = false;
|
||||
return Variant::NIL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const {
|
||||
@@ -1189,6 +1218,24 @@ void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const {
|
||||
}
|
||||
}
|
||||
|
||||
Variant::Type GDInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
|
||||
|
||||
const GDScript *sptr = script.ptr();
|
||||
while (sptr) {
|
||||
|
||||
if (sptr->member_info.has(p_name)) {
|
||||
if (r_is_valid)
|
||||
*r_is_valid = true;
|
||||
return sptr->member_info[p_name].type;
|
||||
}
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
|
||||
if (r_is_valid)
|
||||
*r_is_valid = false;
|
||||
return Variant::NIL;
|
||||
}
|
||||
|
||||
void GDInstance::get_method_list(List<MethodInfo> *p_list) const {
|
||||
|
||||
const GDScript *sptr = script.ptr();
|
||||
@@ -1219,6 +1266,41 @@ bool GDInstance::has_method(const StringName &p_method) const {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant GDInstance::call_member(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
GDScript *sptr = script.ptr();
|
||||
while (sptr) {
|
||||
{
|
||||
const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_method);
|
||||
if (E) {
|
||||
Variant var;
|
||||
do {
|
||||
if (E->get().getter) {
|
||||
Variant::CallError err;
|
||||
var = const_cast<GDInstance *>(this)->call(E->get().getter, NULL, 0, err);
|
||||
if (err.error == Variant::CallError::CALL_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var = members[E->get().index];
|
||||
} while (false);
|
||||
if (var.get_type() == Variant::OBJECT) {
|
||||
GDFunctionObject *func_object = ((Object *)var)->cast_to<GDFunctionObject>();
|
||||
if (func_object)
|
||||
return func_object->apply(p_args, p_argcount, r_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<StringName, GDFunction *>::Element *E = sptr->member_functions.find(p_method);
|
||||
if (E) {
|
||||
return E->get()->call(this, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
return owner->call(p_method, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
Variant GDInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
|
||||
//printf("calling %ls:%i method %ls\n", script->get_path().c_str(), -1, String(p_method).c_str());
|
||||
@@ -1229,6 +1311,7 @@ Variant GDInstance::call(const StringName &p_method, const Variant **p_args, int
|
||||
if (E) {
|
||||
return E->get()->call(this, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
@@ -1382,6 +1465,12 @@ GDInstance::~GDInstance() {
|
||||
GDScriptLanguage::singleton->lock->unlock();
|
||||
#endif
|
||||
}
|
||||
for (Map<StringName, Ref<GDFunctionObject> >::Element *E = functions.front(); E; E = E->next()) {
|
||||
E->get()->instance = NULL;
|
||||
}
|
||||
for (int i = 0; i < lambda_functions.size(); ++i) {
|
||||
lambda_functions[i]->instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/************* SCRIPT LANGUAGE **************/
|
||||
|
||||
@@ -76,6 +76,7 @@ class GDScript : public Script {
|
||||
GDScript *_owner; //for subclasses
|
||||
|
||||
Set<StringName> members; //members are just indices to the instanced script.
|
||||
Vector<StringName> function_indices;
|
||||
Map<StringName, Variant> constants;
|
||||
Map<StringName, GDFunction *> member_functions;
|
||||
Map<StringName, MemberInfo> member_indices; //members are just indices to the instanced script.
|
||||
@@ -205,6 +206,10 @@ class GDInstance : public ScriptInstance {
|
||||
friend class GDScript;
|
||||
friend class GDFunction;
|
||||
friend class GDFunctions;
|
||||
friend class GDFunctionObject;
|
||||
friend class GDLambdaFunctionObject;
|
||||
friend class GDNativeFunctionObject;
|
||||
friend class GDSignalObject;
|
||||
friend class GDCompiler;
|
||||
|
||||
Object *owner;
|
||||
@@ -213,9 +218,16 @@ class GDInstance : public ScriptInstance {
|
||||
Map<StringName, int> member_indices_cache; //used only for hot script reloading
|
||||
#endif
|
||||
Vector<Variant> members;
|
||||
Map<StringName, Ref<GDFunctionObject> > functions;
|
||||
Vector<GDLambdaFunctionObject *> lambda_functions;
|
||||
bool base_ref;
|
||||
|
||||
void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount);
|
||||
Ref<GDFunctionObject> get_function(StringName p_name);
|
||||
Ref<GDLambdaFunctionObject> get_lambda_function(StringName p_name, Variant *p_stack, int p_stack_size);
|
||||
_FORCE_INLINE_ void remove_lambda_function(GDLambdaFunctionObject *func) { lambda_functions.erase(func); }
|
||||
|
||||
Variant call_member(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ Object *get_owner() { return owner; }
|
||||
|
||||
@@ -136,6 +136,9 @@ static void register_editor_plugin() {
|
||||
void register_gdscript_types() {
|
||||
|
||||
ClassDB::register_class<GDScript>();
|
||||
ClassDB::register_virtual_class<GDFunctionObject>();
|
||||
ClassDB::register_virtual_class<GDNativeFunctionObject>();
|
||||
ClassDB::register_virtual_class<GDLambdaFunctionObject>();
|
||||
ClassDB::register_virtual_class<GDFunctionState>();
|
||||
|
||||
script_language_gd = memnew(GDScriptLanguage);
|
||||
|
||||
Reference in New Issue
Block a user