Merge pull request #26547 from vnen/gdscript-dependency-parse

Add a parse mode for GDScript which doesn't load dependencies
This commit is contained in:
Juan Linietsky 2019-03-03 18:00:12 -03:00 committed by GitHub
commit a9fe834a8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 79 deletions

View file

@ -409,6 +409,23 @@ int FileAccess::get_buffer(uint8_t *p_dst, int p_length) const {
return i;
}
String FileAccess::get_as_utf8_string() const {
PoolVector<uint8_t> sourcef;
int len = get_len();
sourcef.resize(len + 1);
PoolVector<uint8_t>::Write w = sourcef.write();
int r = get_buffer(w.ptr(), len);
ERR_FAIL_COND_V(r != len, String());
w[len] = 0;
String s;
if (s.parse_utf8((const char *)w.ptr())) {
return String();
}
return s;
}
void FileAccess::store_16(uint16_t p_dest) {
uint8_t a, b;

View file

@ -113,6 +113,7 @@ public:
virtual String get_line() const;
virtual String get_token() const;
virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
virtual String get_as_utf8_string() const;
/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
* It's not about the current CPU type but file formats.

View file

@ -1840,68 +1840,86 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
return String();
}
int len = f->get_len();
sourcef.resize(len + 1);
PoolVector<uint8_t>::Write w = sourcef.write();
int r = f->get_buffer(w.ptr(), len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, String());
w[len] = 0;
String s;
if (s.parse_utf8((const char *)w.ptr())) {
return String();
}
String source = f->get_as_utf8_string();
GDScriptParser parser;
parser.parse(s, p_path.get_base_dir(), true, p_path);
parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true);
if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
if (r_base_type) {
GDScriptParser::DataType base_type;
if (c->base_type.has_type) {
base_type = c->base_type;
while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
base_type = base_type.class_type->base_type;
} break;
case GDScriptParser::DataType::GDSCRIPT: {
Ref<GDScript> gds = base_type.script_type;
if (gds.is_valid()) {
base_type.kind = GDScriptParser::DataType::NATIVE;
base_type.native_type = gds->get_instance_base_type();
} else {
base_type = GDScriptParser::DataType();
}
} break;
default: {
base_type = GDScriptParser::DataType();
} break;
}
}
}
if (base_type.has_type) {
*r_base_type = base_type.native_type;
} else {
// Fallback
if (c->extends_used && c->extends_class.size() == 1) {
*r_base_type = c->extends_class[0];
} else if (!c->extends_used) {
*r_base_type = "Reference";
}
}
}
if (r_icon_path) {
if (c->icon_path.empty() || c->icon_path.is_abs_path())
*r_icon_path = c->icon_path;
else if (c->icon_path.is_rel_path())
*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
}
if (r_base_type) {
const GDScriptParser::ClassNode *subclass = c;
String path = p_path;
GDScriptParser subparser;
while (subclass) {
if (subclass->extends_used) {
if (subclass->extends_file) {
if (subclass->extends_class.size() == 0) {
get_global_class_name(subclass->extends_file, r_base_type);
subclass = NULL;
break;
} else {
Vector<StringName> extend_classes = subclass->extends_class;
FileAccess *subfile = FileAccess::open(subclass->extends_file, FileAccess::READ);
if (!subfile) {
break;
}
String subsource = subfile->get_as_utf8_string();
if (subsource.empty()) {
break;
}
String subpath = subclass->extends_file;
if (subpath.is_rel_path()) {
subpath = path.get_base_dir().plus_file(subpath).simplify_path();
}
if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, NULL, true)) {
break;
}
path = subpath;
if (!subparser.get_parse_tree() || subparser.get_parse_tree()->type != GDScriptParser::Node::TYPE_CLASS) {
break;
}
subclass = static_cast<const GDScriptParser::ClassNode *>(subparser.get_parse_tree());
while (extend_classes.size() > 0) {
bool found = false;
for (int i = 0; i < subclass->subclasses.size(); i++) {
const GDScriptParser::ClassNode *inner_class = subclass->subclasses[i];
if (inner_class->name == extend_classes[0]) {
extend_classes.remove(0);
found = true;
subclass = inner_class;
break;
}
}
if (!found) {
subclass = NULL;
break;
}
}
}
} else if (subclass->extends_class.size() == 1) {
*r_base_type = subclass->extends_class[0];
subclass = NULL;
} else {
break;
}
} else {
*r_base_type = "Reference";
subclass = NULL;
}
}
}
return c->name;
}
@ -2183,6 +2201,26 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con
return "";
}
void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
FileAccess *file = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND(!file);
String source = file->get_as_utf8_string();
if (source.empty()) {
return;
}
GDScriptParser parser;
if (OK != parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true)) {
return;
}
for (const List<String>::Element *E = parser.get_dependencies().front(); E; E = E->next()) {
p_dependencies->push_back(E->get());
}
}
Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
Ref<GDScript> sqscr = p_resource;

