Compare commits

...

1 Commits

Author SHA1 Message Date
dbsGen
971c36ab35 Add lambda expressions and function objects in GDScript
Squashed version of #2704.

Edit by Rémi Verschelde: Squashed all changes in one commit and fixed some
indentation issues. Also applied clang-format to match the new master branch.
2017-03-05 22:35:43 +01:00
9 changed files with 931 additions and 290 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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] = &target;
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);
}

View File

@@ -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

View File

@@ -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, &current_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, &current_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();
}

View File

@@ -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);

View File

@@ -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 **************/

View File

@@ -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; }

View File

@@ -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);