| 1 | /**************************************************************************/ |
| 2 | /* script_language.h */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #ifndef SCRIPT_LANGUAGE_H |
| 32 | #define SCRIPT_LANGUAGE_H |
| 33 | |
| 34 | #include "core/doc_data.h" |
| 35 | #include "core/io/resource.h" |
| 36 | #include "core/object/script_instance.h" |
| 37 | #include "core/templates/pair.h" |
| 38 | #include "core/templates/rb_map.h" |
| 39 | #include "core/templates/safe_refcount.h" |
| 40 | #include "core/variant/typed_array.h" |
| 41 | |
| 42 | class ScriptLanguage; |
| 43 | template <typename T> |
| 44 | class TypedArray; |
| 45 | |
| 46 | typedef void (*ScriptEditRequestFunction)(const String &p_path); |
| 47 | |
| 48 | class ScriptServer { |
| 49 | enum { |
| 50 | MAX_LANGUAGES = 16 |
| 51 | }; |
| 52 | |
| 53 | static ScriptLanguage *_languages[MAX_LANGUAGES]; |
| 54 | static int _language_count; |
| 55 | static bool scripting_enabled; |
| 56 | static bool reload_scripts_on_save; |
| 57 | static SafeFlag languages_finished; // Used until GH-76581 is fixed properly. |
| 58 | |
| 59 | struct GlobalScriptClass { |
| 60 | StringName language; |
| 61 | String path; |
| 62 | StringName base; |
| 63 | }; |
| 64 | |
| 65 | static HashMap<StringName, GlobalScriptClass> global_classes; |
| 66 | static HashMap<StringName, Vector<StringName>> inheriters_cache; |
| 67 | static bool inheriters_cache_dirty; |
| 68 | |
| 69 | public: |
| 70 | static ScriptEditRequestFunction edit_request_func; |
| 71 | |
| 72 | static void set_scripting_enabled(bool p_enabled); |
| 73 | static bool is_scripting_enabled(); |
| 74 | _FORCE_INLINE_ static int get_language_count() { return _language_count; } |
| 75 | static ScriptLanguage *get_language(int p_idx); |
| 76 | static Error register_language(ScriptLanguage *p_language); |
| 77 | static Error unregister_language(const ScriptLanguage *p_language); |
| 78 | |
| 79 | static void set_reload_scripts_on_save(bool p_enable); |
| 80 | static bool is_reload_scripts_on_save_enabled(); |
| 81 | |
| 82 | static void thread_enter(); |
| 83 | static void thread_exit(); |
| 84 | |
| 85 | static void global_classes_clear(); |
| 86 | static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path); |
| 87 | static void remove_global_class(const StringName &p_class); |
| 88 | static void remove_global_class_by_path(const String &p_path); |
| 89 | static bool is_global_class(const StringName &p_class); |
| 90 | static StringName get_global_class_language(const StringName &p_class); |
| 91 | static String get_global_class_path(const String &p_class); |
| 92 | static StringName get_global_class_base(const String &p_class); |
| 93 | static StringName get_global_class_native_base(const String &p_class); |
| 94 | static void get_global_class_list(List<StringName> *r_global_classes); |
| 95 | static void get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes); |
| 96 | static void save_global_classes(); |
| 97 | static String get_global_class_cache_file_path(); |
| 98 | |
| 99 | static void init_languages(); |
| 100 | static void finish_languages(); |
| 101 | |
| 102 | static bool are_languages_finished() { return languages_finished.is_set(); } |
| 103 | }; |
| 104 | |
| 105 | class PlaceHolderScriptInstance; |
| 106 | |
| 107 | class Script : public Resource { |
| 108 | GDCLASS(Script, Resource); |
| 109 | OBJ_SAVE_TYPE(Script); |
| 110 | |
| 111 | protected: |
| 112 | virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better |
| 113 | void _notification(int p_what); |
| 114 | static void _bind_methods(); |
| 115 | |
| 116 | friend class PlaceHolderScriptInstance; |
| 117 | virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {} |
| 118 | |
| 119 | Variant _get_property_default_value(const StringName &p_property); |
| 120 | TypedArray<Dictionary> _get_script_property_list(); |
| 121 | TypedArray<Dictionary> _get_script_method_list(); |
| 122 | TypedArray<Dictionary> _get_script_signal_list(); |
| 123 | Dictionary _get_script_constant_map(); |
| 124 | |
| 125 | public: |
| 126 | virtual bool can_instantiate() const = 0; |
| 127 | |
| 128 | virtual Ref<Script> get_base_script() const = 0; //for script inheritance |
| 129 | virtual StringName get_global_name() const = 0; |
| 130 | virtual bool inherits_script(const Ref<Script> &p_script) const = 0; |
| 131 | |
| 132 | virtual StringName get_instance_base_type() const = 0; // this may not work in all scripts, will return empty if so |
| 133 | virtual ScriptInstance *instance_create(Object *p_this) = 0; |
| 134 | virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) { return nullptr; } |
| 135 | virtual bool instance_has(const Object *p_this) const = 0; |
| 136 | |
| 137 | virtual bool has_source_code() const = 0; |
| 138 | virtual String get_source_code() const = 0; |
| 139 | virtual void set_source_code(const String &p_code) = 0; |
| 140 | virtual Error reload(bool p_keep_state = false) = 0; |
| 141 | |
| 142 | #ifdef TOOLS_ENABLED |
| 143 | virtual Vector<DocData::ClassDoc> get_documentation() const = 0; |
| 144 | virtual String get_class_icon_path() const = 0; |
| 145 | virtual PropertyInfo get_class_category() const; |
| 146 | #endif // TOOLS_ENABLED |
| 147 | |
| 148 | virtual bool has_method(const StringName &p_method) const = 0; |
| 149 | virtual MethodInfo get_method_info(const StringName &p_method) const = 0; |
| 150 | |
| 151 | virtual bool is_tool() const = 0; |
| 152 | virtual bool is_valid() const = 0; |
| 153 | |
| 154 | virtual ScriptLanguage *get_language() const = 0; |
| 155 | |
| 156 | virtual bool has_script_signal(const StringName &p_signal) const = 0; |
| 157 | virtual void get_script_signal_list(List<MethodInfo> *r_signals) const = 0; |
| 158 | |
| 159 | virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const = 0; |
| 160 | |
| 161 | virtual void update_exports() {} //editor tool |
| 162 | virtual void get_script_method_list(List<MethodInfo> *p_list) const = 0; |
| 163 | virtual void get_script_property_list(List<PropertyInfo> *p_list) const = 0; |
| 164 | |
| 165 | virtual int get_member_line(const StringName &p_member) const { return -1; } |
| 166 | |
| 167 | virtual void get_constants(HashMap<StringName, Variant> *p_constants) {} |
| 168 | virtual void get_members(HashSet<StringName> *p_constants) {} |
| 169 | |
| 170 | virtual bool is_placeholder_fallback_enabled() const { return false; } |
| 171 | |
| 172 | virtual const Variant get_rpc_config() const = 0; |
| 173 | |
| 174 | Script() {} |
| 175 | }; |
| 176 | |
| 177 | class ScriptCodeCompletionCache { |
| 178 | static ScriptCodeCompletionCache *singleton; |
| 179 | |
| 180 | public: |
| 181 | static ScriptCodeCompletionCache *get_singleton() { return singleton; } |
| 182 | |
| 183 | ScriptCodeCompletionCache(); |
| 184 | |
| 185 | virtual ~ScriptCodeCompletionCache() {} |
| 186 | }; |
| 187 | |
| 188 | class ScriptLanguage : public Object { |
| 189 | GDCLASS(ScriptLanguage, Object) |
| 190 | public: |
| 191 | virtual String get_name() const = 0; |
| 192 | |
| 193 | /* LANGUAGE FUNCTIONS */ |
| 194 | virtual void init() = 0; |
| 195 | virtual String get_type() const = 0; |
| 196 | virtual String get_extension() const = 0; |
| 197 | virtual void finish() = 0; |
| 198 | |
| 199 | /* EDITOR FUNCTIONS */ |
| 200 | struct Warning { |
| 201 | int start_line = -1, end_line = -1; |
| 202 | int leftmost_column = -1, rightmost_column = -1; |
| 203 | int code; |
| 204 | String string_code; |
| 205 | String message; |
| 206 | }; |
| 207 | |
| 208 | struct ScriptError { |
| 209 | String path; |
| 210 | int line = -1; |
| 211 | int column = -1; |
| 212 | String message; |
| 213 | }; |
| 214 | |
| 215 | enum TemplateLocation { |
| 216 | TEMPLATE_BUILT_IN, |
| 217 | TEMPLATE_EDITOR, |
| 218 | TEMPLATE_PROJECT |
| 219 | }; |
| 220 | |
| 221 | struct ScriptTemplate { |
| 222 | String inherit = "Object" ; |
| 223 | String name; |
| 224 | String description; |
| 225 | String content; |
| 226 | int id = 0; |
| 227 | TemplateLocation origin = TemplateLocation::TEMPLATE_BUILT_IN; |
| 228 | |
| 229 | String get_hash() const { |
| 230 | return itos(origin) + inherit + name; |
| 231 | } |
| 232 | }; |
| 233 | |
| 234 | void get_core_type_words(List<String> *p_core_type_words) const; |
| 235 | virtual void get_reserved_words(List<String> *p_words) const = 0; |
| 236 | virtual bool is_control_flow_keyword(String p_string) const = 0; |
| 237 | virtual void (List<String> *p_delimiters) const = 0; |
| 238 | virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; |
| 239 | virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } |
| 240 | virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } |
| 241 | virtual bool is_using_templates() { return false; } |
| 242 | virtual bool validate(const String &p_script, const String &p_path = "" , List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0; |
| 243 | virtual String validate_path(const String &p_path) const { return "" ; } |
| 244 | virtual Script *create_script() const = 0; |
| 245 | virtual bool has_named_classes() const = 0; |
| 246 | virtual bool supports_builtin_mode() const = 0; |
| 247 | virtual bool supports_documentation() const { return false; } |
| 248 | virtual bool can_inherit_from_file() const { return false; } |
| 249 | virtual int find_function(const String &p_function, const String &p_code) const = 0; |
| 250 | virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const = 0; |
| 251 | virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } |
| 252 | virtual bool overrides_external_editor() { return false; } |
| 253 | |
| 254 | // Keep enums in sync with: |
| 255 | // scene/gui/code_edit.h - CodeEdit::CodeCompletionKind |
| 256 | enum CodeCompletionKind { |
| 257 | CODE_COMPLETION_KIND_CLASS, |
| 258 | CODE_COMPLETION_KIND_FUNCTION, |
| 259 | CODE_COMPLETION_KIND_SIGNAL, |
| 260 | CODE_COMPLETION_KIND_VARIABLE, |
| 261 | CODE_COMPLETION_KIND_MEMBER, |
| 262 | CODE_COMPLETION_KIND_ENUM, |
| 263 | CODE_COMPLETION_KIND_CONSTANT, |
| 264 | CODE_COMPLETION_KIND_NODE_PATH, |
| 265 | CODE_COMPLETION_KIND_FILE_PATH, |
| 266 | CODE_COMPLETION_KIND_PLAIN_TEXT, |
| 267 | CODE_COMPLETION_KIND_MAX |
| 268 | }; |
| 269 | |
| 270 | // scene/gui/code_edit.h - CodeEdit::CodeCompletionLocation |
| 271 | enum CodeCompletionLocation { |
| 272 | LOCATION_LOCAL = 0, |
| 273 | LOCATION_PARENT_MASK = 1 << 8, |
| 274 | LOCATION_OTHER_USER_CODE = 1 << 9, |
| 275 | LOCATION_OTHER = 1 << 10, |
| 276 | }; |
| 277 | |
| 278 | struct CodeCompletionOption { |
| 279 | CodeCompletionKind kind = CODE_COMPLETION_KIND_PLAIN_TEXT; |
| 280 | String display; |
| 281 | String insert_text; |
| 282 | Color font_color; |
| 283 | Ref<Resource> icon; |
| 284 | Variant default_value; |
| 285 | Vector<Pair<int, int>> matches; |
| 286 | Vector<Pair<int, int>> last_matches = { { -1, -1 } }; // This value correspond to an impossible match |
| 287 | int location = LOCATION_OTHER; |
| 288 | String theme_color_name; |
| 289 | |
| 290 | CodeCompletionOption() {} |
| 291 | |
| 292 | CodeCompletionOption(const String &p_text, CodeCompletionKind p_kind, int p_location = LOCATION_OTHER, const String &p_theme_color_name = "" ) { |
| 293 | display = p_text; |
| 294 | insert_text = p_text; |
| 295 | kind = p_kind; |
| 296 | location = p_location; |
| 297 | theme_color_name = p_theme_color_name; |
| 298 | } |
| 299 | |
| 300 | TypedArray<int> get_option_characteristics(const String &p_base); |
| 301 | void clear_characteristics(); |
| 302 | TypedArray<int> get_option_cached_characteristics() const; |
| 303 | |
| 304 | private: |
| 305 | TypedArray<int> charac; |
| 306 | }; |
| 307 | |
| 308 | virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } |
| 309 | |
| 310 | enum LookupResultType { |
| 311 | LOOKUP_RESULT_SCRIPT_LOCATION, |
| 312 | LOOKUP_RESULT_CLASS, |
| 313 | LOOKUP_RESULT_CLASS_CONSTANT, |
| 314 | LOOKUP_RESULT_CLASS_PROPERTY, |
| 315 | LOOKUP_RESULT_CLASS_METHOD, |
| 316 | LOOKUP_RESULT_CLASS_SIGNAL, |
| 317 | LOOKUP_RESULT_CLASS_ENUM, |
| 318 | LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE, |
| 319 | LOOKUP_RESULT_CLASS_ANNOTATION, |
| 320 | LOOKUP_RESULT_MAX |
| 321 | }; |
| 322 | |
| 323 | struct LookupResult { |
| 324 | LookupResultType type; |
| 325 | Ref<Script> script; |
| 326 | String class_name; |
| 327 | String class_member; |
| 328 | String class_path; |
| 329 | int location; |
| 330 | }; |
| 331 | |
| 332 | virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { return ERR_UNAVAILABLE; } |
| 333 | |
| 334 | virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const = 0; |
| 335 | virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) = 0; |
| 336 | virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) {} |
| 337 | virtual void remove_named_global_constant(const StringName &p_name) {} |
| 338 | |
| 339 | /* MULTITHREAD FUNCTIONS */ |
| 340 | |
| 341 | //some VMs need to be notified of thread creation/exiting to allocate a stack |
| 342 | virtual void thread_enter() {} |
| 343 | virtual void thread_exit() {} |
| 344 | |
| 345 | /* DEBUGGER FUNCTIONS */ |
| 346 | struct StackInfo { |
| 347 | String file; |
| 348 | String func; |
| 349 | int line; |
| 350 | }; |
| 351 | |
| 352 | virtual String debug_get_error() const = 0; |
| 353 | virtual int debug_get_stack_level_count() const = 0; |
| 354 | virtual int debug_get_stack_level_line(int p_level) const = 0; |
| 355 | virtual String debug_get_stack_level_function(int p_level) const = 0; |
| 356 | virtual String debug_get_stack_level_source(int p_level) const = 0; |
| 357 | virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0; |
| 358 | virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0; |
| 359 | virtual ScriptInstance *debug_get_stack_level_instance(int p_level) { return nullptr; } |
| 360 | virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0; |
| 361 | virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0; |
| 362 | |
| 363 | virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); } |
| 364 | |
| 365 | virtual void reload_all_scripts() = 0; |
| 366 | virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) = 0; |
| 367 | /* LOADER FUNCTIONS */ |
| 368 | |
| 369 | virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; |
| 370 | virtual void get_public_functions(List<MethodInfo> *p_functions) const = 0; |
| 371 | virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const = 0; |
| 372 | virtual void get_public_annotations(List<MethodInfo> *p_annotations) const = 0; |
| 373 | |
| 374 | struct ProfilingInfo { |
| 375 | StringName signature; |
| 376 | uint64_t call_count; |
| 377 | uint64_t total_time; |
| 378 | uint64_t self_time; |
| 379 | }; |
| 380 | |
| 381 | virtual void profiling_start() = 0; |
| 382 | virtual void profiling_stop() = 0; |
| 383 | |
| 384 | virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) = 0; |
| 385 | virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) = 0; |
| 386 | |
| 387 | virtual void frame(); |
| 388 | |
| 389 | virtual bool handles_global_class_type(const String &p_type) const { return false; } |
| 390 | virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const { return String(); } |
| 391 | |
| 392 | virtual ~ScriptLanguage() {} |
| 393 | }; |
| 394 | |
| 395 | extern uint8_t script_encryption_key[32]; |
| 396 | |
| 397 | class PlaceHolderScriptInstance : public ScriptInstance { |
| 398 | Object *owner = nullptr; |
| 399 | List<PropertyInfo> properties; |
| 400 | HashMap<StringName, Variant> values; |
| 401 | HashMap<StringName, Variant> constants; |
| 402 | ScriptLanguage *language = nullptr; |
| 403 | Ref<Script> script; |
| 404 | |
| 405 | public: |
| 406 | virtual bool set(const StringName &p_name, const Variant &p_value) override; |
| 407 | virtual bool get(const StringName &p_name, Variant &r_ret) const override; |
| 408 | virtual void get_property_list(List<PropertyInfo> *p_properties) const override; |
| 409 | virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override; |
| 410 | virtual void validate_property(PropertyInfo &p_property) const override {} |
| 411 | |
| 412 | virtual bool property_can_revert(const StringName &p_name) const override { return false; }; |
| 413 | virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; }; |
| 414 | |
| 415 | virtual void get_method_list(List<MethodInfo> *p_list) const override; |
| 416 | virtual bool has_method(const StringName &p_method) const override; |
| 417 | |
| 418 | virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { |
| 419 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
| 420 | return Variant(); |
| 421 | } |
| 422 | virtual void notification(int p_notification, bool p_reversed = false) override {} |
| 423 | |
| 424 | virtual Ref<Script> get_script() const override { return script; } |
| 425 | |
| 426 | virtual ScriptLanguage *get_language() override { return language; } |
| 427 | |
| 428 | Object *get_owner() override { return owner; } |
| 429 | |
| 430 | void update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values); //likely changed in editor |
| 431 | |
| 432 | virtual bool is_placeholder() const override { return true; } |
| 433 | |
| 434 | virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr) override; |
| 435 | virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr) override; |
| 436 | |
| 437 | virtual const Variant get_rpc_config() const override { return Variant(); } |
| 438 | |
| 439 | PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); |
| 440 | ~PlaceHolderScriptInstance(); |
| 441 | }; |
| 442 | |
| 443 | #endif // SCRIPT_LANGUAGE_H |
| 444 | |