View file

@ -511,6 +511,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {

View file

@ -473,29 +473,31 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
Ref<Resource> res;
if (!validating) {
dependencies.push_back(path);
if (!dependencies_only) {
if (!validating) {
//this can be too slow for just validating code
if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) {
res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
} else if (!for_completion || FileAccess::exists(path)) {
res = ResourceLoader::load(path);
//this can be too slow for just validating code
if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) {
res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
} else if (!for_completion || FileAccess::exists(path)) {
res = ResourceLoader::load(path);
}
} else {
if (!FileAccess::exists(path)) {
_set_error("Can't preload resource at path: " + path);
return NULL;
} else if (ScriptCodeCompletionCache::get_singleton()) {
res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
}
}
} else {
if (!FileAccess::exists(path)) {
if (!res.is_valid()) {
_set_error("Can't preload resource at path: " + path);
return NULL;
} else if (ScriptCodeCompletionCache::get_singleton()) {
res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
}
}
if (!res.is_valid()) {
_set_error("Can't preload resource at path: " + path);
return NULL;
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected ')' after 'preload' path");
return NULL;
@ -812,17 +814,19 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
bfn = true;
}
// Check parents for the constant
if (!bfn && cln->extends_file != StringName()) {
Ref<GDScript> parent = ResourceLoader::load(cln->extends_file);
if (parent.is_valid() && parent->is_valid()) {
Map<StringName, Variant> parent_constants;
parent->get_constants(&parent_constants);
if (parent_constants.has(identifier)) {
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = parent_constants[identifier];
expr = constant;
bfn = true;
if (!dependencies_only) {
// Check parents for the constant
if (!bfn && cln->extends_file != StringName()) {
Ref<GDScript> parent = ResourceLoader::load(cln->extends_file);
if (parent.is_valid() && parent->is_valid()) {
Map<StringName, Variant> parent_constants;
parent->get_constants(&parent_constants);
if (parent_constants.has(identifier)) {
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = parent_constants[identifier];
expr = constant;
bfn = true;
}
}
}
}
@ -3378,6 +3382,13 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) {
p_class->extends_file = constant;
tokenizer->advance();
// Add parent script as a dependency
String parent = constant;
if (parent.is_rel_path()) {
parent = base_path.plus_file(parent).simplify_path();
}
dependencies.push_back(parent);
if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) {
return;
} else
@ -8152,6 +8163,10 @@ Error GDScriptParser::_parse(const String &p_base_path) {
return ERR_PARSE_ERROR;
}
if (dependencies_only) {
return OK;
}
_determine_inheritance(main_class);
if (error_set) {
@ -8230,7 +8245,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St
return ret;
}
Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) {
Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines, bool p_dependencies_only) {
clear();
@ -8240,6 +8255,7 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo
validating = p_just_validate;
for_completion = p_for_completion;
dependencies_only = p_dependencies_only;
#ifdef DEBUG_ENABLED
safe_lines = r_safe_lines;
#endif // DEBUG_ENABLED
@ -8296,6 +8312,8 @@ void GDScriptParser::clear() {
parenthesis = 0;
current_export.type = Variant::NIL;
check_types = true;
dependencies_only = false;
dependencies.clear();
error = "";
#ifdef DEBUG_ENABLED
safe_lines = NULL;

View file

@ -533,6 +533,8 @@ private:
int error_line;
int error_column;
bool check_types;
bool dependencies_only;
List<String> dependencies;
#ifdef DEBUG_ENABLED
Set<int> *safe_lines;
#endif // DEBUG_ENABLED
@ -634,7 +636,7 @@ public:
#ifdef DEBUG_ENABLED
const List<GDScriptWarning> &get_warnings() const { return warnings; }
#endif // DEBUG_ENABLED
Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL);
Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL, bool p_dependencies_only = false);
Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
bool is_tool_script() const;
@ -653,6 +655,8 @@ public:
int get_completion_argument_index();
int get_completion_identifier_is_function();
const List<String> &get_dependencies() const { return dependencies; }
void clear();
GDScriptParser();
~GDScriptParser();