feat: updated godot version

This commit is contained in:
Sara Gerretsen 2026-04-04 19:38:56 +02:00
parent 0c508b0831
commit 42b028dbb5
4694 changed files with 236470 additions and 401376 deletions

View file

@ -52,6 +52,7 @@ for name, path in env.module_list.items():
# Generate header to be included in `tests/test_main.cpp` to run module-specific tests.
if env["tests"]:
env.Append(CPPDEFINES=["TESTS_ENABLED"])
env.CommandNoCache("modules_tests.gen.h", test_headers, env.Run(modules_builders.modules_tests_builder))
# libmodules.a with only register_module_types.

View file

@ -34,7 +34,7 @@
#include "core/string/print_string.h"
#define BCDEC_IMPLEMENTATION
#include <thirdparty/misc/bcdec.h>
#include "thirdparty/misc/bcdec.h"
inline void bcdec_bc6h_half_s(const void *compressedBlock, void *decompressedBlock, int destinationPitch) {
bcdec_bc6h_half(compressedBlock, decompressedBlock, destinationPitch, true);

View file

@ -30,28 +30,16 @@
#include "image_compress_betsy.h"
#include "core/config/project_settings.h"
#include "betsy_bc1.h"
#include "alpha_stitch.glsl.gen.h"
#include "bc1.glsl.gen.h"
#include "bc4.glsl.gen.h"
#include "bc6h.glsl.gen.h"
#include "betsy_bc1.h"
#include "rgb_to_rgba.glsl.gen.h"
#include "core/config/project_settings.h"
#include "core/object/callable_mp.h"
#include "core/os/os.h"
#include "servers/display/display_server.h"
#include "servers/rendering/rendering_context_driver.h"
#include "servers/rendering/rendering_device.h"
#include "servers/rendering/rendering_device_binds.h"
#include "servers/rendering/rendering_server.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
#endif
#if defined(METAL_ENABLED)
#include "drivers/metal/rendering_context_driver_metal.h"
#endif
static Mutex betsy_mutex;
static BetsyCompressor *betsy = nullptr;

View file

@ -32,11 +32,18 @@
#include "core/io/image.h"
#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
class RDShaderFile;
class RenderingDevice;
class RenderingContextDriver;
#include "servers/rendering/rendering_device_binds.h"
#include "servers/rendering/rendering_server_default.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
#endif
#if defined(METAL_ENABLED)
#include "drivers/metal/rendering_context_driver_metal.h"
#endif
enum BetsyFormat {
BETSY_FORMAT_BC1,

View file

@ -6,25 +6,19 @@ Import("env_modules")
env_camera = env_modules.Clone()
if env["platform"] in ["windows", "macos", "linuxbsd", "android", "ios", "visionos"]:
if env["platform"] in ["windows", "macos", "linuxbsd", "android"]:
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
if env["platform"] == "windows":
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
elif env["platform"] == "macos":
env_camera.add_source_files(env.modules_sources, "camera_apple.mm")
env_camera.add_source_files(env.modules_sources, "camera_macos.mm")
elif env["platform"] == "android":
env_camera.add_source_files(env.modules_sources, "camera_android.cpp")
env.Append(LIBS=["camera2ndk", "mediandk"])
elif env["platform"] in ["ios", "visionos"]:
ext_module_source = ["camera_apple.mm"]
ext_camera_lib = env_camera.add_library("#bin/libgodot_camera", ext_module_source)
env.Append(LIBS_EXTERNAL=[ext_camera_lib])
env.Append(MODULES_EXTERNAL=["_camera"])
elif env["platform"] == "linuxbsd":
env_camera.add_source_files(env.modules_sources, "camera_linux.cpp")
env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp")

View file

@ -31,7 +31,6 @@
#include "camera_android.h"
#include "core/os/os.h"
#include "platform/android/display_server_android.h"
#include "platform/android/java_godot_io_wrapper.h"
#include "platform/android/os_android.h"
@ -73,7 +72,7 @@ public:
#ifndef IF_EQUAL_RETURN
#define MAKE_FORMAT_CONST(suffix) AIMAGE_FORMAT_##suffix
#define IF_EQUAL_RETURN(param, val) \
#define IF_EQUAL_RETURN(param, val) \
if (MAKE_FORMAT_CONST(val) == param) \
return #val
#endif
@ -90,7 +89,8 @@ String GetFormatName(const int32_t &format) {
// CameraFeedAndroid - Subclass for our camera feed on Android
CameraFeedAndroid::CameraFeedAndroid(ACameraManager *manager, ACameraMetadata *metadata, const char *id,
CameraFeed::FeedPosition position, int32_t orientation) {
CameraFeed::FeedPosition position, int32_t orientation) :
CameraFeed() {
this->manager = manager;
this->metadata = metadata;
this->orientation = orientation;

View file

@ -40,7 +40,6 @@
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraMetadataTags.h>
#include <media/NdkImageReader.h>
#include <optional>
enum class CameraFacing {

View file

@ -350,7 +350,8 @@ bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
return true;
}
CameraFeedLinux::CameraFeedLinux(const String &p_device_name) {
CameraFeedLinux::CameraFeedLinux(const String &p_device_name) :
CameraFeed() {
device_name = p_device_name;
_query_device(device_name);
}

View file

@ -32,7 +32,6 @@
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
#include "servers/camera/camera_server.h"
class CameraLinux : public CameraServer {

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* camera_apple.h */
/* camera_macos.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -35,11 +35,11 @@
#include "servers/camera/camera_server.h"
class CameraApple : public CameraServer {
GDSOFTCLASS(CameraApple, CameraServer);
class CameraMacOS : public CameraServer {
GDSOFTCLASS(CameraMacOS, CameraServer);
public:
CameraApple() = default;
CameraMacOS() = default;
void update_feeds();
void set_monitoring_feeds(bool p_monitoring_feeds) override;

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* camera_apple.mm */
/* camera_macos.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -31,14 +31,11 @@
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
#import "camera_apple.h"
#import "camera_macos.h"
#include "servers/camera/camera_feed.h"
#import <AVFoundation/AVFoundation.h>
#ifdef IOS_ENABLED
#import <UIKit/UIKit.h>
#endif
//////////////////////////////////////////////////////////////////////////
// MyCaptureSession - This is a little helper class so we can capture our frames
@ -66,30 +63,23 @@
width[1] = 0;
height[1] = 0;
#ifdef IOS_ENABLED
if ([p_device lockForConfiguration:&error]) {
if ([p_device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
[p_device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if ([p_device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
[p_device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
if ([p_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
[p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
}
#ifdef APPLE_EMBEDDED_ENABLED
[p_device lockForConfiguration:&error];
[p_device unlockForConfiguration];
}
#endif // IOS_ENABLED
[p_device setFocusMode:AVCaptureFocusModeLocked];
[p_device setExposureMode:AVCaptureExposureModeLocked];
[p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
[p_device unlockForConfiguration];
#endif // APPLE_EMBEDDED_ENABLED
[self beginConfiguration];
#ifdef IOS_ENABLED
#ifdef APPLE_EMBEDDED_ENABLED
self.sessionPreset = AVCaptureSessionPreset1280x720;
#endif // IOS_ENABLED
input = [[AVCaptureDeviceInput alloc] initWithDevice:p_device error:&error];
#endif // APPLE_EMBEDDED_ENABLED
input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
if (!input) {
print_line("Couldn't get input device for camera");
[self commitConfiguration];
@ -229,28 +219,6 @@
// set our texture...
feed->set_ycbcr_images(img[0], img[1]);
#ifdef IOS_ENABLED
UIInterfaceOrientation orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
Transform2D display_transform;
switch (orientation) {
case UIInterfaceOrientationPortrait: {
display_transform = Transform2D(0.0, -1.0, -1.0, 0.0, 1.0, 1.0);
} break;
case UIInterfaceOrientationLandscapeRight: {
display_transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
} break;
case UIInterfaceOrientationLandscapeLeft: {
display_transform = Transform2D(-1.0, 0.0, 0.0, 1.0, 1.0, 0.0);
} break;
default: {
display_transform = Transform2D(0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
} break;
}
feed->set_transform(display_transform);
#endif // IOS_ENABLED
// and unlock
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
}
@ -258,10 +226,10 @@
@end
//////////////////////////////////////////////////////////////////////////
// CameraFeedApple - Subclass for camera feeds in macOS
// CameraFeedMacOS - Subclass for camera feeds in macOS
class CameraFeedApple : public CameraFeed {
GDSOFTCLASS(CameraFeedApple, CameraFeed);
class CameraFeedMacOS : public CameraFeed {
GDSOFTCLASS(CameraFeedMacOS, CameraFeed);
private:
AVCaptureDevice *device;
@ -270,8 +238,8 @@ private:
public:
AVCaptureDevice *get_device() const;
CameraFeedApple();
~CameraFeedApple();
CameraFeedMacOS();
~CameraFeedMacOS();
void set_device(AVCaptureDevice *p_device);
@ -279,23 +247,22 @@ public:
void deactivate_feed() override;
};
AVCaptureDevice *CameraFeedApple::get_device() const {
AVCaptureDevice *CameraFeedMacOS::get_device() const {
return device;
}
CameraFeedApple::CameraFeedApple() {
CameraFeedMacOS::CameraFeedMacOS() {
device = nullptr;
capture_session = nullptr;
transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */
}
CameraFeedApple::~CameraFeedApple() {
CameraFeedMacOS::~CameraFeedMacOS() {
if (is_active()) {
deactivate_feed();
}
}
void CameraFeedApple::set_device(AVCaptureDevice *p_device) {
void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) {
device = p_device;
// get some info
@ -309,14 +276,14 @@ void CameraFeedApple::set_device(AVCaptureDevice *p_device) {
};
}
bool CameraFeedApple::activate_feed() {
bool CameraFeedMacOS::activate_feed() {
if (capture_session) {
// Already recording.
return true;
}
// Start camera capture, check permission.
if (@available(macOS 10.14, iOS 14.0, visionOS 1.0, *)) {
if (@available(macOS 10.14, *)) {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusAuthorized) {
capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
@ -344,7 +311,7 @@ bool CameraFeedApple::activate_feed() {
}
}
void CameraFeedApple::deactivate_feed() {
void CameraFeedMacOS::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
@ -357,7 +324,7 @@ void CameraFeedApple::deactivate_feed() {
// when devices are connected/disconnected
@interface MyDeviceNotifications : NSObject {
CameraApple *camera_server;
CameraMacOS *camera_server;
}
@end
@ -368,7 +335,7 @@ void CameraFeedApple::deactivate_feed() {
camera_server->update_feeds();
}
- (id)initForServer:(CameraApple *)p_server {
- (id)initForServer:(CameraMacOS *)p_server {
if (self = [super init]) {
camera_server = p_server;
@ -389,31 +356,13 @@ void CameraFeedApple::deactivate_feed() {
MyDeviceNotifications *device_notifications = nil;
//////////////////////////////////////////////////////////////////////////
// CameraApple - Subclass for our camera server on macOS
// CameraMacOS - Subclass for our camera server on macOS
void CameraApple::update_feeds() {
void CameraMacOS::update_feeds() {
NSArray<AVCaptureDevice *> *devices = nullptr;
#ifdef APPLE_EMBEDDED_ENABLED
{
NSMutableArray *deviceTypes = [NSMutableArray array];
if (@available(iOS 14.0, visionOS 2.1, *)) {
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
}
#ifdef IOS_ENABLED
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualWideCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTripleCamera];
#endif // IOS_ENABLED
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
devices = session.devices;
}
#else // APPLE_EMBEDDED_ENABLED
#if defined(__x86_64__)
if (@available(macOS 10.15, *)) {
#endif // __x86_64__
#endif
AVCaptureDeviceDiscoverySession *session;
if (@available(macOS 14.0, *)) {
session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeContinuityCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
@ -425,12 +374,11 @@ void CameraApple::update_feeds() {
} else {
devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
}
#endif // __x86_64__
#endif // APPLE_EMBEDDED_ENABLED
#endif
// Deactivate feeds that are gone before removing them.
for (int i = feeds.size() - 1; i >= 0; i--) {
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
if (feed.is_null()) {
continue;
}
@ -446,7 +394,7 @@ void CameraApple::update_feeds() {
for (AVCaptureDevice *device in devices) {
bool found = false;
for (int i = 0; i < feeds.size() && !found; i++) {
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
if (feed.is_null()) {
continue;
}
@ -456,7 +404,7 @@ void CameraApple::update_feeds() {
};
if (!found) {
Ref<CameraFeedApple> newfeed;
Ref<CameraFeedMacOS> newfeed;
newfeed.instantiate();
newfeed->set_device(device);
@ -466,7 +414,7 @@ void CameraApple::update_feeds() {
emit_signal(SNAME(CameraServer::feeds_updated_signal_name));
}
void CameraApple::set_monitoring_feeds(bool p_monitoring_feeds) {
void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) {
if (p_monitoring_feeds == monitoring_feeds) {
return;
}
@ -483,14 +431,3 @@ void CameraApple::set_monitoring_feeds(bool p_monitoring_feeds) {
device_notifications = nil;
}
}
#ifdef APPLE_EMBEDDED_ENABLED
void register_camera_external_module() {
CameraServer::make_default<CameraApple>();
}
void unregister_camera_external_module() {
}
#endif // APPLE_EMBEDDED_ENABLED

View file

@ -30,8 +30,6 @@
#include "camera_win.h"
#include "servers/camera/camera_feed.h"
///@TODO sorry guys, I got about 80% through implementing this using DirectShow only
// to find out Microsoft deprecated half the API and its replacement is as confusing
// as they could make it. Joey suggested looking into libuvc which offers a more direct

View file

@ -30,6 +30,7 @@
#pragma once
#include "servers/camera/camera_feed.h"
#include "servers/camera/camera_server.h"
class CameraWindows : public CameraServer {

View file

@ -3,14 +3,7 @@ def can_build(env, platform):
if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"):
return False
return (
platform == "macos"
or platform == "windows"
or platform == "linuxbsd"
or platform == "android"
or platform == "ios"
or platform == "visionos"
)
return platform == "macos" or platform == "windows" or platform == "linuxbsd" or platform == "android"
def configure(env):

View file

@ -37,7 +37,7 @@
#include "camera_win.h"
#endif
#if defined(MACOS_ENABLED)
#include "camera_apple.h"
#include "camera_macos.h"
#endif
#if defined(ANDROID_ENABLED)
#include "camera_android.h"
@ -55,7 +55,7 @@ void initialize_camera_module(ModuleInitializationLevel p_level) {
CameraServer::make_default<CameraWindows>();
#endif
#if defined(MACOS_ENABLED)
CameraServer::make_default<CameraApple>();
CameraServer::make_default<CameraMacOS>();
#endif
#if defined(ANDROID_ENABLED)
CameraServer::make_default<CameraAndroid>();

View file

@ -30,27 +30,18 @@
#include "csg_shape.h"
#include "core/config/engine.h"
#include "core/math/geometry_2d.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "scene/main/scene_tree.h"
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_mesh.h"
#include "servers/rendering/rendering_server.h"
#ifdef DEV_ENABLED
#include "core/io/json.h"
#endif // DEV_ENABLED
#include "core/math/geometry_2d.h"
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_mesh.h"
#ifndef NAVIGATION_3D_DISABLED
#include "servers/navigation_3d/navigation_server_3d.h"
#endif // NAVIGATION_3D_DISABLED
#include <manifold/manifold.h>
#include <cfloat> // FLT_EPSILON
#ifndef NAVIGATION_3D_DISABLED
Callable CSGShape3D::_navmesh_source_geometry_parsing_callback;
RID CSGShape3D::_navmesh_source_geometry_parser;
@ -204,26 +195,6 @@ void CSGShape3D::set_collision_priority(real_t p_priority) {
real_t CSGShape3D::get_collision_priority() const {
return collision_priority;
}
void CSGShape3D::set_autosmooth(bool p_smooth) {
autosmooth = p_smooth;
_make_dirty();
notify_property_list_changed();
}
bool CSGShape3D::is_autosmooth() const {
return autosmooth;
}
void CSGShape3D::set_smoothing_angle(const float p_angle) {
smoothing_angle = p_angle;
_make_dirty();
}
float CSGShape3D::get_smoothing_angle() const {
return smoothing_angle;
}
#endif // PHYSICS_3D_DISABLED
bool CSGShape3D::is_root_shape() const {
@ -605,14 +576,10 @@ void CSGShape3D::update_shape() {
CSGBrush *n = _get_brush();
ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush.");
AHashMap<Vector3, Vector3> vec_map;
Vector<int> face_count;
face_count.resize(n->materials.size() + 1);
Vector<Vector3> smooth_faces;
LocalVector<Vector3> smooth_vertex;
smooth_faces.resize(n->faces.size());
smooth_vertex.resize(n->faces.size() * 3);
for (int i = 0; i < face_count.size(); i++) {
face_count.write[i] = 0;
}
@ -622,72 +589,23 @@ void CSGShape3D::update_shape() {
ERR_CONTINUE(mat < -1 || mat >= face_count.size());
int idx = mat == -1 ? face_count.size() - 1 : mat;
Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
if (n->faces[i].smooth) {
Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
for (int j = 0; j < 3; j++) {
Vector3 v = n->faces[i].vertices[j];
Vector3 *vec = vec_map.getptr(v);
if (vec) {
*vec += p.normal;
} else {
vec_map.insert(v, p.normal);
}
}
}
smooth_faces.write[i] = p.normal;
// Not sure if resize populates the LocalVector.
smooth_vertex[i * 3 + 0] = Vector3(p.normal);
smooth_vertex[i * 3 + 1] = Vector3(p.normal);
smooth_vertex[i * 3 + 2] = Vector3(p.normal);
// We could use a AHashMap Vector3, int to store the number of connections of each vertex position and end the loop earlier. But I'm not sure if the performance gains outweigh the cost.
face_count.write[idx]++;
}
if (autosmooth) {
// We could add a `use_groups` property later to only apply autosmooth on smooth faces or respect smoothing groups in some way.
if (smoothing_angle > 0.1) {
float smooth_angle_rad = Math::cos(Math::deg_to_rad(smoothing_angle));
for (int i = 0; i < smooth_faces.size(); i++) {
for (int k = 0; k < 3; k++) {
int curr_vert = i * 3 + k;
Vector3 vert_a = n->faces[i].vertices[k];
for (int j = i + 1; j < smooth_faces.size(); j++) {
// Compare the angles of faces instead of vertices.
if (smooth_faces[i].dot(smooth_faces[j]) > smooth_angle_rad) {
for (int h = 0; h < 3; h++) {
Vector3 vert_b = n->faces[j].vertices[h];
if (vert_a == vert_b) {
int curr_j = j * 3 + h;
smooth_vertex[curr_vert] += smooth_faces[j];
smooth_vertex[curr_j] += smooth_faces[i];
break;
}
}
}
}
smooth_vertex[curr_vert].normalize();
}
}
}
} else {
for (int i = 0; i < smooth_faces.size(); i++) {
bool face_is_smooth = n->faces[i].smooth;
if (face_is_smooth) {
for (int k = 0; k < 3; k++) {
Vector3 vert_a = n->faces[i].vertices[k];
int curr_vert = i * 3 + k;
// Skip the other vertices of the face as they will never occupy the same position.
for (int j = i + 1; j < smooth_faces.size(); j++) {
// Preparing for when and if we replace Vector of bool for Vector of int smoothing groups. for now, face_is_smooth is always true.
if (face_is_smooth == n->faces[j].smooth) {
for (int h = 0; h < 3; h++) {
Vector3 vert_b = n->faces[j].vertices[h];
if (vert_a == vert_b) {
int curr_j = j * 3 + h;
smooth_vertex[curr_vert] += smooth_faces[j];
smooth_vertex[curr_j] += smooth_faces[i];
// Skip the other 2 vertices as only one vertex of each face can connect with one vertex of other face.
break;
}
}
}
}
smooth_vertex[curr_vert].normalize();
}
}
}
}
Vector<ShapeUpdateSurface> surfaces;
surfaces.resize(face_count.size());
@ -729,12 +647,19 @@ void CSGShape3D::update_shape() {
int last = surfaces[idx].last_added;
int face_pos_i = i * 3;
Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
for (int j = 0; j < 3; j++) {
Vector3 v = n->faces[i].vertices[j];
Vector3 normal = smooth_vertex[face_pos_i + j];
Vector3 normal = p.normal;
if (n->faces[i].smooth) {
Vector3 *ptr = vec_map.getptr(v);
if (ptr) {
normal = ptr->normalized();
}
}
if (n->faces[i].invert) {
normal = -normal;
@ -803,8 +728,6 @@ void CSGShape3D::update_shape() {
set_base(root_mesh->get_rid());
update_gizmos();
#ifndef PHYSICS_3D_DISABLED
_update_collision_faces();
#endif // PHYSICS_3D_DISABLED
@ -1030,19 +953,6 @@ void CSGShape3D::_validate_property(PropertyInfo &p_property) const {
if (!Engine::get_singleton()->is_editor_hint()) {
return;
}
if (p_property.name == "smoothing_angle") {
if (!autosmooth || (is_inside_tree() && !is_root_shape())) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
if (p_property.name == "autosmooth") {
if (is_inside_tree() && !is_root_shape()) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
bool is_collision_prefixed = p_property.name.begins_with("collision_");
if ((is_collision_prefixed || p_property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
//hide collision if not root
@ -1127,15 +1037,6 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("bake_static_mesh"), &CSGShape3D::bake_static_mesh);
ClassDB::bind_method(D_METHOD("set_autosmooth", "autosmooth"), &CSGShape3D::set_autosmooth);
ClassDB::bind_method(D_METHOD("is_autosmooth"), &CSGShape3D::is_autosmooth);
ClassDB::bind_method(D_METHOD("set_smoothing_angle", "smoothing_angle"), &CSGShape3D::set_smoothing_angle);
ClassDB::bind_method(D_METHOD("get_smoothing_angle"), &CSGShape3D::get_smoothing_angle);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autosmooth"), "set_autosmooth", "is_autosmooth");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "smoothing_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_smoothing_angle", "get_smoothing_angle");
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
#ifndef DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m", PROPERTY_USAGE_NONE), "set_snap", "get_snap");

View file

@ -39,9 +39,8 @@
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#endif // PHYSICS_3D_DISABLED
#include <thirdparty/misc/mikktspace.h>
#include "thirdparty/misc/mikktspace.h"
class Mesh;
class NavigationMesh;
class NavigationMeshSourceGeometryData3D;
@ -68,9 +67,6 @@ private:
bool last_visible = false;
float snap = 0.001;
bool autosmooth = false;
float smoothing_angle = 50.0;
#ifndef PHYSICS_3D_DISABLED
bool use_collision = false;
uint32_t collision_layer = 1;
@ -162,12 +158,6 @@ public:
void set_collision_priority(real_t p_priority);
real_t get_collision_priority() const;
void set_autosmooth(bool p_smooth);
bool is_autosmooth() const;
void set_smoothing_angle(const float p_angle);
float get_smoothing_angle() const;
#ifndef DISABLE_DEPRECATED
void set_snap(float p_snap);
float get_snap() const;

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGBox3D" inherits="CSGPrimitive3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGBox3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Box shape.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCombiner3D" inherits="CSGShape3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGCombiner3D" inherits="CSGShape3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG node that allows you to combine other CSG modifiers.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCylinder3D" inherits="CSGPrimitive3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGCylinder3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Cylinder shape.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGMesh3D" inherits="CSGPrimitive3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGMesh3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Mesh shape that uses a mesh resource.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPolygon3D" inherits="CSGPrimitive3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGPolygon3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPrimitive3D" inherits="CSGShape3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGPrimitive3D" inherits="CSGShape3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Base class for CSG primitives.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGShape3D" inherits="GeometryInstance3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGShape3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The CSG base class.
</brief_description>
@ -74,10 +74,6 @@
</method>
</methods>
<members>
<member name="autosmooth" type="bool" setter="set_autosmooth" getter="is_autosmooth" default="false">
Enables automatic smoothing. This overrides any smoothing on the CSG node and instead uses [member smoothing_angle] to calculate normals based on the angle between faces.
Children of a [CSGCombiner3D] node will be treated as a single mesh.
</member>
<member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents" default="true">
Calculate tangents for the CSG shape which allows the use of normal and height maps. This is only applied on the root shape, this setting is ignored on any child. Setting this to [code]false[/code] can speed up shape generation slightly.
</member>
@ -95,10 +91,6 @@
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
<member name="smoothing_angle" type="float" setter="set_smoothing_angle" getter="get_smoothing_angle" default="50.0">
When autosmooth is enabled, faces with an angle between them greater than this will be smoothed, while faces with a smaller angle will remain sharp.
Note: An angle lower than 0.1 will cause all smoothing to be disabled, this can be used to increase performance.
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap" deprecated="The CSG library no longer uses snapping.">
This property does nothing.
</member>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGSphere3D" inherits="CSGPrimitive3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGSphere3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Sphere shape.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGTorus3D" inherits="CSGPrimitive3D" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="CSGTorus3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Torus shape.
</brief_description>

View file

@ -30,8 +30,6 @@
#include "csg_gizmos.h"
#include "core/math/geometry_3d.h"
#include "core/object/callable_mp.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
@ -42,7 +40,6 @@
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
#include "scene/main/scene_tree.h"
void CSGShapeEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@ -419,14 +416,17 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<Material> handles_material = get_material("handles");
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
Ref<ArrayMesh> collision_mesh;
collision_mesh.instantiate();
Array collision_array;
collision_array.resize(Mesh::ARRAY_MAX);
collision_array[Mesh::ARRAY_VERTEX] = faces;
collision_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, collision_array);
p_gizmo->add_collision_triangles(collision_mesh->generate_triangle_mesh());
if (cs->is_root_shape()) {
Array csg_meshes = cs->get_meshes();
if (csg_meshes.size() == 2) {
Ref<Mesh> csg_mesh = csg_meshes[1];
if (csg_mesh.is_valid()) {
p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
}
}
}
if (p_gizmo->is_selected()) {
// Draw a translucent representation of the CSG node

View file

@ -32,8 +32,6 @@
#include "csg_shape.h"
#include "core/object/class_db.h"
#ifdef TOOLS_ENABLED
#include "editor/csg_gizmos.h"
#endif

View file

@ -30,10 +30,9 @@
#pragma once
#include "../csg.h"
#include "../csg_shape.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "tests/test_macros.h"
namespace TestCSG {

View file

@ -164,8 +164,6 @@ enum DDSFormat {
DDS_LUMINANCE,
DDS_LUMINANCE_ALPHA,
DDS_LUMINANCE_ALPHA_4,
DDS_RG8,
DDS_R8,
DDS_MAX
};
@ -225,6 +223,4 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 },
{ "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 },
{ "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 },
{ "RG8", false, 1, 2, Image::FORMAT_RG8 },
{ "R8", false, 1, 1, Image::FORMAT_R8 },
};

View file

@ -33,8 +33,6 @@
#include "image_saver_dds.h"
#include "texture_loader_dds.h"
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
#include "scene/resources/texture.h"
static Ref<ResourceFormatDDS> resource_loader_dds;

View file

@ -34,8 +34,8 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/io/image.h"
#include "tests/core/config/test_project_settings.h"
#include "tests/test_macros.h"
#include "tests/test_utils.h"

View file

@ -34,7 +34,6 @@
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/object/class_db.h"
#include "scene/resources/image_texture.h"
DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) {
@ -76,9 +75,7 @@ DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) {
case DXGI_R32_FLOAT: {
return DDS_R32F;
}
case DXGI_R8_UNORM: {
return DDS_R8;
}
case DXGI_R8_UNORM:
case DXGI_A8_UNORM: {
return DDS_LUMINANCE;
}
@ -92,7 +89,7 @@ DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) {
return DDS_R16I;
}
case DXGI_R8G8_UNORM: {
return DDS_RG8;
return DDS_LUMINANCE_ALPHA;
}
case DXGI_R9G9B9E5: {
return DDS_RGB9E5;
@ -651,10 +648,6 @@ static Vector<Ref<Image>> _dds_load_images_from_buffer(Ref<FileAccess> p_f, DDSF
r_dds_format = DDS_BGRX8;
} else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
r_dds_format = DDS_RGBX8;
} else if (format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0 && format_blue_mask == 0) {
r_dds_format = DDS_R8;
} else if (format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0) {
r_dds_format = DDS_RG8;
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ENetConnection" inherits="RefCounted" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="ENetConnection" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A wrapper class for an [url=http://enet.bespin.org/group__host.html]ENetHost[/url].
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A MultiplayerPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ENetPacketPeer" inherits="PacketPeer" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="ENetPacketPeer" inherits="PacketPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A wrapper class for an [url=http://enet.bespin.org/group__peer.html]ENetPeer[/url].
</brief_description>

View file

@ -34,7 +34,6 @@
#include "core/io/compression.h"
#include "core/io/ip.h"
#include "core/object/class_db.h"
#include "core/variant/typed_array.h"
void ENetConnection::broadcast(enet_uint8 p_channel, ENetPacket *p_packet) {

View file

@ -30,8 +30,6 @@
#include "enet_multiplayer_peer.h"
#include "core/object/class_db.h"
void ENetMultiplayerPeer::set_target_peer(int p_peer) {
target_peer = p_peer;
}
@ -479,7 +477,7 @@ void ENetMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host);
ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, ENetConnection::get_class_static(), PROPERTY_USAGE_NONE), "", "get_host");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host");
}
ENetMultiplayerPeer::ENetMultiplayerPeer() {

View file

@ -32,6 +32,7 @@
#include "enet_connection.h"
#include "core/crypto/crypto.h"
#include "scene/main/multiplayer_peer.h"
#include <enet/enet.h>

View file

@ -30,8 +30,6 @@
#include "enet_packet_peer.h"
#include "core/object/class_db.h"
void ENetPacketPeer::peer_disconnect(int p_data) {
ERR_FAIL_NULL(peer);
enet_peer_disconnect(peer, p_data);
@ -184,9 +182,6 @@ int ENetPacketPeer::get_packet_flags() const {
void ENetPacketPeer::_on_disconnect() {
if (peer) {
#ifdef GODOT_ENET
enet_peer_socket_destroy(peer);
#endif
peer->data = nullptr;
}
peer = nullptr;
@ -261,9 +256,6 @@ void ENetPacketPeer::_bind_methods() {
ENetPacketPeer::ENetPacketPeer(ENetPeer *p_peer) {
peer = p_peer;
peer->data = this;
#ifdef GODOT_ENET
enet_peer_socket_bind(peer);
#endif
}
ENetPacketPeer::~ENetPacketPeer() {

View file

@ -35,7 +35,6 @@
#include "enet_packet_peer.h"
#include "core/error/error_macros.h"
#include "core/object/class_db.h"
static bool enet_ok = false;

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorSceneFormatImporterFBX2GLTF" inherits="EditorSceneFormatImporter" api_type="editor" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="EditorSceneFormatImporterFBX2GLTF" inherits="EditorSceneFormatImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Importer for the [code].fbx[/code] scene file format.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorSceneFormatImporterUFBX" inherits="EditorSceneFormatImporter" api_type="editor" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="EditorSceneFormatImporterUFBX" inherits="EditorSceneFormatImporter" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Import FBX files using the ufbx library.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FBXDocument" inherits="GLTFDocument" api_type="core" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="FBXDocument" inherits="GLTFDocument" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Handles FBX documents.
</brief_description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FBXState" inherits="GLTFState" api_type="core" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="FBXState" inherits="GLTFState" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>

View file

@ -30,12 +30,9 @@
#include "editor_scene_importer_fbx2gltf.h"
#include "editor_scene_importer_ufbx.h"
#include "core/config/project_settings.h"
#include "core/io/resource_importer.h"
#include "core/os/os.h"
#include "editor/settings/editor_settings.h"
#include "editor_scene_importer_ufbx.h"
#include "modules/gltf/gltf_document.h"

View file

@ -34,7 +34,6 @@
#include "editor_scene_importer_fbx2gltf.h"
#include "core/config/project_settings.h"
#include "core/io/resource_importer.h"
void EditorSceneFormatImporterUFBX::get_extensions(List<String> *r_extensions) const {
r_extensions->push_back("fbx");

View file

@ -30,14 +30,12 @@
#include "fbx_document.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
#include "core/io/config_file.h"
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/io/image.h"
#include "core/io/resource_loader.h"
#include "core/math/color.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_3d.h"

View file

@ -30,8 +30,6 @@
#include "fbx_state.h"
#include "core/object/class_db.h"
void FBXState::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_allow_geometry_helper_nodes"), &FBXState::get_allow_geometry_helper_nodes);
ClassDB::bind_method(D_METHOD("set_allow_geometry_helper_nodes", "allow"), &FBXState::set_allow_geometry_helper_nodes);

View file

@ -32,7 +32,9 @@
#include "modules/gltf/gltf_defines.h"
#include "modules/gltf/gltf_state.h"
#include "modules/gltf/structures/gltf_skeleton.h"
#include "modules/gltf/structures/gltf_skin.h"
#include "modules/gltf/structures/gltf_texture.h"
#include <ufbx.h>

View file

@ -30,17 +30,13 @@
#include "register_types.h"
#include "../gltf/extensions/gltf_document_extension_convert_importer_mesh.h"
#include "fbx_document.h"
#include "core/config/engine.h"
#include "core/object/class_db.h"
#include "modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_scene_importer_fbx2gltf.h"
#include "editor/editor_scene_importer_ufbx.h"
//
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
@ -59,8 +55,8 @@ static void _editor_init() {
#endif // TOOLS_ENABLED
#define FBX_REGISTER_DOCUMENT_EXTENSION(m_doc_ext_class) \
Ref<m_doc_ext_class> extension_##m_doc_ext_class; \
extension_##m_doc_ext_class.instantiate(); \
Ref<m_doc_ext_class> extension_##m_doc_ext_class; \
extension_##m_doc_ext_class.instantiate(); \
FBXDocument::register_gltf_document_extension(extension_##m_doc_ext_class);
void initialize_fbx_module(ModuleInitializationLevel p_level) {

View file

@ -25,6 +25,5 @@ if env.editor_build:
if env["tests"]:
# TODO: Handle test creation magic without needing to pass macro.
env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"])
env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp")

View file

@ -11,10 +11,7 @@ def get_doc_classes():
return [
"@GDScript",
"GDScript",
"GDScriptLanguageProtocol",
"GDScriptSyntaxHighlighter",
"GDScriptTextDocument",
"GDScriptWorkspace",
]

View file

@ -133,7 +133,7 @@
- A constant from the [enum Variant.Type] enumeration, for example [constant TYPE_INT].
- An [Object]-derived class which exists in [ClassDB], for example [Node].
- A [Script] (you can use any class, including inner one).
Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays and dictionaries). Use the operator instead of this method if you do not need to check the type dynamically.
Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays). Use the operator instead of this method if you do not need to check the type dynamically.
[b]Examples:[/b]
[codeblock]
print(is_instance_of(a, TYPE_INT))
@ -142,7 +142,7 @@
print(is_instance_of(a, MyClass.InnerClass))
[/codeblock]
[b]Note:[/b] If [param value] and/or [param type] are freed objects (see [method @GlobalScope.is_instance_valid]), or [param type] is not one of the above options, this method will raise a runtime error.
See also [method @GlobalScope.typeof], [method Object.is_class], [method Object.get_script], [method Array.is_same_typed] (and other [Array] methods), [method Dictionary.is_same_typed] (and other [Dictionary] methods).
See also [method @GlobalScope.typeof], [method type_exists], [method Array.is_same_typed] (and other [Array] methods).
</description>
</method>
<method name="len">
@ -266,7 +266,7 @@
[/codeblock]
</description>
</method>
<method name="type_exists" deprecated="Use [method ClassDB.class_exists] instead.">
<method name="type_exists">
<return type="bool" />
<param index="0" name="type" type="StringName" />
<description>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GDScript" inherits="Script" api_type="core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="GDScript" inherits="Script" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A script implemented in the GDScript programming language.
</brief_description>

View file

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GDScriptLanguageProtocol" inherits="JSONRPC" api_type="editor" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
GDScript language server.
</brief_description>
<description>
Provides access to certain features that are implemented in the language server.
[b]Note:[/b] This class is not a language server client that can be used to access LSP functionality. It only provides access to a limited set of features that is implemented using the same technical foundation as the language server.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_text_document" deprecated="[GDScriptTextDocument] is deprecated.">
<return type="GDScriptTextDocument" />
<description>
Returns the language server's [GDScriptTextDocument] instance.
</description>
</method>
<method name="get_workspace">
<return type="GDScriptWorkspace" />
<description>
Returns the language server's [GDScriptWorkspace] instance.
</description>
</method>
<method name="initialize" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Dictionary" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="initialized" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Variant" />
<description>
</description>
</method>
<method name="is_initialized" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the language server was initialized by a language server client, [code]false[/code] otherwise.
</description>
</method>
<method name="is_smart_resolve_enabled" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the language server is providing the smart resolve feature, [code]false[/code] otherwise. The feature can be configured through the editor settings.
</description>
</method>
<method name="notify_client" deprecated="Might result in unwanted side effects for connected clients.">
<return type="void" />
<param index="0" name="method" type="String" />
<param index="1" name="params" type="Variant" default="null" />
<param index="2" name="client_id" type="int" default="-1" />
<description>
</description>
</method>
<method name="on_client_connected" deprecated="Might result in unwanted side effects for connected clients.">
<return type="int" enum="Error" />
<description>
</description>
</method>
<method name="on_client_disconnected" deprecated="Might result in unwanted side effects for connected clients.">
<return type="void" />
<param index="0" name="client_id" type="int" />
<description>
</description>
</method>
</methods>
</class>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GDScriptSyntaxHighlighter" inherits="EditorSyntaxHighlighter" api_type="editor" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="GDScriptSyntaxHighlighter" inherits="EditorSyntaxHighlighter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A GDScript syntax highlighter that can be used with [TextEdit] and [CodeEdit] nodes.
</brief_description>

View file

@ -1,139 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GDScriptTextDocument" inherits="RefCounted" api_type="editor" deprecated="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Document related language server functionality.
</brief_description>
<description>
Provides language server functionality related to documents.
</description>
<tutorials>
</tutorials>
<methods>
<method name="codeLens" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="colorPresentation" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="completion" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="declaration" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Variant" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="definition" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="didChange" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Variant" />
<description>
</description>
</method>
<method name="didClose" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Variant" />
<description>
</description>
</method>
<method name="didOpen" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Variant" />
<description>
</description>
</method>
<method name="didSave" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Variant" />
<description>
</description>
</method>
<method name="documentLink" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="documentSymbol" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="foldingRange" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="hover" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Variant" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="nativeSymbol" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Variant" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="prepareRename" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Variant" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="references" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Array" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="rename" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Dictionary" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="resolve" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Dictionary" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="show_native_symbol_in_editor" deprecated="Use [method ScriptEditor.goto_help] instead.">
<return type="void" />
<param index="0" name="symbol_id" type="String" />
<description>
</description>
</method>
<method name="signatureHelp" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="Variant" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="willSaveWaitUntil" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Variant" />
<description>
</description>
</method>
</methods>
</class>

View file

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GDScriptWorkspace" inherits="RefCounted" api_type="editor" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Workspace related language server functionality.
</brief_description>
<description>
Provides language server functionality related to the workspace.
</description>
<tutorials>
</tutorials>
<methods>
<method name="apply_new_signal" deprecated="Might result in unwanted side effects for connected clients.">
<return type="void" />
<param index="0" name="obj" type="Object" />
<param index="1" name="function" type="String" />
<param index="2" name="args" type="PackedStringArray" />
<description>
</description>
</method>
<method name="didDeleteFiles" deprecated="Accessing LSP endpoints directly might lead to unwanted side effects. Connect to the server via TCP, like a regular language server client.">
<return type="void" />
<param index="0" name="params" type="Dictionary" />
<description>
</description>
</method>
<method name="generate_script_api">
<return type="Dictionary" />
<param index="0" name="path" type="String" />
<description>
Returns the interface of the script in a machine-readable format.
</description>
</method>
<method name="get_file_path">
<return type="String" />
<param index="0" name="uri" type="String" />
<description>
Converts a URI to a file path.
</description>
</method>
<method name="get_file_uri" qualifiers="const">
<return type="String" />
<param index="0" name="path" type="String" />
<description>
Converts a file path to a URI.
</description>
</method>
<method name="parse_local_script" deprecated="Might result in unwanted side effects for connected clients.">
<return type="int" enum="Error" />
<param index="0" name="path" type="String" />
<description>
</description>
</method>
<method name="parse_script" deprecated="Might result in unwanted side effects for connected clients.">
<return type="int" enum="Error" />
<param index="0" name="path" type="String" />
<param index="1" name="content" type="String" />
<description>
</description>
</method>
<method name="publish_diagnostics" deprecated="Might result in unwanted side effects for connected clients.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
</description>
</method>
</methods>
</class>

View file

@ -33,7 +33,6 @@
#include "../gdscript.h"
#include "core/config/project_settings.h"
#include "core/doc_data.h"
HashMap<String, String> GDScriptDocGen::singletons;
@ -316,11 +315,6 @@ String GDScriptDocGen::docvalue_from_expression(const GDP::ExpressionNode *p_exp
const GDP::IdentifierNode *id = static_cast<const GDP::IdentifierNode *>(p_expression);
return id->name;
} break;
case GDP::Node::LAMBDA: {
const GDP::LambdaNode *lambda = static_cast<const GDP::LambdaNode *>(p_expression);
const GDP::IdentifierNode *id = lambda->function->identifier;
return id != nullptr ? id->name : "<anonymous lambda>";
} break;
default: {
// Nothing to do.
} break;
@ -439,9 +433,10 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
method_doc.qualifiers += "static";
}
if (func_name == "_init" || func_name == "_static_init") {
if (func_name == "_init") {
method_doc.return_type = "void";
} else if (!m_func->get_datatype().is_variant()) {
} else if (m_func->return_type) {
// `m_func->return_type->get_datatype()` is a metatype.
_doctype_from_gdtype(m_func->get_datatype(), method_doc.return_type, method_doc.return_enum, true);
} else if (!m_func->body->has_return) {
// If no `return` statement, then return type is `void`, not `Variant`.

View file

@ -32,6 +32,8 @@
#include "../gdscript_parser.h"
#include "core/doc_data.h"
class GDScriptDocGen {
using GDP = GDScriptParser;
using GDType = GDP::DataType;

View file

@ -35,8 +35,8 @@
#include "core/config/project_settings.h"
#include "core/core_constants.h"
#include "core/object/class_db.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/text_edit.h"
Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {

View file

@ -30,7 +30,7 @@
#pragma once
#include "editor/script/syntax_highlighters.h"
#include "editor/script/script_editor_plugin.h"
class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
GDCLASS(GDScriptSyntaxHighlighter, EditorSyntaxHighlighter)

View file

@ -450,7 +450,6 @@ GDScriptEditorTranslationParserPlugin::GDScriptEditorTranslationParserPlugin() {
first_arg_patterns.insert("add_radio_check_item");
first_arg_patterns.insert("add_separator");
first_arg_patterns.insert("add_submenu_item");
first_arg_patterns.insert("add_submenu_node_item");
second_arg_patterns.insert("set_tab_title");
second_arg_patterns.insert("add_icon_check_item");

View file

@ -38,10 +38,6 @@
#include "gdscript_tokenizer_buffer.h"
#include "gdscript_warning.h"
#include "core/io/resource_loader.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#ifdef TOOLS_ENABLED
#include "editor/gdscript_docgen.h"
#endif
@ -54,6 +50,7 @@
#include "core/config/project_settings.h"
#include "core/core_constants.h"
#include "core/io/file_access.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
@ -227,17 +224,16 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant());
if (_baseptr->native.ptr()) {
owner = _baseptr->native->instantiate();
RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = Ref<RefCounted>(r);
}
} else {
ref = memnew(RefCounted); // By default, no base means use reference.
owner = ref.ptr();
owner = memnew(RefCounted); //by default, no base means use reference
}
ERR_FAIL_NULL_V_MSG(owner, Variant(), "Can't inherit from a virtual class.");
RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = Ref<RefCounted>(r);
}
GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r_error);
if (!instance) {
if (ref.is_null()) {
@ -648,7 +644,7 @@ void GDScript::_update_exports_down(bool p_base_exports_changed) {
return;
}
HashSet<ObjectID> copy(inheriters_cache); //might get modified
HashSet<ObjectID> copy = inheriters_cache; //might get modified
for (const ObjectID &E : copy) {
Object *id = ObjectDB::get_instance(E);
@ -824,10 +820,10 @@ Error GDScript::reload(bool p_keep_state) {
}
if (err) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().start_line, "Parser Error: " + parser.get_errors().front()->get().message);
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
// TODO: Show all error messages.
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().start_line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
reloading = false;
return ERR_PARSE_ERROR;
}
@ -837,12 +833,12 @@ Error GDScript::reload(bool p_keep_state) {
if (err) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().start_line, "Parser Error: " + parser.get_errors().front()->get().message);
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();
while (e != nullptr) {
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().start_line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
e = e->next();
}
reloading = false;
@ -931,6 +927,10 @@ const Variant GDScript::get_rpc_config() const {
return rpc_config;
}
void GDScript::unload_static() const {
GDScriptCache::remove_script(fully_qualified_name);
}
Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
@ -945,19 +945,9 @@ Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int
top = top->base.ptr();
}
{
Variant ret = Script::callp(p_method, p_args, p_argcount, r_error);
if (r_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD) {
return ret;
}
}
//none found, regular
if (native.is_valid()) {
return native->callp(p_method, p_args, p_argcount, r_error);
}
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
return Script::callp(p_method, p_args, p_argcount, r_error);
}
bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
@ -1105,15 +1095,7 @@ void GDScript::set_path(const String &p_path, bool p_take_over) {
String old_path = path;
path = p_path;
path_valid = true;
String old_base = GDScript::canonicalize_path(old_path);
if (!old_base.is_empty() && fully_qualified_name.begins_with(old_base)) {
fully_qualified_name = GDScript::canonicalize_path(p_path) + fully_qualified_name.substr(old_base.length());
}
if (is_root_script()) {
GDScriptCache::move_script(old_path, p_path);
}
GDScriptCache::move_script(old_path, p_path);
for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
kv.value->set_path(p_path, p_take_over);
@ -2892,3 +2874,170 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
}
return Ref<GDScript>(Object::cast_to<GDScript>(obj));
}
Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String &p_name) {
{
MutexLock lock(mutex);
SelfList<GDScript> *elem = script_list.first();
while (elem) {
GDScript *scr = elem->self();
if (scr->fully_qualified_name == p_name) {
return scr;
}
elem = elem->next();
}
}
Ref<GDScript> scr;
scr.instantiate();
scr->fully_qualified_name = p_name;
return scr;
}
/*************** RESOURCE ***************/
Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Error err;
bool ignoring = p_cache_mode == CACHE_MODE_IGNORE || p_cache_mode == CACHE_MODE_IGNORE_DEEP;
Ref<GDScript> scr = GDScriptCache::get_full_script(p_original_path, err, "", ignoring);
if (err && scr.is_valid()) {
// If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.
ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_original_path, error_names[err]));
}
if (r_error) {
// Don't fail loading because of parsing error.
*r_error = scr.is_valid() ? OK : err;
}
return scr;
}
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
p_extensions->push_back("gdc");
}
bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
return (p_type == "Script" || p_type == "GDScript");
}
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
if (el == "gd" || el == "gdc") {
return "GDScript";
}
return "";
}
void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'.");
String source = file->get_as_utf8_string();
if (source.is_empty()) {
return;
}
GDScriptParser parser;
if (OK != parser.parse(source, p_path, false)) {
return;
}
for (const String &E : parser.get_dependencies()) {
p_dependencies->push_back(E);
}
}
void ResourceFormatLoaderGDScript::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Ref<GDScript> scr = ResourceLoader::load(p_path);
if (scr.is_null()) {
return;
}
const String source = scr->get_source_code();
GDScriptTokenizerText tokenizer;
tokenizer.set_source_code(source);
GDScriptTokenizer::Token current = tokenizer.scan();
while (current.type != GDScriptTokenizer::Token::TK_EOF) {
if (!current.is_identifier()) {
current = tokenizer.scan();
continue;
}
int insert_idx = 0;
for (int i = 0; i < current.start_line - 1; i++) {
insert_idx = source.find("\n", insert_idx) + 1;
}
// Insert the "cursor" character, needed for the lookup to work.
const String source_with_cursor = source.insert(insert_idx + current.start_column, String::chr(0xFFFF));
ScriptLanguage::LookupResult result;
if (scr->get_language()->lookup_code(source_with_cursor, current.get_identifier(), p_path, nullptr, result) == OK) {
if (!result.class_name.is_empty() && ClassDB::class_exists(result.class_name)) {
r_classes->insert(result.class_name);
}
if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY) {
PropertyInfo prop;
if (ClassDB::get_property_info(result.class_name, result.class_member, &prop)) {
if (!prop.class_name.is_empty() && ClassDB::class_exists(prop.class_name)) {
r_classes->insert(prop.class_name);
}
if (!prop.hint_string.is_empty() && ClassDB::class_exists(prop.hint_string)) {
r_classes->insert(prop.hint_string);
}
}
} else if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD) {
MethodInfo met;
if (ClassDB::get_method_info(result.class_name, result.class_member, &met)) {
if (!met.return_val.class_name.is_empty() && ClassDB::class_exists(met.return_val.class_name)) {
r_classes->insert(met.return_val.class_name);
}
if (!met.return_val.hint_string.is_empty() && ClassDB::class_exists(met.return_val.hint_string)) {
r_classes->insert(met.return_val.hint_string);
}
}
}
}
current = tokenizer.scan();
}
}
Error ResourceFormatSaverGDScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<GDScript> sqscr = p_resource;
ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
String source = sqscr->get_source_code();
{
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'.");
file->store_string(source);
if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
return ERR_CANT_CREATE;
}
}
if (ScriptServer::is_reload_scripts_on_save_enabled()) {
GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true);
}
return OK;
}
void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
if (Object::cast_to<GDScript>(*p_resource)) {
p_extensions->push_back("gd");
}
}
bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<GDScript>(*p_resource) != nullptr;
}

View file

@ -35,6 +35,8 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
#include "core/doc_data.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
#include "core/templates/rb_set.h"
@ -337,6 +339,8 @@ public:
virtual const Variant get_rpc_config() const override;
void unload_static() const;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
#endif
@ -596,6 +600,7 @@ public:
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override;
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override;
virtual Script *create_script() const override;
virtual bool supports_builtin_mode() const override;
virtual bool supports_documentation() const override;
virtual bool can_inherit_from_file() const override { return true; }
@ -654,6 +659,29 @@ public:
void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
Ref<GDScript> get_script_by_fully_qualified_name(const String &p_name);
GDScriptLanguage();
~GDScriptLanguage();
};
class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
GDSOFTCLASS(ResourceFormatLoaderGDScript, ResourceFormatLoader);
public:
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
GDSOFTCLASS(ResourceFormatSaverGDScript, ResourceFormatSaver);
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
virtual bool recognize(const Ref<Resource> &p_resource) const override;
};

View file

@ -113,8 +113,6 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_
return type;
}
// WARNING: Use this function **only** to create non-GDScript script metatypes. Otherwise, `check_type_compatibility()`
// may return an incorrect result due to the assumption "A script type cannot be a subtype of a GDScript class".
static GDScriptParser::DataType make_script_meta_type(const Ref<Script> &p_script) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@ -1690,13 +1688,13 @@ void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_anno
reduce_expression(argument);
bool is_argument_value_reduced = false;
Variant value = make_expression_reduced_value(argument, is_argument_value_reduced);
if (!is_argument_value_reduced) {
if (!argument->is_constant) {
push_error(vformat(R"(Argument %d of annotation "%s" isn't a constant expression.)", i + 1, p_annotation->name), argument);
return;
}
Variant value = argument->reduced_value;
if (value.get_type() != argument_info.type) {
#ifdef DEBUG_ENABLED
if (argument_info.type == Variant::INT && value.get_type() == Variant::FLOAT) {
@ -1875,9 +1873,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) {
bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC);
if (p_function->return_type == nullptr) {
p_function->set_datatype(parent_return_type);
} else {
if (p_function->return_type != nullptr) {
// Check return type covariance.
GDScriptParser::DataType return_type = p_function->get_datatype();
if (return_type.is_variant()) {
@ -2055,7 +2051,7 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript
}
}
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite, bool p_is_root) {
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
for (int i = 0; i < p_suite->statements.size(); i++) {
GDScriptParser::Node *stmt = p_suite->statements[i];
// Apply annotations.
@ -2064,7 +2060,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite, bool p_
E->apply(parser, stmt, nullptr); // TODO: Provide `p_class`.
}
resolve_node(stmt, p_is_root);
resolve_node(stmt);
resolve_pending_lambda_bodies();
decide_suite_type(p_suite, stmt);
}
@ -2260,7 +2256,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
GDScriptParser::DataType list_type;
if (p_for->list) {
reduce_expression(p_for->list);
resolve_node(p_for->list, false);
bool is_range = false;
if (p_for->list->type == GDScriptParser::Node::CALL) {
@ -2377,7 +2373,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) {
reduce_expression(p_while->condition);
resolve_node(p_while->condition, false);
resolve_suite(p_while->loop);
p_while->set_datatype(p_while->loop->get_datatype());
}
@ -2426,7 +2423,7 @@ void GDScriptAnalyzer::resolve_match_branch(GDScriptParser::MatchBranchNode *p_m
}
if (p_match_branch->guard_body) {
resolve_suite(p_match_branch->guard_body, false);
resolve_suite(p_match_branch->guard_body);
}
resolve_suite(p_match_branch->block);
@ -2515,22 +2512,19 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
}
void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
const bool has_expected_type = parser->current_function != nullptr;
const GDScriptParser::DataType expected_type = has_expected_type ? parser->current_function->get_datatype() : GDScriptParser::DataType();
GDScriptParser::DataType result;
if (p_return->return_value == nullptr) {
// Return type is `null` by default.
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::NIL;
result.is_constant = true;
} else {
const bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL;
const bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL;
GDScriptParser::DataType expected_type;
bool has_expected_type = parser->current_function != nullptr;
if (has_expected_type) {
expected_type = parser->current_function->get_datatype();
}
if (p_return->return_value != nullptr) {
bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL;
bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL;
if (is_void_function && is_call) {
// Pretend the call is a root expression to allow those that are `void`.
// Pretend the call is a root expression to allow those that are "void".
reduce_call(static_cast<GDScriptParser::CallNode *>(p_return->return_value), false, true);
} else {
reduce_expression(p_return->return_value);
@ -2564,30 +2558,28 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
}
result = p_return->return_value->get_datatype();
}
} else {
// Return type is null by default.
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::NIL;
result.is_constant = true;
}
if (has_expected_type && !expected_type.is_variant() && expected_type.is_hard_type()) {
if (has_expected_type && !expected_type.is_variant()) {
if (result.is_variant() || !result.is_hard_type()) {
p_return->use_conversion = true;
mark_node_unsafe(p_return);
if (!is_type_compatible(expected_type, result, true, p_return)) {
downgrade_node_type_source(p_return);
}
} else if (!is_type_compatible(expected_type, result, true, p_return)) {
if (is_type_compatible(result, expected_type)) {
p_return->use_conversion = true;
mark_node_unsafe(p_return);
} else {
mark_node_unsafe(p_return);
if (!is_type_compatible(result, expected_type)) {
push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return);
}
} else {
if (!is_type_compatible(expected_type, result)) {
p_return->use_conversion = true;
}
#ifdef DEBUG_ENABLED
if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
}
} else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
#endif // DEBUG_ENABLED
}
}
@ -2887,7 +2879,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (id_type.is_hard_type()) {
switch (id_type.kind) {
case GDScriptParser::DataType::BUILTIN:
need_warn = !Variant::is_type_shared(id_type.builtin_type);
// TODO: Change `Variant::is_type_shared()` to include packed arrays?
need_warn = !Variant::is_type_shared(id_type.builtin_type) && id_type.builtin_type < Variant::PACKED_BYTE_ARRAY;
break;
case GDScriptParser::DataType::ENUM:
need_warn = true;
@ -3120,10 +3113,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
}
#endif // DEBUG_ENABLED
if (p_binary_op->left_operand->is_constant &&
p_binary_op->right_operand->is_constant &&
!p_binary_op->left_operand->reduced_value.is_shared() &&
!p_binary_op->right_operand->reduced_value.is_shared()) {
if (p_binary_op->left_operand->is_constant && p_binary_op->right_operand->is_constant) {
p_binary_op->is_constant = true;
if (p_binary_op->variant_op < Variant::OP_MAX) {
bool valid = false;
@ -3267,9 +3257,30 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
call_type.kind = GDScriptParser::DataType::BUILTIN;
call_type.builtin_type = builtin_type;
// Reference types are not suited for compile-time construction.
// Because in this case they would be the same reference in all constructed values.
if (all_is_constant && !Variant::is_type_shared(builtin_type)) {
bool safe_to_fold = true;
switch (builtin_type) {
// Those are stored by reference so not suited for compile-time construction.
// Because in this case they would be the same reference in all constructed values.
case Variant::OBJECT:
case Variant::DICTIONARY:
case Variant::ARRAY:
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
case Variant::PACKED_FLOAT32_ARRAY:
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::PACKED_STRING_ARRAY:
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
case Variant::PACKED_VECTOR4_ARRAY:
safe_to_fold = false;
break;
default:
break;
}
if (all_is_constant && safe_to_fold) {
// Construct here.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@ -5201,9 +5212,9 @@ void GDScriptAnalyzer::reduce_type_test(GDScriptParser::TypeTestNode *p_type_tes
p_type_test->is_constant = true;
p_type_test->reduced_value = false;
if (!is_type_compatible_strict_collections(test_type, operand_type)) {
if (!is_type_compatible(test_type, operand_type)) {
push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", operand_type.to_string(), test_type.to_string()), p_type_test->operand);
} else if (is_type_compatible_strict_collections(test_type, type_from_variant(p_type_test->operand->reduced_value, p_type_test->operand))) {
} else if (is_type_compatible(test_type, type_from_variant(p_type_test->operand->reduced_value, p_type_test->operand))) {
p_type_test->reduced_value = test_type.builtin_type != Variant::OBJECT || !p_type_test->operand->reduced_value.is_null();
}
@ -5253,33 +5264,25 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result);
}
Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &r_is_reduced) {
Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) {
if (p_expression == nullptr) {
return Variant();
}
if (p_expression->is_constant) {
r_is_reduced = true;
is_reduced = true;
return p_expression->reduced_value;
}
switch (p_expression->type) {
case GDScriptParser::Node::ARRAY:
return make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), r_is_reduced);
return make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), is_reduced);
case GDScriptParser::Node::DICTIONARY:
return make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), r_is_reduced);
return make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), is_reduced);
case GDScriptParser::Node::SUBSCRIPT:
return make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), r_is_reduced);
return make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), is_reduced);
case GDScriptParser::Node::CALL:
return make_call_reduced_value(static_cast<GDScriptParser::CallNode *>(p_expression), r_is_reduced);
case GDScriptParser::Node::BINARY_OPERATOR:
return make_binary_op_reduced_value(static_cast<GDScriptParser::BinaryOpNode *>(p_expression), r_is_reduced);
case GDScriptParser::Node::TERNARY_OPERATOR:
return make_ternary_op_reduced_value(static_cast<GDScriptParser::TernaryOpNode *>(p_expression), r_is_reduced);
case GDScriptParser::Node::CAST:
return make_cast_reduced_value(static_cast<GDScriptParser::CastNode *>(p_expression), r_is_reduced);
case GDScriptParser::Node::TYPE_TEST:
return make_type_test_reduced_value(static_cast<GDScriptParser::TypeTestNode *>(p_expression), r_is_reduced);
return make_call_reduced_value(static_cast<GDScriptParser::CallNode *>(p_expression), is_reduced);
default:
break;
}
@ -5287,7 +5290,7 @@ Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::Expressi
return Variant();
}
Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &r_is_reduced) {
Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) {
Array array = p_array->get_datatype().has_container_element_type(0) ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type(0)) : Array();
array.resize(p_array->elements.size());
@ -5305,11 +5308,11 @@ Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_
array.make_read_only();
r_is_reduced = true;
is_reduced = true;
return array;
}
Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &r_is_reduced) {
Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced) {
Dictionary dictionary = p_dictionary->get_datatype().has_container_element_types()
? make_dictionary_from_element_datatype(p_dictionary->get_datatype().get_container_element_type_or_variant(0), p_dictionary->get_datatype().get_container_element_type_or_variant(1))
: Dictionary();
@ -5334,11 +5337,11 @@ Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::Dictiona
dictionary.make_read_only();
r_is_reduced = true;
is_reduced = true;
return dictionary;
}
Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &r_is_reduced) {
Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced) {
if (p_subscript->base == nullptr || p_subscript->index == nullptr) {
return Variant();
}
@ -5353,7 +5356,7 @@ Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::Subscript
bool is_valid = false;
Variant value = base_value.get_named(p_subscript->attribute->name, is_valid);
if (is_valid) {
r_is_reduced = true;
is_reduced = true;
return value;
} else {
return Variant();
@ -5368,7 +5371,7 @@ Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::Subscript
bool is_valid = false;
Variant value = base_value.get(index_value, &is_valid);
if (is_valid) {
r_is_reduced = true;
is_reduced = true;
return value;
} else {
return Variant();
@ -5376,7 +5379,7 @@ Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::Subscript
}
}
Variant GDScriptAnalyzer::make_call_reduced_value(GDScriptParser::CallNode *p_call, bool &r_is_reduced) {
Variant GDScriptAnalyzer::make_call_reduced_value(GDScriptParser::CallNode *p_call, bool &is_reduced) {
if (p_call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {
Variant::Type type = Variant::NIL;
if (p_call->function_name == SNAME("Array")) {
@ -5404,6 +5407,7 @@ Variant GDScriptAnalyzer::make_call_reduced_value(GDScriptParser::CallNode *p_ca
Callable::CallError ce;
Variant::construct(type, result, argptrs, args.size(), ce);
if (ce.error) {
push_error(vformat(R"(Failed to construct "%s".)", Variant::get_type_name(type)), p_call);
return Variant();
}
@ -5415,146 +5419,13 @@ Variant GDScriptAnalyzer::make_call_reduced_value(GDScriptParser::CallNode *p_ca
dictionary.make_read_only();
}
r_is_reduced = true;
is_reduced = true;
return result;
}
return Variant();
}
Variant GDScriptAnalyzer::make_binary_op_reduced_value(GDScriptParser::BinaryOpNode *p_binary_op, bool &r_is_reduced) {
if (p_binary_op->variant_op == Variant::OP_MAX) {
return Variant();
}
bool is_left_op_value_reduced = false;
Variant left_op_value = make_expression_reduced_value(p_binary_op->left_operand, is_left_op_value_reduced);
if (!is_left_op_value_reduced) {
return Variant();
}
bool is_right_op_value_reduced = false;
Variant right_op_value = make_expression_reduced_value(p_binary_op->right_operand, is_right_op_value_reduced);
if (!is_right_op_value_reduced) {
return Variant();
}
Variant result;
bool valid = false;
Variant::evaluate(p_binary_op->variant_op, left_op_value, right_op_value, result, valid);
if (!valid) {
return Variant();
}
if (result.get_type() == Variant::ARRAY) {
Array array = result;
array.make_read_only();
} else if (result.get_type() == Variant::DICTIONARY) {
Dictionary dictionary = result;
dictionary.make_read_only();
}
r_is_reduced = true;
return result;
}
Variant GDScriptAnalyzer::make_ternary_op_reduced_value(GDScriptParser::TernaryOpNode *p_ternary_op, bool &r_is_reduced) {
bool is_condition_value_reduced = false;
Variant condition_value = make_expression_reduced_value(p_ternary_op->condition, is_condition_value_reduced);
if (!is_condition_value_reduced) {
return Variant();
}
bool is_true_expr_value_reduced = false;
Variant true_expr_value = make_expression_reduced_value(p_ternary_op->true_expr, is_true_expr_value_reduced);
if (!is_true_expr_value_reduced) {
return Variant();
}
bool is_false_expr_value_reduced = false;
Variant false_expr_value = make_expression_reduced_value(p_ternary_op->false_expr, is_false_expr_value_reduced);
if (!is_false_expr_value_reduced) {
return Variant();
}
r_is_reduced = true;
return condition_value.booleanize() ? true_expr_value : false_expr_value;
}
Variant GDScriptAnalyzer::make_cast_reduced_value(GDScriptParser::CastNode *p_cast, bool &r_is_reduced) {
bool is_operand_value_reduced = false;
Variant operand_value = make_expression_reduced_value(p_cast->operand, is_operand_value_reduced);
if (!is_operand_value_reduced) {
return Variant();
}
GDScriptParser::DataType cast_type = type_from_metatype(resolve_datatype(p_cast->cast_type));
if (!cast_type.is_set()) {
return Variant();
}
if (cast_type.is_variant()) {
r_is_reduced = true;
return operand_value;
}
if (cast_type.kind == GDScriptParser::DataType::BUILTIN || cast_type.kind == GDScriptParser::DataType::ENUM) {
Variant result;
const Variant *argptr = &operand_value;
Callable::CallError ce;
Variant::construct(cast_type.builtin_type, result, &argptr, 1, ce);
if (ce.error) {
return Variant();
}
if (result.get_type() == Variant::ARRAY) {
Array array = cast_type.has_container_element_type(0) ? make_array_from_element_datatype(cast_type.get_container_element_type(0)) : Array();
array.assign(result);
array.make_read_only();
result = array;
} else if (result.get_type() == Variant::DICTIONARY) {
Dictionary dictionary = cast_type.has_container_element_types()
? make_dictionary_from_element_datatype(cast_type.get_container_element_type_or_variant(0), cast_type.get_container_element_type_or_variant(1))
: Dictionary();
dictionary.assign(result);
dictionary.make_read_only();
result = dictionary;
}
r_is_reduced = true;
return result;
}
return Variant();
}
Variant GDScriptAnalyzer::make_type_test_reduced_value(GDScriptParser::TypeTestNode *p_type_test, bool &r_is_reduced) {
bool is_operand_value_reduced = false;
Variant operand_value = make_expression_reduced_value(p_type_test->operand, is_operand_value_reduced);
if (!is_operand_value_reduced) {
return Variant();
}
GDScriptParser::DataType test_type = type_from_metatype(p_type_test->test_type->get_datatype());
if (!test_type.is_set()) {
return Variant();
}
GDScriptParser::DataType operand_type = type_from_variant(operand_value, p_type_test->operand);
if (!operand_type.is_set()) {
return Variant();
}
bool result = false;
if (is_type_compatible_strict_collections(test_type, operand_type)) {
result = test_type.builtin_type != Variant::OBJECT || !operand_value.is_null();
}
r_is_reduced = true;
return result;
}
Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node) {
Array array;
@ -5652,57 +5523,6 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
return result;
}
GDScriptParser::DataType GDScriptAnalyzer::type_from_script(const Ref<Script> &p_script, const GDScriptParser::Node *p_source, bool p_is_meta_type) {
ERR_FAIL_COND_V(!p_script.is_valid(), GDScriptParser::DataType());
GDScriptParser::DataType result;
result.is_constant = true;
result.kind = GDScriptParser::DataType::NATIVE;
result.builtin_type = Variant::OBJECT;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; // Constant has explicit type.
result.is_meta_type = p_is_meta_type;
Ref<GDScript> gds = p_script;
if (gds.is_valid()) {
// This might be an inner class, so we want to get the parser for the root.
// But still get the inner class from that tree.
String script_path = gds->get_script_path();
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(script_path);
if (ref.is_null()) {
push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
GDScriptParser::DataType error_type;
error_type.kind = GDScriptParser::DataType::VARIANT;
return error_type;
}
Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
GDScriptParser::ClassNode *found = nullptr;
if (err == OK) {
found = ref->get_parser()->find_class(gds->fully_qualified_name);
if (found != nullptr) {
err = resolve_class_inheritance(found, p_source);
}
}
if (err || found == nullptr) {
push_error(vformat(R"(Could not resolve script "%s".)", script_path), p_source);
GDScriptParser::DataType error_type;
error_type.kind = GDScriptParser::DataType::VARIANT;
return error_type;
}
result.kind = GDScriptParser::DataType::CLASS;
result.native_type = found->get_datatype().native_type;
result.class_type = found;
result.script_path = ref->get_parser()->script_path;
} else {
result.kind = GDScriptParser::DataType::SCRIPT;
result.native_type = p_script->get_instance_base_type();
result.script_path = p_script->get_path();
}
result.script_type = p_script;
return result;
}
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.is_constant = true;
@ -5713,7 +5533,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
if (p_value.get_type() == Variant::ARRAY) {
const Array &array = p_value;
if (array.get_typed_script()) {
result.set_container_element_type(0, type_from_metatype(type_from_script(array.get_typed_script(), p_source, true)));
result.set_container_element_type(0, type_from_metatype(make_script_meta_type(array.get_typed_script())));
} else if (array.get_typed_class_name()) {
result.set_container_element_type(0, type_from_metatype(make_native_meta_type(array.get_typed_class_name())));
} else if (array.get_typed_builtin() != Variant::NIL) {
@ -5722,14 +5542,14 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
} else if (p_value.get_type() == Variant::DICTIONARY) {
const Dictionary &dict = p_value;
if (dict.get_typed_key_script()) {
result.set_container_element_type(0, type_from_metatype(type_from_script(dict.get_typed_key_script(), p_source, true)));
result.set_container_element_type(0, type_from_metatype(make_script_meta_type(dict.get_typed_key_script())));
} else if (dict.get_typed_key_class_name()) {
result.set_container_element_type(0, type_from_metatype(make_native_meta_type(dict.get_typed_key_class_name())));
} else if (dict.get_typed_key_builtin() != Variant::NIL) {
result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)dict.get_typed_key_builtin())));
}
if (dict.get_typed_value_script()) {
result.set_container_element_type(1, type_from_metatype(type_from_script(dict.get_typed_value_script(), p_source, true)));
result.set_container_element_type(1, type_from_metatype(make_script_meta_type(dict.get_typed_value_script())));
} else if (dict.get_typed_value_class_name()) {
result.set_container_element_type(1, type_from_metatype(make_native_meta_type(dict.get_typed_value_class_name())));
} else if (dict.get_typed_value_builtin() != Variant::NIL) {
@ -5746,16 +5566,50 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
result.native_type = obj->get_class_name();
Ref<Script> scr = p_value; // Check if value is a script itself.
bool is_meta_type;
if (scr.is_valid()) {
is_meta_type = true;
result.is_meta_type = true;
} else {
is_meta_type = false;
result.is_meta_type = false;
scr = obj->get_script();
}
if (scr.is_valid()) {
result = type_from_script(scr, p_source, is_meta_type);
Ref<GDScript> gds = scr;
if (gds.is_valid()) {
// This might be an inner class, so we want to get the parser for the root.
// But still get the inner class from that tree.
String script_path = gds->get_script_path();
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(script_path);
if (ref.is_null()) {
push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
GDScriptParser::DataType error_type;
error_type.kind = GDScriptParser::DataType::VARIANT;
return error_type;
}
Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
GDScriptParser::ClassNode *found = nullptr;
if (err == OK) {
found = ref->get_parser()->find_class(gds->fully_qualified_name);
if (found != nullptr) {
err = resolve_class_inheritance(found, p_source);
}
}
if (err || found == nullptr) {
push_error(vformat(R"(Could not resolve script "%s".)", script_path), p_source);
GDScriptParser::DataType error_type;
error_type.kind = GDScriptParser::DataType::VARIANT;
return error_type;
}
result.kind = GDScriptParser::DataType::CLASS;
result.native_type = found->get_datatype().native_type;
result.class_type = found;
result.script_path = ref->get_parser()->script_path;
} else {
result.kind = GDScriptParser::DataType::SCRIPT;
result.native_type = scr->get_instance_base_type();
result.script_path = scr->get_path();
}
result.script_type = scr;
} else {
result.kind = GDScriptParser::DataType::NATIVE;
if (result.native_type == GDScriptNativeClass::get_class_static()) {
@ -6296,21 +6150,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
return check_type_compatibility(p_target, p_source, p_allow_implicit_conversion, p_source_node);
}
// NOTE:`is_type_compatible()` considers typed arrays/dictionaries compatible with untyped ones (but the operation is unsafe).
// However, in the case of constant expressions, this leads to incorrect results.
bool GDScriptAnalyzer::is_type_compatible_strict_collections(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source) {
if (p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) {
if (p_target.has_container_element_type(0) && !p_source.has_container_element_type(0)) {
return false;
}
} else if (p_target.builtin_type == Variant::DICTIONARY && p_source.builtin_type == Variant::DICTIONARY) {
if (p_target.has_container_element_types() && !p_source.has_container_element_types()) {
return false;
}
}
return is_type_compatible(p_target, p_source);
}
// TODO: Add safe/unsafe return variable (for variant cases)
bool GDScriptAnalyzer::check_type_compatibility(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) {
// These return "true" so it doesn't affect users negatively.
@ -6538,7 +6377,7 @@ void GDScriptAnalyzer::resolve_pending_lambda_bodies() {
GDScriptParser::LambdaNode *previous_lambda = current_lambda;
bool previous_static_context = static_context;
List<GDScriptParser::LambdaNode *> lambdas = std::move(pending_body_resolution_lambdas);
List<GDScriptParser::LambdaNode *> lambdas = pending_body_resolution_lambdas;
pending_body_resolution_lambdas.clear();
for (GDScriptParser::LambdaNode *lambda : lambdas) {

View file

@ -33,6 +33,7 @@
#include "gdscript_cache.h"
#include "gdscript_parser.h"
#include "core/object/object.h"
#include "core/object/ref_counted.h"
class GDScriptAnalyzer {
@ -80,7 +81,7 @@ class GDScriptAnalyzer {
void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr, bool p_is_lambda = false);
void resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda = false);
void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
void resolve_suite(GDScriptParser::SuiteNode *p_suite, bool p_is_root = true);
void resolve_suite(GDScriptParser::SuiteNode *p_suite);
void resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind);
void resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local);
void resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local);
@ -115,24 +116,15 @@ class GDScriptAnalyzer {
void reduce_type_test(GDScriptParser::TypeTestNode *p_type_test);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
// These methods provide a fallback mechanism for constant folding in constant contexts (constant initializers,
// annotation arguments) and allow constant folding even when the normal constant folding mechanism does not consider
// an expression to be constant (usually due to the presence of `[...]` and `{...}` constructs in it).
// These methods assume that the normal expression reduction mechanism has already been performed.
Variant make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &r_is_reduced);
Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &r_is_reduced);
Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &r_is_reduced);
Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &r_is_reduced);
Variant make_call_reduced_value(GDScriptParser::CallNode *p_call, bool &r_is_reduced);
Variant make_binary_op_reduced_value(GDScriptParser::BinaryOpNode *p_binary_op, bool &r_is_reduced);
Variant make_ternary_op_reduced_value(GDScriptParser::TernaryOpNode *p_ternary_op, bool &r_is_reduced);
Variant make_cast_reduced_value(GDScriptParser::CastNode *p_cast, bool &r_is_reduced);
Variant make_type_test_reduced_value(GDScriptParser::TypeTestNode *p_type_test, bool &r_is_reduced);
Variant make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced);
Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced);
Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced);
Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced);
Variant make_call_reduced_value(GDScriptParser::CallNode *p_call, bool &is_reduced);
// Helpers.
Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);
Dictionary make_dictionary_from_element_datatype(const GDScriptParser::DataType &p_key_element_datatype, const GDScriptParser::DataType &p_value_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);
GDScriptParser::DataType type_from_script(const Ref<Script> &p_script, const GDScriptParser::Node *p_source, bool p_is_meta_type);
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false, bool p_is_readonly = false) const;
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
@ -146,7 +138,6 @@ class GDScriptAnalyzer {
void update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type);
void update_dictionary_literal_element_type(GDScriptParser::DictionaryNode *p_dictionary, const GDScriptParser::DataType &p_key_element_type, const GDScriptParser::DataType &p_value_element_type);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
bool is_type_compatible_strict_collections(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
void downgrade_node_type_source(GDScriptParser::Node *p_node);

View file

@ -30,7 +30,7 @@
#include "gdscript_byte_codegen.h"
#include "core/object/class_db.h"
#include "core/debugger/engine_debugger.h"
uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) {
function->_argument_count++;
@ -189,7 +189,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
}
if (temporaries[i].type != Variant::NIL) {
function->temporary_slots.push_back(Pair(stack_index, temporaries[i].type));
function->temporary_slots[stack_index] = temporaries[i].type;
}
}
@ -200,9 +200,6 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
for (const KeyValue<Variant, int> &K : constant_map) {
function->constants.write[K.value] = K.key;
}
for (const KeyValue<StringName, int> &K : local_constants) {
function->constant_map.insert(K.key, function->constants[K.value]);
}
} else {
function->_constants_ptr = nullptr;
function->_constant_count = 0;
@ -1834,30 +1831,23 @@ void GDScriptByteCodeGenerator::write_newline(int p_line) {
}
}
void GDScriptByteCodeGenerator::write_return(const Address &p_return_value, bool p_use_conversion) {
if (!p_use_conversion) {
append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
return;
}
void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
if (!function->return_type.has_type() || p_return_value.type.has_type()) {
// Either the function is untyped or the return value is also typed.
switch (function->return_type.kind) {
case GDScriptDataType::VARIANT: {
ERR_PRINT("Compiler bug: Unresolved return.");
// Shouldn't get here, but fail-safe to a regular return.
append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
} break;
case GDScriptDataType::BUILTIN: {
if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
// If this is a typed function, then we need to check for potential conversions.
if (function->return_type.has_type()) {
if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
// Typed array.
const GDScriptDataType &element_type = function->return_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
append(element_type.builtin_type);
append(element_type.native_type);
} else if (function->return_type.builtin_type == Variant::DICTIONARY && function->return_type.has_container_element_types()) {
} else if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::DICTIONARY &&
function->return_type.has_container_element_types()) {
// Typed dictionary.
const GDScriptDataType &key_type = function->return_type.get_container_element_type_or_variant(0);
const GDScriptDataType &value_type = function->return_type.get_container_element_type_or_variant(1);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_DICTIONARY);
@ -1868,29 +1858,72 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value, bool
append(key_type.native_type);
append(value_type.builtin_type);
append(value_type.native_type);
} else {
} else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) {
// Add conversion.
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN);
append(p_return_value);
append(function->return_type.builtin_type);
} else {
// Just assign.
append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
}
} break;
case GDScriptDataType::NATIVE: {
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE);
} else {
append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[function->return_type.native_type];
Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(class_idx);
} break;
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = function->return_type.script_type;
int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
}
} else {
switch (function->return_type.kind) {
case GDScriptDataType::BUILTIN: {
if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
const GDScriptDataType &element_type = function->return_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
append(element_type.builtin_type);
append(element_type.native_type);
} else if (function->return_type.builtin_type == Variant::DICTIONARY && function->return_type.has_container_element_types()) {
const GDScriptDataType &key_type = function->return_type.get_container_element_type_or_variant(0);
const GDScriptDataType &value_type = function->return_type.get_container_element_type_or_variant(1);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_DICTIONARY);
append(p_return_value);
append(get_constant_pos(key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
append(get_constant_pos(value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
append(key_type.builtin_type);
append(key_type.native_type);
append(value_type.builtin_type);
append(value_type.native_type);
} else {
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN);
append(p_return_value);
append(function->return_type.builtin_type);
}
} break;
case GDScriptDataType::NATIVE: {
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE);
append(p_return_value);
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[function->return_type.native_type];
Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(class_idx);
} break;
case GDScriptDataType::GDSCRIPT:
case GDScriptDataType::SCRIPT: {
Variant script = function->return_type.script_type;
int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT);
append(p_return_value);
append(script_idx);
} break;
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT);
append(p_return_value);
append(script_idx);
} break;
default: {
ERR_PRINT("Compiler bug: unresolved return.");
// Shouldn't get here, but fail-safe to a regular return;
append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
} break;
}
}
}

View file

@ -552,7 +552,7 @@ public:
virtual void write_continue() override;
virtual void write_breakpoint() override;
virtual void write_newline(int p_line) override;
virtual void write_return(const Address &p_return_value, bool p_use_conversion) override;
virtual void write_return(const Address &p_return_value) override;
virtual void write_assert(const Address &p_test, const Address &p_message) override;
virtual ~GDScriptByteCodeGenerator();

View file

@ -36,7 +36,6 @@
#include "gdscript_parser.h"
#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/templates/vector.h"
GDScriptParserRef::Status GDScriptParserRef::get_status() const {
@ -69,10 +68,6 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
ERR_FAIL_COND_V(clearing, ERR_BUG);
ERR_FAIL_COND_V(parser == nullptr && status != EMPTY, ERR_BUG);
if (p_new_status < status) {
return OK;
}
while (result == OK && p_new_status > status) {
switch (status) {
case EMPTY: {
@ -260,7 +255,7 @@ void GDScriptCache::remove_parser(const String &p_path) {
singleton->parser_map.erase(p_path);
// Have to copy while iterating, because parser_inverse_dependencies is modified.
HashSet<String> ideps(singleton->parser_inverse_dependencies[p_path]);
HashSet<String> ideps = singleton->parser_inverse_dependencies[p_path];
singleton->parser_inverse_dependencies.erase(p_path);
for (String idep_path : ideps) {
remove_parser(idep_path);
@ -428,7 +423,7 @@ Error GDScriptCache::finish_compiling(const String &p_owner) {
singleton->full_gdscript_cache[p_owner] = script;
singleton->shallow_gdscript_cache.erase(p_owner);
HashSet<String> depends(singleton->dependencies[p_owner]);
HashSet<String> depends = singleton->dependencies[p_owner];
Error err = OK;
for (const String &E : depends) {

View file

@ -78,9 +78,11 @@ public:
~GDScriptParserRef();
};
#ifdef TESTS_ENABLED
namespace GDScriptTests {
class TestGDScriptCacheAccessor;
}
#endif // TESTS_ENABLED
class GDScriptCache {
// String key is full path.
@ -95,7 +97,9 @@ class GDScriptCache {
friend class GDScript;
friend class GDScriptParserRef;
friend class GDScriptInstance;
#ifdef TESTS_ENABLED
friend class GDScriptTests::TestGDScriptCacheAccessor;
#endif // TESTS_ENABLED
static GDScriptCache *singleton;

View file

@ -31,6 +31,7 @@
#pragma once
#include "gdscript_function.h"
#include "gdscript_utility_functions.h"
#include "core/string/string_name.h"
#include "core/variant/variant.h"
@ -159,7 +160,7 @@ public:
virtual void write_continue() = 0;
virtual void write_breakpoint() = 0;
virtual void write_newline(int p_line) = 0;
virtual void write_return(const Address &p_return_value, bool p_use_conversion) = 0;
virtual void write_return(const Address &p_return_value) = 0;
virtual void write_assert(const Address &p_test, const Address &p_message) = 0;
virtual ~GDScriptCodeGenerator() {}

View file

@ -38,8 +38,8 @@
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
#include "scene/scene_string_names.h"
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
if (codegen.function_node && codegen.function_node->is_static) {
@ -419,7 +419,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
// If it's an autoload singleton, we postpone to load it at runtime.
// This is so one autoload doesn't try to load another before it's compiled.
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads(ProjectSettings::get_singleton()->get_autoload_list());
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
@ -2169,10 +2169,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
}
if (return_n->void_return) {
// Always return `null`, even if the expression is a call to a `void` function.
gen->write_return(codegen.add_constant(Variant()), false);
// Always return "null", even if the expression is a call to a void function.
gen->write_return(codegen.add_constant(Variant()));
} else {
gen->write_return(return_value, return_n->use_conversion);
gen->write_return(return_value);
}
if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
@ -2796,26 +2796,23 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
return err;
}
} else if (!base->is_valid()) {
String base_qualified_name = base->fully_qualified_name;
String base_path = base->path;
Error err = OK;
Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base_path, err, p_script->path);
Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base->path, err, p_script->path);
if (err) {
_set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base_qualified_name, base_path, error_names[err]), nullptr);
_set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
return err;
}
if (base_root.is_valid()) {
base = Ref<GDScript>(base_root->find_class(base_qualified_name));
base = Ref<GDScript>(base_root->find_class(base->fully_qualified_name));
}
if (base.is_null()) {
_set_error(vformat(R"(Could not find class "%s" in "%s".)", base_qualified_name, base_path), nullptr);
_set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
return ERR_COMPILATION_FAILED;
}
err = _prepare_compilation(base.ptr(), p_class->base_type.class_type, p_keep_state);
if (err) {
_set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base_qualified_name, base_path), nullptr);
_set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
return err;
}
}
@ -2863,11 +2860,9 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
}
break;
}
minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);
const GDScriptParser::DataType variable_type = variable->get_datatype();
minfo.data_type = _gdtype_from_datatype(variable_type, p_script);
PropertyInfo prop_info = variable_type.to_property_info(name);
PropertyInfo prop_info = variable->get_datatype().to_property_info(name);
PropertyInfo export_info = variable->export_info;
if (variable->exported) {
@ -2878,28 +2873,6 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
prop_info.hint = export_info.hint;
prop_info.hint_string = export_info.hint_string;
prop_info.usage = export_info.usage;
} else {
// Enum hint doesn't really belong to the data type information, so we don't want to add it to
// `GDScriptParser::DataType::to_property_info()`. However, we still want to add this metadata
// for unexported properties so they display nicely in the Remote Tree Inspector.
if (variable_type.kind == GDScriptParser::DataType::ENUM && !variable_type.is_meta_type) {
prop_info.hint = PROPERTY_HINT_ENUM;
String enum_hint_string;
bool first = true;
for (const KeyValue<StringName, int64_t> &E : variable_type.enum_values) {
if (first) {
first = false;
} else {
enum_hint_string += ",";
}
enum_hint_string += E.key.operator String().capitalize().xml_escape();
enum_hint_string += ":";
enum_hint_string += String::num_int64(E.value).xml_escape();
}
prop_info.hint_string = enum_hint_string;
}
}
prop_info.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
minfo.property_info = prop_info;

View file

@ -129,7 +129,7 @@ class GDScriptCompiler {
}
void start_block() {
HashMap<StringName, GDScriptCodeGenerator::Address> old_locals(locals);
HashMap<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
locals_stack.push_back(old_locals);
generator->start_block();
}

View file

@ -33,7 +33,6 @@
#include "gdscript.h"
#include "gdscript_function.h"
#include "core/object/method_bind.h"
#include "core/string/string_builder.h"
static String _get_variant_string(const Variant &p_variant) {
@ -1117,56 +1116,56 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 3;
} break;
#define DISASSEMBLE_ITERATE(m_type) \
case OPCODE_ITERATE_##m_type: { \
text += "for-loop (typed "; \
text += #m_type; \
text += ") "; \
text += DADDR(3); \
text += " in "; \
text += DADDR(2); \
text += " counter "; \
text += DADDR(1); \
text += " end "; \
#define DISASSEMBLE_ITERATE(m_type) \
case OPCODE_ITERATE_##m_type: { \
text += "for-loop (typed "; \
text += #m_type; \
text += ") "; \
text += DADDR(3); \
text += " in "; \
text += DADDR(2); \
text += " counter "; \
text += DADDR(1); \
text += " end "; \
text += itos(_code_ptr[ip + 4]); \
incr += 5; \
incr += 5; \
} break
#define DISASSEMBLE_ITERATE_BEGIN(m_type) \
case OPCODE_ITERATE_BEGIN_##m_type: { \
text += "for-init (typed "; \
text += #m_type; \
text += ") "; \
text += DADDR(3); \
text += " in "; \
text += DADDR(2); \
text += " counter "; \
text += DADDR(1); \
text += " end "; \
text += itos(_code_ptr[ip + 4]); \
incr += 5; \
text += "for-init (typed "; \
text += #m_type; \
text += ") "; \
text += DADDR(3); \
text += " in "; \
text += DADDR(2); \
text += " counter "; \
text += DADDR(1); \
text += " end "; \
text += itos(_code_ptr[ip + 4]); \
incr += 5; \
} break
#define DISASSEMBLE_ITERATE_TYPES(m_macro) \
m_macro(INT); \
m_macro(FLOAT); \
m_macro(VECTOR2); \
m_macro(VECTOR2I); \
m_macro(VECTOR3); \
m_macro(VECTOR3I); \
m_macro(STRING); \
m_macro(DICTIONARY); \
m_macro(ARRAY); \
m_macro(PACKED_BYTE_ARRAY); \
m_macro(PACKED_INT32_ARRAY); \
m_macro(PACKED_INT64_ARRAY); \
m_macro(PACKED_FLOAT32_ARRAY); \
m_macro(PACKED_FLOAT64_ARRAY); \
m_macro(PACKED_STRING_ARRAY); \
m_macro(PACKED_VECTOR2_ARRAY); \
m_macro(PACKED_VECTOR3_ARRAY); \
m_macro(PACKED_COLOR_ARRAY); \
m_macro(PACKED_VECTOR4_ARRAY); \
m_macro(INT); \
m_macro(FLOAT); \
m_macro(VECTOR2); \
m_macro(VECTOR2I); \
m_macro(VECTOR3); \
m_macro(VECTOR3I); \
m_macro(STRING); \
m_macro(DICTIONARY); \
m_macro(ARRAY); \
m_macro(PACKED_BYTE_ARRAY); \
m_macro(PACKED_INT32_ARRAY); \
m_macro(PACKED_INT64_ARRAY); \
m_macro(PACKED_FLOAT32_ARRAY); \
m_macro(PACKED_FLOAT64_ARRAY); \
m_macro(PACKED_STRING_ARRAY); \
m_macro(PACKED_VECTOR2_ARRAY); \
m_macro(PACKED_VECTOR3_ARRAY); \
m_macro(PACKED_COLOR_ARRAY); \
m_macro(PACKED_VECTOR4_ARRAY); \
m_macro(OBJECT)
case OPCODE_ITERATE_BEGIN: {
@ -1257,11 +1256,11 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
#define DISASSEMBLE_TYPE_ADJUST(m_v_type) \
case OPCODE_TYPE_ADJUST_##m_v_type: { \
text += "type adjust ("; \
text += #m_v_type; \
text += ") "; \
text += DADDR(1); \
incr += 2; \
text += "type adjust ("; \
text += #m_v_type; \
text += ") "; \
text += DADDR(1); \
incr += 2; \
} break
DISASSEMBLE_TYPE_ADJUST(BOOL);

View file

@ -29,6 +29,7 @@
/**************************************************************************/
#include "gdscript.h"
#include "gdscript_analyzer.h"
#include "gdscript_parser.h"
#include "gdscript_tokenizer.h"
@ -42,9 +43,7 @@
#include "core/config/engine.h"
#include "core/core_constants.h"
#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/math/expression.h"
#include "core/object/class_db.h"
#include "core/variant/container_type_validate.h"
#ifdef TOOLS_ENABLED
@ -170,8 +169,8 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
for (const GDScriptParser::ParserError &pe : parser.get_errors()) {
ScriptLanguage::ScriptError e;
e.path = p_path;
e.line = pe.start_line;
e.column = pe.start_column;
e.line = pe.line;
e.column = pe.column;
e.message = pe.message;
r_errors->push_back(e);
}
@ -181,8 +180,8 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) {
ScriptLanguage::ScriptError e;
e.path = E.key;
e.line = pe.start_line;
e.column = pe.start_column;
e.line = pe.line;
e.column = pe.column;
e.message = pe.message;
r_errors->push_back(e);
}
@ -247,6 +246,10 @@ int GDScriptLanguage::find_function(const String &p_function, const String &p_co
return -1;
}
Script *GDScriptLanguage::create_script() const {
return memnew(GDScript);
}
/* DEBUGGER FUNCTIONS */
thread_local int GDScriptLanguage::_debug_parse_err_line = -1;
@ -342,12 +345,7 @@ void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p
f->debug_get_stack_member_state(*cl->line, &locals);
for (const Pair<StringName, int> &E : locals) {
p_locals->push_back(E.first);
if (f->constant_map.has(E.first)) {
p_values->push_back(f->constant_map[E.first]);
} else {
p_values->push_back(cl->stack[E.second]);
}
p_values->push_back(cl->stack[E.second]);
}
}
@ -478,7 +476,7 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const
MethodInfo mi;
mi.name = "preload";
mi.arguments.push_back(PropertyInfo(Variant::STRING, "path"));
mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, Resource::get_class_static());
mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource");
p_functions->push_back(mi);
}
{
@ -940,7 +938,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption hint1("attenuation", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
hint1.insert_text = hint1.display.quote(p_quote_style);
r_result.insert(hint1.display, hint1);
ScriptLanguage::CodeCompletionOption hint2("positive_only", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
ScriptLanguage::CodeCompletionOption hint2("inout", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
hint2.insert_text = hint2.display.quote(p_quote_style);
r_result.insert(hint2.display, hint2);
}
@ -1125,7 +1123,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
}
// Autoload singletons
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads(ProjectSettings::get_singleton()->get_autoload_list());
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
const ProjectSettings::AutoloadInfo &info = E.value;
@ -3345,7 +3343,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
}
MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, Resource::get_class_static()), "preload", PropertyInfo(Variant::STRING, "path"));
MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), "preload", PropertyInfo(Variant::STRING, "path"));
r_arghint = _make_arguments_hint(mi, p_argidx);
return;
} else if (p_call->type != GDScriptParser::Node::CALL) {
@ -3466,7 +3464,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
switch (completion_context.type) {
case GDScriptParser::COMPLETION_NONE:
case GDScriptParser::COMPLETION_DECLARATION:
break;
case GDScriptParser::COMPLETION_ANNOTATION: {
List<MethodInfo> annotations;
@ -4414,8 +4411,6 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
case GDScriptParser::COMPLETION_METHOD:
case GDScriptParser::COMPLETION_ASSIGN:
case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_DECLARATION:
case GDScriptParser::COMPLETION_INHERIT_TYPE:
case GDScriptParser::COMPLETION_IDENTIFIER:
case GDScriptParser::COMPLETION_PROPERTY_METHOD:
case GDScriptParser::COMPLETION_SUBSCRIPT: {
@ -4608,14 +4603,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
} break;
case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {
// This logic applies to any method declaration (override or not),
// but shows parent documentation on virtual method overrides.
GDScriptParser::DataType base_type = context.current_class->base_type;
if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {
return OK;
}
base_type = context.current_class->get_datatype();
if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {
return OK;
}

View file

@ -32,125 +32,6 @@
#include "gdscript.h"
#include "core/object/class_db.h"
bool GDScriptDataType::is_type(const Variant &p_variant, bool p_allow_implicit_conversion) const {
switch (kind) {
case VARIANT: {
return true;
} break;
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) {
Array array = p_variant;
if (array.is_typed()) {
const GDScriptDataType &elem_type = container_element_types[0];
Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
StringName array_native_type = array.get_typed_class_name();
Ref<Script> array_script_type_ref = array.get_typed_script();
if (array_script_type_ref.is_valid()) {
valid = (elem_type.kind == SCRIPT || elem_type.kind == GDSCRIPT) && elem_type.script_type == array_script_type_ref.ptr();
} else if (array_native_type != StringName()) {
valid = elem_type.kind == NATIVE && elem_type.native_type == array_native_type;
} else {
valid = elem_type.kind == BUILTIN && elem_type.builtin_type == array_builtin_type;
}
} else {
valid = false;
}
} else if (valid && builtin_type == Variant::DICTIONARY && has_container_element_types()) {
Dictionary dictionary = p_variant;
if (dictionary.is_typed()) {
if (dictionary.is_typed_key()) {
GDScriptDataType key = get_container_element_type_or_variant(0);
Variant::Type key_builtin_type = (Variant::Type)dictionary.get_typed_key_builtin();
StringName key_native_type = dictionary.get_typed_key_class_name();
Ref<Script> key_script_type_ref = dictionary.get_typed_key_script();
if (key_script_type_ref.is_valid()) {
valid = (key.kind == SCRIPT || key.kind == GDSCRIPT) && key.script_type == key_script_type_ref.ptr();
} else if (key_native_type != StringName()) {
valid = key.kind == NATIVE && key.native_type == key_native_type;
} else {
valid = key.kind == BUILTIN && key.builtin_type == key_builtin_type;
}
}
if (valid && dictionary.is_typed_value()) {
GDScriptDataType value = get_container_element_type_or_variant(1);
Variant::Type value_builtin_type = (Variant::Type)dictionary.get_typed_value_builtin();
StringName value_native_type = dictionary.get_typed_value_class_name();
Ref<Script> value_script_type_ref = dictionary.get_typed_value_script();
if (value_script_type_ref.is_valid()) {
valid = (value.kind == SCRIPT || value.kind == GDSCRIPT) && value.script_type == value_script_type_ref.ptr();
} else if (value_native_type != StringName()) {
valid = value.kind == NATIVE && value.native_type == value_native_type;
} else {
valid = value.kind == BUILTIN && value.builtin_type == value_builtin_type;
}
}
} else {
valid = false;
}
} else if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(var_type, builtin_type);
}
return valid;
} break;
case NATIVE: {
if (p_variant.get_type() == Variant::NIL) {
return true;
}
if (p_variant.get_type() != Variant::OBJECT) {
return false;
}
bool was_freed = false;
Object *obj = p_variant.get_validated_object_with_check(was_freed);
if (!obj) {
return !was_freed;
}
if (!ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
return false;
}
return true;
} break;
case SCRIPT:
case GDSCRIPT: {
if (p_variant.get_type() == Variant::NIL) {
return true;
}
if (p_variant.get_type() != Variant::OBJECT) {
return false;
}
bool was_freed = false;
Object *obj = p_variant.get_validated_object_with_check(was_freed);
if (!obj) {
return !was_freed;
}
Ref<Script> base = obj && obj->get_script_instance() ? obj->get_script_instance()->get_script() : nullptr;
bool valid = false;
while (base.is_valid()) {
if (base == script_type) {
valid = true;
break;
}
base = base->get_base_script();
}
return valid;
} break;
}
return false;
}
/////////////////////
Variant GDScriptFunction::get_constant(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, constants.size(), "<errconst>");
return constants[p_idx];
@ -325,7 +206,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
return Variant();
#endif
}
// Do these now to avoid locking again after the call.
// Do these now to avoid locking again after the call
scripts_list.remove_from_list();
instances_list.remove_from_list();
}
@ -334,9 +215,26 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
Callable::CallError err;
Variant ret = function->call(nullptr, nullptr, 0, err, &state);
function = nullptr; // Cleaned up.
bool completed = true;
// If the return value is a GDScriptFunctionState reference,
// then the function did await again after resuming.
if (ret.is_ref_counted()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
// Keep the first state alive via reference.
gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
function = nullptr; //cleaned up;
state.result = Variant();
if (completed) {
_clear_stack();
}
return ret;
}
@ -379,6 +277,5 @@ GDScriptFunctionState::~GDScriptFunctionState() {
MutexLock lock(GDScriptLanguage::singleton->mutex);
scripts_list.remove_from_list();
instances_list.remove_from_list();
_clear_stack();
}
}

View file

@ -64,7 +64,120 @@ public:
_FORCE_INLINE_ bool has_type() const { return kind != VARIANT; }
bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const;
bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
switch (kind) {
case VARIANT: {
return true;
} break;
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) {
Array array = p_variant;
if (array.is_typed()) {
const GDScriptDataType &elem_type = container_element_types[0];
Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
StringName array_native_type = array.get_typed_class_name();
Ref<Script> array_script_type_ref = array.get_typed_script();
if (array_script_type_ref.is_valid()) {
valid = (elem_type.kind == SCRIPT || elem_type.kind == GDSCRIPT) && elem_type.script_type == array_script_type_ref.ptr();
} else if (array_native_type != StringName()) {
valid = elem_type.kind == NATIVE && elem_type.native_type == array_native_type;
} else {
valid = elem_type.kind == BUILTIN && elem_type.builtin_type == array_builtin_type;
}
} else {
valid = false;
}
} else if (valid && builtin_type == Variant::DICTIONARY && has_container_element_types()) {
Dictionary dictionary = p_variant;
if (dictionary.is_typed()) {
if (dictionary.is_typed_key()) {
GDScriptDataType key = get_container_element_type_or_variant(0);
Variant::Type key_builtin_type = (Variant::Type)dictionary.get_typed_key_builtin();
StringName key_native_type = dictionary.get_typed_key_class_name();
Ref<Script> key_script_type_ref = dictionary.get_typed_key_script();
if (key_script_type_ref.is_valid()) {
valid = (key.kind == SCRIPT || key.kind == GDSCRIPT) && key.script_type == key_script_type_ref.ptr();
} else if (key_native_type != StringName()) {
valid = key.kind == NATIVE && key.native_type == key_native_type;
} else {
valid = key.kind == BUILTIN && key.builtin_type == key_builtin_type;
}
}
if (valid && dictionary.is_typed_value()) {
GDScriptDataType value = get_container_element_type_or_variant(1);
Variant::Type value_builtin_type = (Variant::Type)dictionary.get_typed_value_builtin();
StringName value_native_type = dictionary.get_typed_value_class_name();
Ref<Script> value_script_type_ref = dictionary.get_typed_value_script();
if (value_script_type_ref.is_valid()) {
valid = (value.kind == SCRIPT || value.kind == GDSCRIPT) && value.script_type == value_script_type_ref.ptr();
} else if (value_native_type != StringName()) {
valid = value.kind == NATIVE && value.native_type == value_native_type;
} else {
valid = value.kind == BUILTIN && value.builtin_type == value_builtin_type;
}
}
} else {
valid = false;
}
} else if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(var_type, builtin_type);
}
return valid;
} break;
case NATIVE: {
if (p_variant.get_type() == Variant::NIL) {
return true;
}
if (p_variant.get_type() != Variant::OBJECT) {
return false;
}
bool was_freed = false;
Object *obj = p_variant.get_validated_object_with_check(was_freed);
if (!obj) {
return !was_freed;
}
if (!ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
return false;
}
return true;
} break;
case SCRIPT:
case GDSCRIPT: {
if (p_variant.get_type() == Variant::NIL) {
return true;
}
if (p_variant.get_type() != Variant::OBJECT) {
return false;
}
bool was_freed = false;
Object *obj = p_variant.get_validated_object_with_check(was_freed);
if (!obj) {
return !was_freed;
}
Ref<Script> base = obj && obj->get_script_instance() ? obj->get_script_instance()->get_script() : nullptr;
bool valid = false;
while (base.is_valid()) {
if (base == script_type) {
valid = true;
break;
}
base = base->get_base_script();
}
return valid;
} break;
}
return false;
}
bool can_contain_object() const {
if (kind == BUILTIN) {
@ -360,13 +473,12 @@ private:
SelfList<GDScriptFunction> function_list{ this };
mutable Variant nil;
TightLocalVector<Pair<int, Variant::Type>> temporary_slots;
HashMap<int, Variant::Type> temporary_slots;
List<StackDebug> stack_debug;
Vector<int> code;
Vector<int> default_arguments;
Vector<Variant> constants;
HashMap<StringName, Variant> constant_map;
Vector<StringName> global_names;
Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
Vector<Variant::ValidatedSetter> setters;
@ -505,6 +617,7 @@ class GDScriptFunctionState : public RefCounted {
GDScriptFunction *function = nullptr;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Ref<GDScriptFunctionState> first_state;
SelfList<GDScriptFunctionState> scripts_list;
SelfList<GDScriptFunctionState> instances_list;

View file

@ -36,7 +36,6 @@
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "core/object/class_db.h"
#include "scene/main/multiplayer_api.h"
#ifdef DEBUG_ENABLED
@ -236,22 +235,12 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
// TODO: Improve error reporting by pointing at source code.
// TODO: Errors might point at more than one place at once (e.g. show previous declaration).
panic_mode = true;
ParserError err;
err.message = p_message;
// TODO: Improve positional information.
if (p_origin == nullptr) {
err.start_line = previous.start_line;
err.start_column = previous.start_column;
err.end_line = previous.end_line;
err.end_column = previous.end_column;
errors.push_back({ p_message, previous.start_line, previous.start_column });
} else {
err.start_line = p_origin->start_line;
err.start_column = p_origin->start_column;
err.end_line = p_origin->end_line;
err.end_column = p_origin->end_column;
errors.push_back({ p_message, p_origin->start_line, p_origin->start_column });
}
errors.push_back(err);
}
#ifdef DEBUG_ENABLED
@ -290,9 +279,7 @@ void GDScriptParser::apply_pending_warnings() {
warning.code = pw.code;
warning.symbols = pw.symbols;
warning.start_line = pw.source->start_line;
warning.start_column = pw.source->start_column;
warning.end_line = pw.source->end_line;
warning.end_column = pw.source->end_column;
if (pw.treated_as_error) {
push_error(warning.get_message() + String(" (Warning treated as error.)"), pw.source);
@ -704,12 +691,12 @@ void GDScriptParser::parse_program() {
current_class = head;
bool can_have_class_or_extends = true;
#define PUSH_PENDING_ANNOTATIONS_TO_HEAD \
if (!annotation_stack.is_empty()) { \
#define PUSH_PENDING_ANNOTATIONS_TO_HEAD \
if (!annotation_stack.is_empty()) { \
for (AnnotationNode *annot : annotation_stack) { \
head->annotations.push_back(annot); \
} \
annotation_stack.clear(); \
head->annotations.push_back(annot); \
} \
annotation_stack.clear(); \
}
while (!check(GDScriptTokenizer::Token::TK_EOF)) {
@ -938,8 +925,6 @@ bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
GDScriptParser::ClassNode *GDScriptParser::parse_class(bool p_is_static) {
ClassNode *n_class = alloc_node<ClassNode>();
make_completion_context(COMPLETION_DECLARATION, n_class);
ClassNode *previous_class = current_class;
current_class = n_class;
n_class->outer = previous_class;
@ -996,12 +981,6 @@ void GDScriptParser::parse_class_name() {
current_class->fqcn = String(current_class->identifier->name);
}
if (script_path.begins_with("res://") && script_path.contains("::")) {
push_error(R"("class_name" isn't allowed in built-in scripts.)");
}
make_completion_context(COMPLETION_DECLARATION, current_class);
if (match(GDScriptTokenizer::Token::EXTENDS)) {
// Allow extends on the same line.
parse_extends();
@ -1235,8 +1214,6 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_is_static) {
GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_is_static, bool p_allow_property) {
VariableNode *variable = alloc_node<VariableNode>();
make_completion_context(COMPLETION_DECLARATION, variable);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
complete_extents(variable);
return nullptr;
@ -1473,8 +1450,6 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
GDScriptParser::ConstantNode *GDScriptParser::parse_constant(bool p_is_static) {
ConstantNode *constant = alloc_node<ConstantNode>();
make_completion_context(COMPLETION_DECLARATION, constant);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
complete_extents(constant);
return nullptr;
@ -1543,8 +1518,6 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
GDScriptParser::SignalNode *GDScriptParser::parse_signal(bool p_is_static) {
SignalNode *signal = alloc_node<SignalNode>();
make_completion_context(COMPLETION_DECLARATION, signal);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
complete_extents(signal);
return nullptr;
@ -1591,8 +1564,6 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
EnumNode *enum_node = alloc_node<EnumNode>();
bool named = false;
make_completion_context(COMPLETION_DECLARATION, enum_node);
if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
enum_node->identifier = parse_identifier();
named = true;
@ -3483,9 +3454,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
if (!check(GDScriptTokenizer::Token::PERIOD)) {
make_completion_context(COMPLETION_SUPER, call);
}
if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
push_multiline(true);
advance();
push_multiline(true);
if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
// Implicit call to the parent method of the same name.
if (current_function == nullptr) {
push_error(R"(Cannot use implicit "super" call outside of a function.)");
@ -3502,18 +3472,15 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
consume(GDScriptTokenizer::Token::PERIOD, R"(Expected "." or "(" after "super".)");
make_completion_context(COMPLETION_SUPER_METHOD, call);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
pop_multiline();
complete_extents(call);
return nullptr;
}
IdentifierNode *identifier = parse_identifier();
call->callee = identifier;
call->function_name = identifier->name;
if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
push_multiline(true);
advance();
} else {
push_error(R"(Expected "(" after function name.)");
if (!consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after function name.)")) {
pop_multiline();
complete_extents(call);
return nullptr;
}
@ -3935,24 +3902,20 @@ enum DocLineState {
DOC_LINE_IN_KBD,
};
static void _process_doc_line(const String &p_line, String &r_text, const String &p_space_prefix, DocLineState &r_state) {
static String _process_doc_line(const String &p_line, const String &p_text, const String &p_space_prefix, DocLineState &r_state) {
String line = p_line;
if (r_state == DOC_LINE_NORMAL) {
line = line.lstrip(" \t");
line = line.strip_edges(true, false);
} else {
line = line.trim_prefix(p_space_prefix);
}
String line_join;
if (!r_text.is_empty()) {
if (!p_text.is_empty()) {
if (r_state == DOC_LINE_NORMAL) {
if (r_text.ends_with("[/codeblock]")) {
if (p_text.ends_with("[/codeblock]")) {
line_join = "\n";
} else if (r_text.ends_with("[br]")) {
// We want to replace `[br][br]` with `\n` (paragraph), so we move the trailing `[br]` here.
r_text = r_text.left(-4); // `-len("[br]")`.
line = "[br]" + line;
} else if (!r_text.ends_with("\n")) {
} else if (!p_text.ends_with("[br]")) {
line_join = " ";
}
} else {
@ -3982,14 +3945,7 @@ static void _process_doc_line(const String &p_line, String &r_text, const String
from = rb_pos + 1;
String tag = line.substr(lb_pos + 1, rb_pos - lb_pos - 1);
if (tag == "br") {
if (line.substr(from, 4) == "[br]") { // `len("[br]")`.
// Replace `[br][br]` with `\n` (paragraph).
result += line.substr(buffer_start, lb_pos - buffer_start) + '\n';
from += 4; // `len("[br]")`.
buffer_start = from;
}
} else if (tag == "code" || tag.begins_with("code ")) {
if (tag == "code" || tag.begins_with("code ")) {
r_state = DOC_LINE_IN_CODE;
} else if (tag == "codeblock" || tag.begins_with("codeblock ")) {
if (lb_pos == 0) {
@ -4057,10 +4013,10 @@ static void _process_doc_line(const String &p_line, String &r_text, const String
result += line.substr(buffer_start);
if (r_state == DOC_LINE_NORMAL) {
result = result.rstrip(" \t");
result = result.strip_edges(false, true);
}
r_text += line_join + result;
return line_join + result;
}
bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) {
@ -4123,7 +4079,7 @@ GDScriptParser::MemberDocData GDScriptParser::parse_doc_comment(int p_line, bool
}
}
_process_doc_line(doc_line, result.description, space_prefix, state);
result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
}
return result;
@ -4233,9 +4189,9 @@ GDScriptParser::ClassDocData GDScriptParser::parse_class_doc_comment(int p_line,
}
if (is_in_brief) {
_process_doc_line(doc_line, result.brief, space_prefix, state);
result.brief += _process_doc_line(doc_line, result.brief, space_prefix, state);
} else {
_process_doc_line(doc_line, result.description, space_prefix, state);
result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
}
}
@ -4832,10 +4788,10 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta
String enum_hint_string;
bool first = true;
for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
if (first) {
first = false;
} else {
if (!first) {
enum_hint_string += ",";
} else {
first = false;
}
enum_hint_string += E.key.operator String().capitalize().xml_escape();
enum_hint_string += ":";
@ -4909,10 +4865,10 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta
String enum_hint_string;
bool first = true;
for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
if (first) {
first = false;
} else {
if (!first) {
enum_hint_string += ",";
} else {
first = false;
}
enum_hint_string += E.key.operator String().capitalize().xml_escape();
enum_hint_string += ":";
@ -5132,14 +5088,14 @@ bool GDScriptParser::warning_ignore_annotation(AnnotationNode *p_annotation, Nod
int end_line = p_target->end_line;
switch (p_target->type) {
#define SIMPLE_CASE(m_type, m_class, m_property) \
case m_type: { \
#define SIMPLE_CASE(m_type, m_class, m_property) \
case m_type: { \
m_class *node = static_cast<m_class *>(p_target); \
if (node->m_property == nullptr) { \
end_line = node->start_line; \
} else { \
end_line = node->m_property->end_line; \
} \
if (node->m_property == nullptr) { \
end_line = node->start_line; \
} else { \
end_line = node->m_property->end_line; \
} \
} break;
// Can contain properties (set/get).

View file

@ -269,10 +269,7 @@ public:
// };
// Type type = NO_ERROR;
String message;
int start_line = 0;
int start_column = 0;
int end_line = 0;
int end_column = 0;
int line = 0, column = 0;
};
#ifdef TOOLS_ENABLED
@ -340,10 +337,8 @@ public:
};
Type type = NONE;
int start_line = 0;
int start_column = 0;
int end_line = 0;
int end_column = 0;
int start_line = 0, end_line = 0;
int start_column = 0, end_column = 0;
Node *next = nullptr;
List<AnnotationNode *> annotations;
@ -1047,7 +1042,6 @@ public:
struct ReturnNode : public Node {
ExpressionNode *return_value = nullptr;
bool void_return = false;
bool use_conversion = false;
ReturnNode() {
type = RETURN;
@ -1114,10 +1108,8 @@ public:
StringName name;
FunctionNode *source_function = nullptr;
int start_line = 0;
int start_column = 0;
int end_line = 0;
int end_column = 0;
int start_line = 0, end_line = 0;
int start_column = 0, end_column = 0;
DataType get_datatype() const;
String get_name() const;
@ -1298,7 +1290,7 @@ public:
COMPLETION_ATTRIBUTE_METHOD, // After id.| to look for methods.
COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, // Constants inside a built-in type (e.g. Color.BLUE) or static methods (e.g. Color.html).
COMPLETION_CALL_ARGUMENTS, // Complete with nodes, input actions, enum values (or usual expressions).
COMPLETION_DECLARATION, // Potential declaration (var, const, class, etc.).
// TODO: COMPLETION_DECLARATION, // Potential declaration (var, const, func).
COMPLETION_GET_NODE, // Get node with $ notation.
COMPLETION_IDENTIFIER, // List available identifiers in scope.
COMPLETION_INHERIT_TYPE, // Type after extends. Exclude non-viable types (built-ins, enums, void). Includes subtypes using the argument index.

View file

@ -1,182 +0,0 @@
/**************************************************************************/
/* gdscript_resource_format.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "gdscript_resource_format.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
#include "core/io/file_access.h"
#include "core/object/class_db.h"
Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Error err;
bool ignoring = p_cache_mode == CACHE_MODE_IGNORE || p_cache_mode == CACHE_MODE_IGNORE_DEEP;
Ref<GDScript> scr = GDScriptCache::get_full_script(p_original_path, err, "", ignoring);
if (err && scr.is_valid()) {
// If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.
ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_original_path, error_names[err]));
}
if (r_error) {
// Don't fail loading because of parsing error.
*r_error = scr.is_valid() ? OK : err;
}
return scr;
}
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
p_extensions->push_back("gdc");
}
bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
return (p_type == "Script" || p_type == "GDScript");
}
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
if (el == "gd" || el == "gdc") {
return "GDScript";
}
return "";
}
void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'.");
String source = file->get_as_utf8_string();
if (source.is_empty()) {
return;
}
GDScriptParser parser;
if (OK != parser.parse(source, p_path, false)) {
return;
}
for (const String &E : parser.get_dependencies()) {
p_dependencies->push_back(E);
}
}
void ResourceFormatLoaderGDScript::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Ref<GDScript> scr = ResourceLoader::load(p_path);
if (scr.is_null()) {
return;
}
const String source = scr->get_source_code();
GDScriptTokenizerText tokenizer;
tokenizer.set_source_code(source);
GDScriptTokenizer::Token current = tokenizer.scan();
while (current.type != GDScriptTokenizer::Token::TK_EOF) {
if (!current.is_identifier()) {
current = tokenizer.scan();
continue;
}
int insert_idx = 0;
for (int i = 0; i < current.start_line - 1; i++) {
insert_idx = source.find("\n", insert_idx) + 1;
}
// Insert the "cursor" character, needed for the lookup to work.
const String source_with_cursor = source.insert(insert_idx + current.start_column, String::chr(0xFFFF));
ScriptLanguage::LookupResult result;
if (scr->get_language()->lookup_code(source_with_cursor, current.get_identifier(), p_path, nullptr, result) == OK) {
if (!result.class_name.is_empty() && ClassDB::class_exists(result.class_name)) {
r_classes->insert(result.class_name);
}
if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY) {
PropertyInfo prop;
if (ClassDB::get_property_info(result.class_name, result.class_member, &prop)) {
if (!prop.class_name.is_empty() && ClassDB::class_exists(prop.class_name)) {
r_classes->insert(prop.class_name);
}
if (!prop.hint_string.is_empty() && ClassDB::class_exists(prop.hint_string)) {
r_classes->insert(prop.hint_string);
}
}
} else if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD) {
MethodInfo met;
if (ClassDB::get_method_info(result.class_name, result.class_member, &met)) {
if (!met.return_val.class_name.is_empty() && ClassDB::class_exists(met.return_val.class_name)) {
r_classes->insert(met.return_val.class_name);
}
if (!met.return_val.hint_string.is_empty() && ClassDB::class_exists(met.return_val.hint_string)) {
r_classes->insert(met.return_val.hint_string);
}
}
}
}
current = tokenizer.scan();
}
}
Error ResourceFormatSaverGDScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<GDScript> sqscr = p_resource;
ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
String source = sqscr->get_source_code();
{
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'.");
file->store_string(source);
if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
return ERR_CANT_CREATE;
}
}
if (ScriptServer::is_reload_scripts_on_save_enabled()) {
GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true);
}
return OK;
}
void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
if (Object::cast_to<GDScript>(*p_resource)) {
p_extensions->push_back("gd");
}
}
bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<GDScript>(*p_resource) != nullptr;
}

View file

@ -1,55 +0,0 @@
/**************************************************************************/
/* gdscript_resource_format.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
GDSOFTCLASS(ResourceFormatLoaderGDScript, ResourceFormatLoader);
public:
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
GDSOFTCLASS(ResourceFormatSaverGDScript, ResourceFormatSaver);
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
virtual bool recognize(const Ref<Resource> &p_resource) const override;
};

View file

@ -372,6 +372,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::make_token(Token::Type p_type) {
if (start_line == line) {
// Single line token.
if (cursor_line == start_line && cursor_column >= start_column && cursor_column <= last_column) {
token.cursor_position = cursor_column - start_column;
if (cursor_column == start_column) {
token.cursor_place = CURSOR_BEGINNING;
} else if (cursor_column < column) {
@ -384,6 +385,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::make_token(Token::Type p_type) {
// Multi line token.
if (cursor_line == start_line && cursor_column >= start_column) {
// Is in first line.
token.cursor_position = cursor_column - start_column;
if (cursor_column == start_column) {
token.cursor_place = CURSOR_BEGINNING;
} else {
@ -391,6 +393,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::make_token(Token::Type p_type) {
}
} else if (cursor_line == line && cursor_column <= last_column) {
// Is in last line.
token.cursor_position = cursor_column - start_column;
if (cursor_column < column) {
token.cursor_place = CURSOR_MIDDLE;
} else {
@ -398,7 +401,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::make_token(Token::Type p_type) {
}
} else if (cursor_line > start_line && cursor_line < line) {
// Is in middle line.
token.cursor_place = CURSOR_MIDDLE;
token.cursor_position = CURSOR_MIDDLE;
}
}
}
@ -483,66 +486,66 @@ GDScriptTokenizer::Token GDScriptTokenizerText::annotation() {
return annotation;
}
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("as", Token::AS) \
KEYWORD("and", Token::AND) \
KEYWORD("assert", Token::ASSERT) \
KEYWORD("await", Token::AWAIT) \
KEYWORD_GROUP('b') \
KEYWORD("break", Token::BREAK) \
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("as", Token::AS) \
KEYWORD("and", Token::AND) \
KEYWORD("assert", Token::ASSERT) \
KEYWORD("await", Token::AWAIT) \
KEYWORD_GROUP('b') \
KEYWORD("break", Token::BREAK) \
KEYWORD("breakpoint", Token::BREAKPOINT) \
KEYWORD_GROUP('c') \
KEYWORD("class", Token::CLASS) \
KEYWORD_GROUP('c') \
KEYWORD("class", Token::CLASS) \
KEYWORD("class_name", Token::CLASS_NAME) \
KEYWORD("const", Token::TK_CONST) \
KEYWORD("continue", Token::CONTINUE) \
KEYWORD_GROUP('e') \
KEYWORD("elif", Token::ELIF) \
KEYWORD("else", Token::ELSE) \
KEYWORD("enum", Token::ENUM) \
KEYWORD("extends", Token::EXTENDS) \
KEYWORD_GROUP('f') \
KEYWORD("for", Token::FOR) \
KEYWORD("func", Token::FUNC) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("in", Token::TK_IN) \
KEYWORD("is", Token::IS) \
KEYWORD_GROUP('m') \
KEYWORD("match", Token::MATCH) \
KEYWORD_GROUP('n') \
KEYWORD("namespace", Token::NAMESPACE) \
KEYWORD("not", Token::NOT) \
KEYWORD_GROUP('o') \
KEYWORD("or", Token::OR) \
KEYWORD_GROUP('p') \
KEYWORD("pass", Token::PASS) \
KEYWORD("preload", Token::PRELOAD) \
KEYWORD_GROUP('r') \
KEYWORD("return", Token::RETURN) \
KEYWORD_GROUP('s') \
KEYWORD("self", Token::SELF) \
KEYWORD("signal", Token::SIGNAL) \
KEYWORD("static", Token::STATIC) \
KEYWORD("super", Token::SUPER) \
KEYWORD_GROUP('t') \
KEYWORD("trait", Token::TRAIT) \
KEYWORD_GROUP('v') \
KEYWORD("var", Token::VAR) \
KEYWORD("void", Token::TK_VOID) \
KEYWORD_GROUP('w') \
KEYWORD("while", Token::WHILE) \
KEYWORD("when", Token::WHEN) \
KEYWORD_GROUP('y') \
KEYWORD("yield", Token::YIELD) \
KEYWORD_GROUP('I') \
KEYWORD("INF", Token::CONST_INF) \
KEYWORD_GROUP('N') \
KEYWORD("NAN", Token::CONST_NAN) \
KEYWORD_GROUP('P') \
KEYWORD("PI", Token::CONST_PI) \
KEYWORD_GROUP('T') \
KEYWORD("const", Token::TK_CONST) \
KEYWORD("continue", Token::CONTINUE) \
KEYWORD_GROUP('e') \
KEYWORD("elif", Token::ELIF) \
KEYWORD("else", Token::ELSE) \
KEYWORD("enum", Token::ENUM) \
KEYWORD("extends", Token::EXTENDS) \
KEYWORD_GROUP('f') \
KEYWORD("for", Token::FOR) \
KEYWORD("func", Token::FUNC) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("in", Token::TK_IN) \
KEYWORD("is", Token::IS) \
KEYWORD_GROUP('m') \
KEYWORD("match", Token::MATCH) \
KEYWORD_GROUP('n') \
KEYWORD("namespace", Token::NAMESPACE) \
KEYWORD("not", Token::NOT) \
KEYWORD_GROUP('o') \
KEYWORD("or", Token::OR) \
KEYWORD_GROUP('p') \
KEYWORD("pass", Token::PASS) \
KEYWORD("preload", Token::PRELOAD) \
KEYWORD_GROUP('r') \
KEYWORD("return", Token::RETURN) \
KEYWORD_GROUP('s') \
KEYWORD("self", Token::SELF) \
KEYWORD("signal", Token::SIGNAL) \
KEYWORD("static", Token::STATIC) \
KEYWORD("super", Token::SUPER) \
KEYWORD_GROUP('t') \
KEYWORD("trait", Token::TRAIT) \
KEYWORD_GROUP('v') \
KEYWORD("var", Token::VAR) \
KEYWORD("void", Token::TK_VOID) \
KEYWORD_GROUP('w') \
KEYWORD("while", Token::WHILE) \
KEYWORD("when", Token::WHEN) \
KEYWORD_GROUP('y') \
KEYWORD("yield", Token::YIELD) \
KEYWORD_GROUP('I') \
KEYWORD("INF", Token::CONST_INF) \
KEYWORD_GROUP('N') \
KEYWORD("NAN", Token::CONST_NAN) \
KEYWORD_GROUP('P') \
KEYWORD("PI", Token::CONST_PI) \
KEYWORD_GROUP('T') \
KEYWORD("TAU", Token::CONST_TAU)
#define MIN_KEYWORD_LENGTH 2
@ -604,18 +607,18 @@ GDScriptTokenizer::Token GDScriptTokenizerText::potential_identifier() {
// Define some helper macros for the switch case.
#define KEYWORD_GROUP_CASE(char) \
break; \
break; \
case char:
#define KEYWORD(keyword, token_type) \
{ \
const int keyword_length = sizeof(keyword) - 1; \
static_assert(keyword_length <= MAX_KEYWORD_LENGTH, "There's a keyword longer than the defined maximum length"); \
#define KEYWORD(keyword, token_type) \
{ \
const int keyword_length = sizeof(keyword) - 1; \
static_assert(keyword_length <= MAX_KEYWORD_LENGTH, "There's a keyword longer than the defined maximum length"); \
static_assert(keyword_length >= MIN_KEYWORD_LENGTH, "There's a keyword shorter than the defined minimum length"); \
if (keyword_length == len && name == keyword) { \
Token kw = make_token(token_type); \
kw.literal = name; \
return kw; \
} \
if (keyword_length == len && name == keyword) { \
Token kw = make_token(token_type); \
kw.literal = name; \
return kw; \
} \
}
// Find if it's a keyword.

View file

@ -165,10 +165,8 @@ public:
Type type = EMPTY;
Variant literal;
int start_line = 0;
int start_column = 0;
int end_line = 0;
int end_column = 0;
int start_line = 0, end_line = 0, start_column = 0, end_column = 0;
int cursor_position = -1;
CursorPlace cursor_place = CURSOR_NONE;
String source;
@ -227,16 +225,13 @@ class GDScriptTokenizerText : public GDScriptTokenizer {
String source;
const char32_t *_source = nullptr;
const char32_t *_current = nullptr;
int line = 0;
int column = 0;
int cursor_line = -1;
int cursor_column = -1;
int line = -1, column = -1;
int cursor_line = -1, cursor_column = -1;
int tab_size = 4;
// Keep track of multichar tokens.
const char32_t *_start = nullptr;
int start_line = 0;
int start_column = 0;
int start_line = 0, start_column = 0;
// Info cache.
bool line_continuation = false; // Whether this line is a continuation of the previous, like when using '\'.

View file

@ -41,36 +41,36 @@
#ifdef DEBUG_ENABLED
#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count) \
if (unlikely(p_arg_count < m_min_count)) { \
*r_ret = Variant(); \
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
r_error.expected = m_min_count; \
return; \
} \
if (unlikely(p_arg_count > m_max_count)) { \
*r_ret = Variant(); \
#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count) \
if (unlikely(p_arg_count < m_min_count)) { \
*r_ret = Variant(); \
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
r_error.expected = m_min_count; \
return; \
} \
if (unlikely(p_arg_count > m_max_count)) { \
*r_ret = Variant(); \
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
r_error.expected = m_max_count; \
return; \
r_error.expected = m_max_count; \
return; \
}
#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type) \
#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type) \
if (unlikely(!Variant::can_convert_strict(p_args[m_arg]->get_type(), m_type))) { \
*r_ret = Variant(); \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg; \
r_error.expected = m_type; \
return; \
*r_ret = Variant(); \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg; \
r_error.expected = m_type; \
return; \
}
#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
if (unlikely(m_cond)) { \
*r_ret = m_msg; \
#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
if (unlikely(m_cond)) { \
*r_ret = m_msg; \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg; \
r_error.expected = m_type; \
return; \
r_error.argument = m_arg; \
r_error.expected = m_type; \
return; \
}
#else // !DEBUG_ENABLED
@ -81,20 +81,20 @@
#endif // DEBUG_ENABLED
#define VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
if (unlikely(m_cond)) { \
*r_ret = m_msg; \
#define VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
if (unlikely(m_cond)) { \
*r_ret = m_msg; \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg; \
r_error.expected = m_type; \
return; \
r_error.argument = m_arg; \
r_error.expected = m_type; \
return; \
}
#define GDFUNC_FAIL_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
*r_ret = m_msg; \
#define GDFUNC_FAIL_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
*r_ret = m_msg; \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; \
return; \
return; \
}
struct GDScriptUtilityFunctionsDefinitions {
@ -109,13 +109,13 @@ struct GDScriptUtilityFunctionsDefinitions {
Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
}
#endif // DISABLE_DEPRECATED
static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
DEBUG_VALIDATE_ARG_COUNT(1, 1);
DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING_NAME);
*r_ret = ClassDB::class_exists(*p_args[0]);
}
#endif // DISABLE_DEPRECATED
static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
DEBUG_VALIDATE_ARG_COUNT(1, 1);
@ -527,19 +527,19 @@ static void _register_function(const StringName &p_name, const MethodInfo &p_met
utility_function_name_table.push_back(p_name);
}
#define REGISTER_FUNC(m_func, m_is_const, m_return, m_args, m_is_vararg, m_default_args) \
{ \
String name(#m_func); \
if (name.begins_with("_")) { \
name = name.substr(1); \
} \
MethodInfo info = m_args; \
info.name = name; \
info.return_val = m_return; \
info.default_arguments = m_default_args; \
if (m_is_vararg) { \
info.flags |= METHOD_FLAG_VARARG; \
} \
#define REGISTER_FUNC(m_func, m_is_const, m_return, m_args, m_is_vararg, m_default_args) \
{ \
String name(#m_func); \
if (name.begins_with("_")) { \
name = name.substr(1); \
} \
MethodInfo info = m_args; \
info.name = name; \
info.return_val = m_return; \
info.default_arguments = m_default_args; \
if (m_is_vararg) { \
info.flags |= METHOD_FLAG_VARARG; \
} \
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
}
@ -571,8 +571,8 @@ void GDScriptUtilityFunctions::register_functions() {
/* clang-format off */
#ifndef DISABLE_DEPRECATED
REGISTER_FUNC( convert, true, RETVAR, ARGS( ARGVAR("what"), ARGTYPE("type") ), false, varray( ));
REGISTER_FUNC( type_exists, true, RET(BOOL), ARGS( ARG("type", STRING_NAME) ), false, varray( ));
#endif // DISABLE_DEPRECATED
REGISTER_FUNC( type_exists, true, RET(BOOL), ARGS( ARG("type", STRING_NAME) ), false, varray( ));
REGISTER_FUNC( _char, true, RET(STRING), ARGS( ARG("code", INT) ), false, varray( ));
REGISTER_FUNC( ord, true, RET(INT), ARGS( ARG("char", STRING) ), false, varray( ));
REGISTER_FUNC( range, false, RET(ARRAY), NOARGS, true, varray( ));

View file

@ -32,7 +32,6 @@
#include "gdscript_function.h"
#include "gdscript_lambda_callable.h"
#include "core/object/class_db.h"
#include "core/os/os.h"
#include "core/profiling/profiling.h"
@ -252,167 +251,167 @@ void (*type_init_function_table[])(Variant *) = {
};
#if defined(__GNUC__) || defined(__clang__)
#define OPCODES_TABLE \
static const void *switch_table_ops[] = { \
&&OPCODE_OPERATOR, \
&&OPCODE_OPERATOR_VALIDATED, \
&&OPCODE_TYPE_TEST_BUILTIN, \
&&OPCODE_TYPE_TEST_ARRAY, \
&&OPCODE_TYPE_TEST_DICTIONARY, \
&&OPCODE_TYPE_TEST_NATIVE, \
&&OPCODE_TYPE_TEST_SCRIPT, \
&&OPCODE_SET_KEYED, \
&&OPCODE_SET_KEYED_VALIDATED, \
&&OPCODE_SET_INDEXED_VALIDATED, \
&&OPCODE_GET_KEYED, \
&&OPCODE_GET_KEYED_VALIDATED, \
&&OPCODE_GET_INDEXED_VALIDATED, \
&&OPCODE_SET_NAMED, \
&&OPCODE_SET_NAMED_VALIDATED, \
&&OPCODE_GET_NAMED, \
&&OPCODE_GET_NAMED_VALIDATED, \
&&OPCODE_SET_MEMBER, \
&&OPCODE_GET_MEMBER, \
&&OPCODE_SET_STATIC_VARIABLE, \
&&OPCODE_GET_STATIC_VARIABLE, \
&&OPCODE_ASSIGN, \
&&OPCODE_ASSIGN_NULL, \
&&OPCODE_ASSIGN_TRUE, \
&&OPCODE_ASSIGN_FALSE, \
&&OPCODE_ASSIGN_TYPED_BUILTIN, \
&&OPCODE_ASSIGN_TYPED_ARRAY, \
&&OPCODE_ASSIGN_TYPED_DICTIONARY, \
&&OPCODE_ASSIGN_TYPED_NATIVE, \
&&OPCODE_ASSIGN_TYPED_SCRIPT, \
&&OPCODE_CAST_TO_BUILTIN, \
&&OPCODE_CAST_TO_NATIVE, \
&&OPCODE_CAST_TO_SCRIPT, \
&&OPCODE_CONSTRUCT, \
&&OPCODE_CONSTRUCT_VALIDATED, \
&&OPCODE_CONSTRUCT_ARRAY, \
&&OPCODE_CONSTRUCT_TYPED_ARRAY, \
&&OPCODE_CONSTRUCT_DICTIONARY, \
&&OPCODE_CONSTRUCT_TYPED_DICTIONARY, \
&&OPCODE_CALL, \
&&OPCODE_CALL_RETURN, \
&&OPCODE_CALL_ASYNC, \
&&OPCODE_CALL_UTILITY, \
&&OPCODE_CALL_UTILITY_VALIDATED, \
&&OPCODE_CALL_GDSCRIPT_UTILITY, \
&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
&&OPCODE_CALL_SELF_BASE, \
&&OPCODE_CALL_METHOD_BIND, \
&&OPCODE_CALL_METHOD_BIND_RET, \
&&OPCODE_CALL_BUILTIN_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN, \
#define OPCODES_TABLE \
static const void *switch_table_ops[] = { \
&&OPCODE_OPERATOR, \
&&OPCODE_OPERATOR_VALIDATED, \
&&OPCODE_TYPE_TEST_BUILTIN, \
&&OPCODE_TYPE_TEST_ARRAY, \
&&OPCODE_TYPE_TEST_DICTIONARY, \
&&OPCODE_TYPE_TEST_NATIVE, \
&&OPCODE_TYPE_TEST_SCRIPT, \
&&OPCODE_SET_KEYED, \
&&OPCODE_SET_KEYED_VALIDATED, \
&&OPCODE_SET_INDEXED_VALIDATED, \
&&OPCODE_GET_KEYED, \
&&OPCODE_GET_KEYED_VALIDATED, \
&&OPCODE_GET_INDEXED_VALIDATED, \
&&OPCODE_SET_NAMED, \
&&OPCODE_SET_NAMED_VALIDATED, \
&&OPCODE_GET_NAMED, \
&&OPCODE_GET_NAMED_VALIDATED, \
&&OPCODE_SET_MEMBER, \
&&OPCODE_GET_MEMBER, \
&&OPCODE_SET_STATIC_VARIABLE, \
&&OPCODE_GET_STATIC_VARIABLE, \
&&OPCODE_ASSIGN, \
&&OPCODE_ASSIGN_NULL, \
&&OPCODE_ASSIGN_TRUE, \
&&OPCODE_ASSIGN_FALSE, \
&&OPCODE_ASSIGN_TYPED_BUILTIN, \
&&OPCODE_ASSIGN_TYPED_ARRAY, \
&&OPCODE_ASSIGN_TYPED_DICTIONARY, \
&&OPCODE_ASSIGN_TYPED_NATIVE, \
&&OPCODE_ASSIGN_TYPED_SCRIPT, \
&&OPCODE_CAST_TO_BUILTIN, \
&&OPCODE_CAST_TO_NATIVE, \
&&OPCODE_CAST_TO_SCRIPT, \
&&OPCODE_CONSTRUCT, \
&&OPCODE_CONSTRUCT_VALIDATED, \
&&OPCODE_CONSTRUCT_ARRAY, \
&&OPCODE_CONSTRUCT_TYPED_ARRAY, \
&&OPCODE_CONSTRUCT_DICTIONARY, \
&&OPCODE_CONSTRUCT_TYPED_DICTIONARY, \
&&OPCODE_CALL, \
&&OPCODE_CALL_RETURN, \
&&OPCODE_CALL_ASYNC, \
&&OPCODE_CALL_UTILITY, \
&&OPCODE_CALL_UTILITY_VALIDATED, \
&&OPCODE_CALL_GDSCRIPT_UTILITY, \
&&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
&&OPCODE_CALL_SELF_BASE, \
&&OPCODE_CALL_METHOD_BIND, \
&&OPCODE_CALL_METHOD_BIND_RET, \
&&OPCODE_CALL_BUILTIN_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN, \
&&OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN, \
&&OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN, \
&&OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN, \
&&OPCODE_AWAIT, \
&&OPCODE_AWAIT_RESUME, \
&&OPCODE_CREATE_LAMBDA, \
&&OPCODE_CREATE_SELF_LAMBDA, \
&&OPCODE_JUMP, \
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
&&OPCODE_JUMP_TO_DEF_ARGUMENT, \
&&OPCODE_JUMP_IF_SHARED, \
&&OPCODE_RETURN, \
&&OPCODE_RETURN_TYPED_BUILTIN, \
&&OPCODE_RETURN_TYPED_ARRAY, \
&&OPCODE_RETURN_TYPED_DICTIONARY, \
&&OPCODE_RETURN_TYPED_NATIVE, \
&&OPCODE_RETURN_TYPED_SCRIPT, \
&&OPCODE_ITERATE_BEGIN, \
&&OPCODE_ITERATE_BEGIN_INT, \
&&OPCODE_ITERATE_BEGIN_FLOAT, \
&&OPCODE_ITERATE_BEGIN_VECTOR2, \
&&OPCODE_ITERATE_BEGIN_VECTOR2I, \
&&OPCODE_ITERATE_BEGIN_VECTOR3, \
&&OPCODE_ITERATE_BEGIN_VECTOR3I, \
&&OPCODE_ITERATE_BEGIN_STRING, \
&&OPCODE_ITERATE_BEGIN_DICTIONARY, \
&&OPCODE_ITERATE_BEGIN_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \
&&OPCODE_ITERATE_BEGIN_OBJECT, \
&&OPCODE_ITERATE_BEGIN_RANGE, \
&&OPCODE_ITERATE, \
&&OPCODE_ITERATE_INT, \
&&OPCODE_ITERATE_FLOAT, \
&&OPCODE_ITERATE_VECTOR2, \
&&OPCODE_ITERATE_VECTOR2I, \
&&OPCODE_ITERATE_VECTOR3, \
&&OPCODE_ITERATE_VECTOR3I, \
&&OPCODE_ITERATE_STRING, \
&&OPCODE_ITERATE_DICTIONARY, \
&&OPCODE_ITERATE_ARRAY, \
&&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
&&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
&&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
&&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
&&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
&&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
&&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
&&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
&&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \
&&OPCODE_ITERATE_OBJECT, \
&&OPCODE_ITERATE_RANGE, \
&&OPCODE_STORE_GLOBAL, \
&&OPCODE_STORE_NAMED_GLOBAL, \
&&OPCODE_TYPE_ADJUST_BOOL, \
&&OPCODE_TYPE_ADJUST_INT, \
&&OPCODE_TYPE_ADJUST_FLOAT, \
&&OPCODE_TYPE_ADJUST_STRING, \
&&OPCODE_TYPE_ADJUST_VECTOR2, \
&&OPCODE_TYPE_ADJUST_VECTOR2I, \
&&OPCODE_TYPE_ADJUST_RECT2, \
&&OPCODE_TYPE_ADJUST_RECT2I, \
&&OPCODE_TYPE_ADJUST_VECTOR3, \
&&OPCODE_TYPE_ADJUST_VECTOR3I, \
&&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
&&OPCODE_TYPE_ADJUST_VECTOR4, \
&&OPCODE_TYPE_ADJUST_VECTOR4I, \
&&OPCODE_TYPE_ADJUST_PLANE, \
&&OPCODE_TYPE_ADJUST_QUATERNION, \
&&OPCODE_TYPE_ADJUST_AABB, \
&&OPCODE_TYPE_ADJUST_BASIS, \
&&OPCODE_TYPE_ADJUST_TRANSFORM3D, \
&&OPCODE_TYPE_ADJUST_PROJECTION, \
&&OPCODE_TYPE_ADJUST_COLOR, \
&&OPCODE_TYPE_ADJUST_STRING_NAME, \
&&OPCODE_TYPE_ADJUST_NODE_PATH, \
&&OPCODE_TYPE_ADJUST_RID, \
&&OPCODE_TYPE_ADJUST_OBJECT, \
&&OPCODE_TYPE_ADJUST_CALLABLE, \
&&OPCODE_TYPE_ADJUST_SIGNAL, \
&&OPCODE_TYPE_ADJUST_DICTIONARY, \
&&OPCODE_TYPE_ADJUST_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY, \
&&OPCODE_ASSERT, \
&&OPCODE_BREAKPOINT, \
&&OPCODE_LINE, \
&&OPCODE_END \
}; \
&&OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN, \
&&OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN, \
&&OPCODE_AWAIT, \
&&OPCODE_AWAIT_RESUME, \
&&OPCODE_CREATE_LAMBDA, \
&&OPCODE_CREATE_SELF_LAMBDA, \
&&OPCODE_JUMP, \
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
&&OPCODE_JUMP_TO_DEF_ARGUMENT, \
&&OPCODE_JUMP_IF_SHARED, \
&&OPCODE_RETURN, \
&&OPCODE_RETURN_TYPED_BUILTIN, \
&&OPCODE_RETURN_TYPED_ARRAY, \
&&OPCODE_RETURN_TYPED_DICTIONARY, \
&&OPCODE_RETURN_TYPED_NATIVE, \
&&OPCODE_RETURN_TYPED_SCRIPT, \
&&OPCODE_ITERATE_BEGIN, \
&&OPCODE_ITERATE_BEGIN_INT, \
&&OPCODE_ITERATE_BEGIN_FLOAT, \
&&OPCODE_ITERATE_BEGIN_VECTOR2, \
&&OPCODE_ITERATE_BEGIN_VECTOR2I, \
&&OPCODE_ITERATE_BEGIN_VECTOR3, \
&&OPCODE_ITERATE_BEGIN_VECTOR3I, \
&&OPCODE_ITERATE_BEGIN_STRING, \
&&OPCODE_ITERATE_BEGIN_DICTIONARY, \
&&OPCODE_ITERATE_BEGIN_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \
&&OPCODE_ITERATE_BEGIN_OBJECT, \
&&OPCODE_ITERATE_BEGIN_RANGE, \
&&OPCODE_ITERATE, \
&&OPCODE_ITERATE_INT, \
&&OPCODE_ITERATE_FLOAT, \
&&OPCODE_ITERATE_VECTOR2, \
&&OPCODE_ITERATE_VECTOR2I, \
&&OPCODE_ITERATE_VECTOR3, \
&&OPCODE_ITERATE_VECTOR3I, \
&&OPCODE_ITERATE_STRING, \
&&OPCODE_ITERATE_DICTIONARY, \
&&OPCODE_ITERATE_ARRAY, \
&&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
&&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
&&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
&&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
&&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
&&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
&&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
&&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
&&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \
&&OPCODE_ITERATE_OBJECT, \
&&OPCODE_ITERATE_RANGE, \
&&OPCODE_STORE_GLOBAL, \
&&OPCODE_STORE_NAMED_GLOBAL, \
&&OPCODE_TYPE_ADJUST_BOOL, \
&&OPCODE_TYPE_ADJUST_INT, \
&&OPCODE_TYPE_ADJUST_FLOAT, \
&&OPCODE_TYPE_ADJUST_STRING, \
&&OPCODE_TYPE_ADJUST_VECTOR2, \
&&OPCODE_TYPE_ADJUST_VECTOR2I, \
&&OPCODE_TYPE_ADJUST_RECT2, \
&&OPCODE_TYPE_ADJUST_RECT2I, \
&&OPCODE_TYPE_ADJUST_VECTOR3, \
&&OPCODE_TYPE_ADJUST_VECTOR3I, \
&&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
&&OPCODE_TYPE_ADJUST_VECTOR4, \
&&OPCODE_TYPE_ADJUST_VECTOR4I, \
&&OPCODE_TYPE_ADJUST_PLANE, \
&&OPCODE_TYPE_ADJUST_QUATERNION, \
&&OPCODE_TYPE_ADJUST_AABB, \
&&OPCODE_TYPE_ADJUST_BASIS, \
&&OPCODE_TYPE_ADJUST_TRANSFORM3D, \
&&OPCODE_TYPE_ADJUST_PROJECTION, \
&&OPCODE_TYPE_ADJUST_COLOR, \
&&OPCODE_TYPE_ADJUST_STRING_NAME, \
&&OPCODE_TYPE_ADJUST_NODE_PATH, \
&&OPCODE_TYPE_ADJUST_RID, \
&&OPCODE_TYPE_ADJUST_OBJECT, \
&&OPCODE_TYPE_ADJUST_CALLABLE, \
&&OPCODE_TYPE_ADJUST_SIGNAL, \
&&OPCODE_TYPE_ADJUST_DICTIONARY, \
&&OPCODE_TYPE_ADJUST_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, \
&&OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY, \
&&OPCODE_ASSERT, \
&&OPCODE_BREAKPOINT, \
&&OPCODE_LINE, \
&&OPCODE_END \
}; \
static_assert(std_size(switch_table_ops) == (OPCODE_END + 1), "Opcodes in jump table aren't the same as opcodes in enum.");
#define OPCODE(m_op) \
@ -425,7 +424,7 @@ void (*type_init_function_table[])(Variant *) = {
#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
#ifdef DEBUG_ENABLED
#define DISPATCH_OPCODE \
#define DISPATCH_OPCODE \
last_opcode = _code_ptr[ip]; \
goto *switch_table_ops[last_opcode]
#else // !DEBUG_ENABLED
@ -443,7 +442,7 @@ void (*type_init_function_table[])(Variant *) = {
#define DISPATCH_OPCODE continue
#ifdef _MSC_VER
#define OPCODE_SWITCH(m_test) \
#define OPCODE_SWITCH(m_test) \
__assume(m_test <= OPCODE_END); \
switch (m_test)
#else // !_MSC_VER
@ -543,9 +542,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int line = _initial_line;
if (p_state) {
// Use existing (supplied) state (awaited).
//use existing (supplied) state (awaited)
stack = (Variant *)p_state->stack.ptr();
instruction_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; // `ptr()` to avoid bounds check.
instruction_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
line = p_state->line;
ip = p_state->ip;
alloca_size = p_state->stack.size();
@ -553,11 +552,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
p_instance = p_state->instance;
defarg = p_state->defarg;
// Responsibility for the stack is moved from `GDScriptFunctionState` to this method. So, we reset `p_state->stack_size`
// to prevent `GDScriptFunctionState::_clear_stack()` from clearing the stack again.
// NOTE: Strictly speaking, ownership doesn't move. However, we can be sure that `p_state->stack` won't be cleared
// before the current call completes, and that `p_state` won't be resumed again.
p_state->stack_size = 0;
} else {
if (p_argcount != _argument_count) {
if (p_argcount > _argument_count) {
@ -648,8 +642,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
instruction_args = nullptr;
}
for (const Pair<int, Variant::Type> &E : temporary_slots) {
type_init_function_table[E.second](&stack[E.first]);
for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
type_init_function_table[E.value](&stack[E.key]);
}
}
@ -660,11 +654,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&stack[ADDR_STACK_SELF], Variant);
script = _script;
}
// We must call a `Variant` constructor here, as accessing an object without doing so is undefined behavior.
memnew_placement(&stack[ADDR_STACK_CLASS], Variant);
VariantInternal::object_assign_without_ref_unsafe(&stack[ADDR_STACK_CLASS], script);
memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
memnew_placement(&stack[ADDR_STACK_NIL], Variant);
String err_text;
@ -673,61 +663,61 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GDScriptLanguage::get_singleton()->enter_function(&call_level, p_instance, this, stack, &ip, &line);
#ifdef DEBUG_ENABLED
#define GD_ERR_BREAK(m_cond) \
{ \
if (unlikely(m_cond)) { \
#define GD_ERR_BREAK(m_cond) \
{ \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
OPCODE_BREAK; \
} \
OPCODE_BREAK; \
} \
}
#define CHECK_SPACE(m_space) \
GD_ERR_BREAK((ip + m_space) > _code_size)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
{ \
int address = _code_ptr[ip + 1 + (m_code_ofs)]; \
int address_type = (address & ADDR_TYPE_MASK) >> ADDR_BITS; \
if (unlikely(address_type < 0 || address_type >= ADDR_TYPE_MAX)) { \
err_text = "Bad address type."; \
OPCODE_BREAK; \
} \
int address_index = address & ADDR_MASK; \
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
{ \
int address = _code_ptr[ip + 1 + (m_code_ofs)]; \
int address_type = (address & ADDR_TYPE_MASK) >> ADDR_BITS; \
if (unlikely(address_type < 0 || address_type >= ADDR_TYPE_MAX)) { \
err_text = "Bad address type."; \
OPCODE_BREAK; \
} \
int address_index = address & ADDR_MASK; \
if (unlikely(address_index < 0 || address_index >= variant_address_limits[address_type])) { \
if (address_type == ADDR_TYPE_MEMBER && !p_instance) { \
err_text = "Cannot access member without instance."; \
} else { \
err_text = "Bad address index."; \
} \
OPCODE_BREAK; \
} \
m_v = &variant_addresses[address_type][address_index]; \
if (unlikely(!m_v)) \
OPCODE_BREAK; \
if (address_type == ADDR_TYPE_MEMBER && !p_instance) { \
err_text = "Cannot access member without instance."; \
} else { \
err_text = "Bad address index."; \
} \
OPCODE_BREAK; \
} \
m_v = &variant_addresses[address_type][address_index]; \
if (unlikely(!m_v)) \
OPCODE_BREAK; \
}
#else // !DEBUG_ENABLED
#define GD_ERR_BREAK(m_cond)
#define CHECK_SPACE(m_space)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
{ \
int address = _code_ptr[ip + 1 + (m_code_ofs)]; \
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
{ \
int address = _code_ptr[ip + 1 + (m_code_ofs)]; \
m_v = &variant_addresses[(address & ADDR_TYPE_MASK) >> ADDR_BITS][address & ADDR_MASK]; \
if (unlikely(!m_v)) \
OPCODE_BREAK; \
if (unlikely(!m_v)) \
OPCODE_BREAK; \
}
#endif // DEBUG_ENABLED
#define LOAD_INSTRUCTION_ARGS \
int instr_arg_count = _code_ptr[ip + 1]; \
#define LOAD_INSTRUCTION_ARGS \
int instr_arg_count = _code_ptr[ip + 1]; \
for (int i = 0; i < instr_arg_count; i++) { \
GET_VARIANT_PTR(v, i + 1); \
instruction_args[i] = v; \
} \
GET_VARIANT_PTR(v, i + 1); \
instruction_args[i] = v; \
} \
ip += 1; // Offset to skip instruction argcount.
#define GET_INSTRUCTION_ARG(m_v, m_idx) \
@ -2822,8 +2812,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Variant::construct(ret_type, retvalue, const_cast<const Variant **>(&r), 1, ce);
} else {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)",
_get_var_type(r), Variant::get_type_name(ret_type));
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)",
Variant::get_type_name(r->get_type()), Variant::get_type_name(ret_type));
#endif // DEBUG_ENABLED
// Construct a base type anyway so type constraints are met.
@ -2852,9 +2842,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (r->get_type() != Variant::ARRAY) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Array[%s]".)",
_get_var_type(r), Variant::get_type_name(builtin_type));
#endif // DEBUG_ENABLED
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "Array[%s]".)",
Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type));
#endif
OPCODE_BREAK;
}
@ -2862,7 +2852,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Array[%s]".)",
err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)",
_get_var_type(r), _get_element_type(builtin_type, native_type, *script_type));
#endif // DEBUG_ENABLED
OPCODE_BREAK;
@ -2894,7 +2884,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (r->get_type() != Variant::DICTIONARY) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Dictionary[%s, %s]".)",
err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Dictionary[%s, %s]".)",
_get_var_type(r), _get_element_type(key_builtin_type, key_native_type, *key_script_type),
_get_element_type(value_builtin_type, value_native_type, *value_script_type));
#endif // DEBUG_ENABLED
@ -2906,7 +2896,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (dictionary->get_typed_key_builtin() != ((uint32_t)key_builtin_type) || dictionary->get_typed_key_class_name() != key_native_type || dictionary->get_typed_key_script() != *key_script_type ||
dictionary->get_typed_value_builtin() != ((uint32_t)value_builtin_type) || dictionary->get_typed_value_class_name() != value_native_type || dictionary->get_typed_value_script() != *value_script_type) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Dictionary[%s, %s]".)",
err_text = vformat(R"(Trying to return a dictionary of type "%s" where expected return type is "Dictionary[%s, %s]".)",
_get_var_type(r), _get_element_type(key_builtin_type, key_native_type, *key_script_type),
_get_element_type(value_builtin_type, value_native_type, *value_script_type));
#endif // DEBUG_ENABLED
@ -2930,10 +2920,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!nc);
if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)",
_get_var_type(r), nc->get_name());
#endif // DEBUG_ENABLED
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)",
Variant::get_type_name(r->get_type()), nc->get_name());
OPCODE_BREAK;
}
@ -2950,8 +2938,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif // DEBUG_ENABLED
if (ret_obj && !ClassDB::is_parent_class(ret_obj->get_class_name(), nc->get_name())) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)",
_get_var_type(r), nc->get_name());
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)",
ret_obj->get_class_name(), nc->get_name());
#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
@ -2973,8 +2961,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)",
_get_var_type(r), GDScript::debug_get_script_name(Ref<Script>(base_type)));
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)",
Variant::get_type_name(r->get_type()), GDScript::debug_get_script_name(Ref<Script>(base_type)));
#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
@ -2995,8 +2983,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
ScriptInstance *ret_inst = ret_obj->get_script_instance();
if (!ret_inst) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)",
_get_var_type(r), GDScript::debug_get_script_name(Ref<GDScript>(base_type)));
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)",
ret_obj->get_class_name(), GDScript::debug_get_script_name(Ref<GDScript>(base_type)));
#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
@ -3014,8 +3002,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (!valid) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)",
_get_var_type(r), GDScript::debug_get_script_name(Ref<GDScript>(base_type)));
err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)",
GDScript::debug_get_script_name(ret_obj->get_script_instance()->get_script()), GDScript::debug_get_script_name(Ref<GDScript>(base_type)));
#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
@ -3313,25 +3301,25 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
#define OPCODE_ITERATE_BEGIN_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_var_ret_type, m_ret_type, m_ret_get_func) \
OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) { \
CHECK_SPACE(8); \
GET_VARIANT_PTR(counter, 0); \
GET_VARIANT_PTR(container, 1); \
Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \
VariantInternal::initialize(counter, Variant::INT); \
*VariantInternal::get_int(counter) = 0; \
if (!array->is_empty()) { \
GET_VARIANT_PTR(iterator, 2); \
VariantInternal::initialize(iterator, Variant::m_var_ret_type); \
m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \
*it = array->get(0); \
ip += 5; \
} else { \
int jumpto = _code_ptr[ip + 4]; \
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); \
ip = jumpto; \
} \
} \
OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) { \
CHECK_SPACE(8); \
GET_VARIANT_PTR(counter, 0); \
GET_VARIANT_PTR(container, 1); \
Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \
VariantInternal::initialize(counter, Variant::INT); \
*VariantInternal::get_int(counter) = 0; \
if (!array->is_empty()) { \
GET_VARIANT_PTR(iterator, 2); \
VariantInternal::initialize(iterator, Variant::m_var_ret_type); \
m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \
*it = array->get(0); \
ip += 5; \
} else { \
int jumpto = _code_ptr[ip + 4]; \
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); \
ip = jumpto; \
} \
} \
DISPATCH_OPCODE
OPCODE_ITERATE_BEGIN_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, INT, int64_t, get_int);
@ -3681,24 +3669,24 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func) \
OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) { \
CHECK_SPACE(4); \
GET_VARIANT_PTR(counter, 0); \
GET_VARIANT_PTR(container, 1); \
#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func) \
OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) { \
CHECK_SPACE(4); \
GET_VARIANT_PTR(counter, 0); \
GET_VARIANT_PTR(container, 1); \
const Vector<m_elem_type> *array = VariantInternal::m_get_func((const Variant *)container); \
int64_t *idx = VariantInternal::get_int(counter); \
(*idx)++; \
if (*idx >= array->size()) { \
int jumpto = _code_ptr[ip + 4]; \
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); \
ip = jumpto; \
} else { \
GET_VARIANT_PTR(iterator, 2); \
*VariantInternal::m_ret_get_func(iterator) = array->get(*idx); \
ip += 5; \
} \
} \
int64_t *idx = VariantInternal::get_int(counter); \
(*idx)++; \
if (*idx >= array->size()) { \
int jumpto = _code_ptr[ip + 4]; \
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); \
ip = jumpto; \
} else { \
GET_VARIANT_PTR(iterator, 2); \
*VariantInternal::m_ret_get_func(iterator) = array->get(*idx); \
ip += 5; \
} \
} \
DISPATCH_OPCODE
OPCODE_ITERATE_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, get_int);
@ -3822,13 +3810,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
#define OPCODE_TYPE_ADJUST(m_v_type, m_c_type) \
OPCODE(OPCODE_TYPE_ADJUST_##m_v_type) { \
CHECK_SPACE(2); \
GET_VARIANT_PTR(arg, 0); \
#define OPCODE_TYPE_ADJUST(m_v_type, m_c_type) \
OPCODE(OPCODE_TYPE_ADJUST_##m_v_type) { \
CHECK_SPACE(2); \
GET_VARIANT_PTR(arg, 0); \
VariantTypeAdjust<m_c_type>::adjust(arg); \
ip += 2; \
} \
ip += 2; \
} \
DISPATCH_OPCODE
OPCODE_TYPE_ADJUST(BOOL, bool);
@ -4011,14 +3999,15 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// This ensures the call stack can be properly shown when using `await`, showing what resumed the function.
if (!p_state || awaited) {
GDScriptLanguage::get_singleton()->exit_function();
// Free stack, except reserved addresses.
for (int i = FIXED_ADDRESSES_MAX; i < _stack_size; i++) {
stack[i].~Variant();
}
}
// We deliberately avoid calling the destructor for `ADDR_STACK_CLASS`, since we initialized it
// without incrementing any reference count that it might have.
stack[ADDR_STACK_SELF].~Variant();
stack[ADDR_STACK_NIL].~Variant();
for (int i = FIXED_ADDRESSES_MAX; i < _stack_size; i++) {
// Always free reserved addresses, since they are never copied.
for (int i = 0; i < FIXED_ADDRESSES_MAX; i++) {
stack[i].~Variant();
}

View file

@ -30,7 +30,6 @@
#include "gdscript_warning.h"
#include "core/object/property_info.h"
#include "core/variant/variant.h"
#ifdef DEBUG_ENABLED

View file

@ -32,11 +32,10 @@
#ifdef DEBUG_ENABLED
#include "core/object/object.h"
#include "core/string/ustring.h"
#include "core/templates/vector.h"
class String;
struct PropertyInfo;
class GDScriptWarning {
public:
enum WarnLevel {
@ -159,10 +158,7 @@ public:
static_assert(std_size(default_warning_levels) == WARNING_MAX, "Amount of default levels does not match the amount of warnings.");
Code code = WARNING_MAX;
int start_line = 0;
int start_column = 0;
int end_line = 0;
int end_column = 0;
int start_line = -1, end_line = -1;
Vector<String> symbols;
String get_name() const;

View file

@ -32,11 +32,10 @@
#include "../gdscript.h"
#include "../gdscript_analyzer.h"
#include "editor/settings/editor_settings.h"
#include "gdscript_language_protocol.h"
#include "gdscript_workspace.h"
#include "editor/settings/editor_settings.h"
int get_indent_size() {
if (EditorSettings::get_singleton()) {
return EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
@ -136,12 +135,18 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostic.severity = LSP::DiagnosticSeverity::Error;
diagnostic.message = error.message;
diagnostic.source = "gdscript";
GodotRange godot_range(
GodotPosition(error.start_line, error.start_column),
GodotPosition(error.end_line, error.end_column));
diagnostic.range = godot_range.to_lsp(get_lines());
diagnostic.code = -1;
LSP::Range range;
LSP::Position pos;
const PackedStringArray line_array = get_lines();
int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, line_array.size() - 1);
const String &line_text = line_array[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
range.end = range.start;
range.end.character = line_text.strip_edges(false).length();
diagnostic.range = range;
diagnostics.push_back(diagnostic);
}
@ -151,12 +156,17 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostic.severity = LSP::DiagnosticSeverity::Warning;
diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message();
diagnostic.source = "gdscript";
GodotRange godot_range(
GodotPosition(warning.start_line, warning.start_column),
GodotPosition(warning.end_line, warning.end_column));
diagnostic.range = godot_range.to_lsp(get_lines());
diagnostic.code = warning.code;
LSP::Range range;
LSP::Position pos;
int line = LINE_NUMBER_TO_INDEX(warning.start_line);
const String &line_text = get_lines()[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
range.end = pos;
range.end.character = line_text.strip_edges(false).length();
diagnostic.range = range;
diagnostics.push_back(diagnostic);
}
}
@ -860,6 +870,30 @@ const List<LSP::DocumentLink> &ExtendGDScriptParser::get_document_links() const
return document_links;
}
const Array &ExtendGDScriptParser::get_member_completions() {
if (member_completions.is_empty()) {
for (const KeyValue<String, const LSP::DocumentSymbol *> &E : members) {
const LSP::DocumentSymbol *symbol = E.value;
LSP::CompletionItem item = symbol->make_completion_item();
item.data = JOIN_SYMBOLS(path, E.key);
member_completions.push_back(item.to_json());
}
for (const KeyValue<String, ClassMembers> &E : inner_classes) {
const ClassMembers *inner_class = &E.value;
for (const KeyValue<String, const LSP::DocumentSymbol *> &F : *inner_class) {
const LSP::DocumentSymbol *symbol = F.value;
LSP::CompletionItem item = symbol->make_completion_item();
item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(E.key, F.key));
member_completions.push_back(item.to_json());
}
}
}
return member_completions;
}
Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::FunctionNode *p_func) const {
ERR_FAIL_NULL_V(p_func, Dictionary());
Dictionary func;

View file

@ -42,6 +42,14 @@
#define COLUMN_NUMBER_TO_INDEX(p_column) ((p_column) - 1)
#endif
#ifndef SYMBOL_SEPARATOR
#define SYMBOL_SEPARATOR "::"
#endif
#ifndef JOIN_SYMBOLS
#define JOIN_SYMBOLS(p_path, name) ((p_path) + SYMBOL_SEPARATOR + (name))
#endif
typedef HashMap<String, const LSP::DocumentSymbol *> ClassMembers;
/**
@ -126,6 +134,8 @@ class ExtendGDScriptParser : public GDScriptParser {
const LSP::DocumentSymbol *search_symbol_defined_at_line(int p_line, const LSP::DocumentSymbol &p_parent, const String &p_symbol_name = "") const;
Array member_completions;
public:
_FORCE_INLINE_ const String &get_path() const { return path; }
_FORCE_INLINE_ const Vector<String> &get_lines() const { return lines; }

View file

@ -30,27 +30,23 @@
#include "gdscript_language_protocol.h"
#include "godot_lsp.h"
#include "core/config/project_settings.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "core/os/os.h"
#include "editor/doc/doc_tools.h"
#include "editor/doc/editor_help.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/settings/editor_settings.h"
#include "modules/gdscript/language_server/godot_lsp.h"
#define LSP_CLIENT_V(m_ret_val) \
#define LSP_CLIENT_V(m_ret_val) \
ERR_FAIL_COND_V(latest_client_id == LSP_NO_CLIENT, m_ret_val); \
ERR_FAIL_COND_V(!clients.has(latest_client_id), m_ret_val); \
Ref<LSPeer> client = clients.get(latest_client_id); \
ERR_FAIL_COND_V(!clients.has(latest_client_id), m_ret_val); \
Ref<LSPeer> client = clients.get(latest_client_id); \
ERR_FAIL_COND_V(!client.is_valid(), m_ret_val);
#define LSP_CLIENT \
ERR_FAIL_COND(latest_client_id == LSP_NO_CLIENT); \
ERR_FAIL_COND(!clients.has(latest_client_id)); \
#define LSP_CLIENT \
ERR_FAIL_COND(latest_client_id == LSP_NO_CLIENT); \
ERR_FAIL_COND(!clients.has(latest_client_id)); \
Ref<LSPeer> client = clients.get(latest_client_id); \
ERR_FAIL_COND(!client.is_valid());
@ -152,9 +148,6 @@ Error GDScriptLanguageProtocol::on_client_connected() {
void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {
clients.erase(p_client_id);
if (clients.is_empty()) {
scene_cache.clear();
}
EditorNode::get_log()->add_message("[LSP] Disconnected", EditorLog::MSG_TYPE_EDITOR);
}
@ -178,18 +171,15 @@ String GDScriptLanguageProtocol::format_output(const String &p_text) {
}
void GDScriptLanguageProtocol::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
ClassDB::bind_method(D_METHOD("is_initialized"), &GDScriptLanguageProtocol::is_initialized);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("initialize", "params"), &GDScriptLanguageProtocol::initialize);
ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized);
ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
ClassDB::bind_method(D_METHOD("on_client_disconnected", "client_id"), &GDScriptLanguageProtocol::on_client_disconnected);
ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
ClassDB::bind_method(D_METHOD("notify_client", "method", "params", "client_id"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
#endif // !DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
ClassDB::bind_method(D_METHOD("is_initialized"), &GDScriptLanguageProtocol::is_initialized);
}
Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
@ -249,6 +239,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
if (!_initialized) {
workspace->initialize();
text_document->initialize();
_initialized = true;
}
@ -279,8 +270,6 @@ void GDScriptLanguageProtocol::poll(int p_limit_usec) {
on_client_connected();
}
scene_cache.poll();
HashMap<int, Ref<LSPeer>>::Iterator E = clients.begin();
while (E != clients.end()) {
Ref<LSPeer> peer = E->value;
@ -327,7 +316,6 @@ void GDScriptLanguageProtocol::stop() {
peer->connection->disconnect_from_host();
}
scene_cache.clear();
server->stop();
}
@ -460,8 +448,6 @@ void GDScriptLanguageProtocol::lsp_did_open(const Dictionary &p_params) {
client->managed_files[path] = document;
client->parse_script(path);
scene_cache.request_load(path);
}
void GDScriptLanguageProtocol::lsp_did_change(const Dictionary &p_params) {
@ -507,8 +493,6 @@ void GDScriptLanguageProtocol::lsp_did_close(const Dictionary &p_params) {
/// A close notification requires a previous open notification to be sent.
ERR_FAIL_COND_MSG(!was_opened, "LSP: Client is closing file without opening it.");
scene_cache.unload(path);
}
void GDScriptLanguageProtocol::resolve_related_symbols(const LSP::TextDocumentPositionParams &p_doc_pos, List<const LSP::DocumentSymbol *> &r_list) {
@ -574,7 +558,6 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
SET_DOCUMENT_METHOD(didSave);
SET_DOCUMENT_METHOD(documentSymbol);
SET_DOCUMENT_METHOD(documentHighlight);
SET_DOCUMENT_METHOD(completion);
SET_DOCUMENT_METHOD(rename);
SET_DOCUMENT_METHOD(prepareRename);

View file

@ -32,7 +32,6 @@
#include "gdscript_text_document.h"
#include "gdscript_workspace.h"
#include "scene_cache.h"
#include "core/io/stream_peer_tcp.h"
#include "core/io/tcp_server.h"
@ -47,7 +46,9 @@
class GDScriptLanguageProtocol : public JSONRPC {
GDCLASS(GDScriptLanguageProtocol, JSONRPC)
#ifdef TESTS_ENABLED
friend class TestGDScriptLanguageProtocolInitializer;
#endif
private:
struct LSPeer : RefCounted {
@ -91,7 +92,6 @@ private:
static GDScriptLanguageProtocol *singleton;
HashMap<int, Ref<LSPeer>> clients;
SceneCache scene_cache;
Ref<TCPServer> server;
int latest_client_id = LSP_NO_CLIENT;
int next_client_id = 0;
@ -119,8 +119,6 @@ public:
_FORCE_INLINE_ static GDScriptLanguageProtocol *get_singleton() { return singleton; }
_FORCE_INLINE_ Ref<GDScriptWorkspace> get_workspace() { return workspace; }
_FORCE_INLINE_ Ref<GDScriptTextDocument> get_text_document() { return text_document; }
_FORCE_INLINE_ SceneCache *get_scene_cache() { return &scene_cache; }
_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
void poll(int p_limit_usec);

View file

@ -30,8 +30,6 @@
#include "gdscript_language_server.h"
#include "gdscript_language_protocol.h"
#include "core/os/os.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
@ -40,6 +38,14 @@
int GDScriptLanguageServer::port_override = -1;
GDScriptLanguageServer::GDScriptLanguageServer() {
// TODO: Move to editor_settings.cpp
_EDITOR_DEF("network/language_server/remote_host", host);
_EDITOR_DEF("network/language_server/remote_port", port);
_EDITOR_DEF("network/language_server/enable_smart_resolve", true);
_EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false);
_EDITOR_DEF("network/language_server/use_thread", use_thread);
_EDITOR_DEF("network/language_server/poll_limit_usec", poll_limit_usec);
set_process_internal(true);
}
@ -56,7 +62,7 @@ void GDScriptLanguageServer::_notification(int p_what) {
}
if (started && !use_thread) {
GDScriptLanguageProtocol::get_singleton()->poll(poll_limit_usec);
protocol.poll(poll_limit_usec);
}
} break;
@ -82,7 +88,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {
GDScriptLanguageServer *self = static_cast<GDScriptLanguageServer *>(p_userdata);
while (self->thread_running) {
// Poll 20 times per second
GDScriptLanguageProtocol::get_singleton()->poll(self->poll_limit_usec);
self->protocol.poll(self->poll_limit_usec);
OS::get_singleton()->delay_usec(50000);
}
}
@ -92,18 +98,15 @@ void GDScriptLanguageServer::start() {
port = (GDScriptLanguageServer::port_override > -1) ? GDScriptLanguageServer::port_override : (int)_EDITOR_GET("network/language_server/remote_port");
use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
poll_limit_usec = (int)_EDITOR_GET("network/language_server/poll_limit_usec");
const Error status = GDScriptLanguageProtocol::get_singleton()->start(port, IPAddress(host));
if (status != OK) {
EditorNode::get_log()->add_message("--- Failed to start GDScript language server on port " + itos(port) + ": " + error_names[status] + " ---", EditorLog::MSG_TYPE_EDITOR);
return;
if (protocol.start(port, IPAddress(host)) == OK) {
EditorNode::get_log()->add_message("--- GDScript language server started on port " + itos(port) + " ---", EditorLog::MSG_TYPE_EDITOR);
if (use_thread) {
thread_running = true;
thread.start(GDScriptLanguageServer::thread_main, this);
}
set_process_internal(!use_thread);
started = true;
}
EditorNode::get_log()->add_message("--- GDScript language server started on port " + itos(port) + " ---", EditorLog::MSG_TYPE_EDITOR);
if (use_thread) {
thread_running = true;
thread.start(GDScriptLanguageServer::thread_main, this);
}
set_process_internal(!use_thread);
started = true;
}
void GDScriptLanguageServer::stop() {
@ -112,7 +115,7 @@ void GDScriptLanguageServer::stop() {
thread_running = false;
thread.wait_to_finish();
}
GDScriptLanguageProtocol::get_singleton()->stop();
protocol.stop();
started = false;
EditorNode::get_log()->add_message("--- GDScript language server stopped ---", EditorLog::MSG_TYPE_EDITOR);
}

View file

@ -30,23 +30,24 @@
#pragma once
#include "gdscript_language_protocol.h"
#include "editor/plugins/editor_plugin.h"
class GDScriptLanguageServer : public EditorPlugin {
GDCLASS(GDScriptLanguageServer, EditorPlugin);
GDScriptLanguageProtocol protocol;
Thread thread;
bool thread_running = false;
// There is no notification when the editor is initialized. We need to poll till we attempted to start the server.
bool start_attempted = false;
bool started = false;
// Defaults located in editor_settings.cpp
bool use_thread = false;
String host;
int port = 0;
int poll_limit_usec = 0;
String host = "127.0.0.1";
int port = 6005;
int poll_limit_usec = 100000;
static void thread_main(void *p_userdata);
private:

View file

@ -34,38 +34,32 @@
#include "gdscript_extend_parser.h"
#include "gdscript_language_protocol.h"
#include "core/io/resource_loader.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/script/script_text_editor.h"
#include "editor/settings/editor_settings.h"
#include "servers/display/display_server.h"
void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor", "symbol_id"), &GDScriptTextDocument::show_native_symbol_in_editor);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("didOpen", "params"), &GDScriptTextDocument::didOpen);
ClassDB::bind_method(D_METHOD("didClose", "params"), &GDScriptTextDocument::didClose);
ClassDB::bind_method(D_METHOD("didChange", "params"), &GDScriptTextDocument::didChange);
ClassDB::bind_method(D_METHOD("willSaveWaitUntil", "params"), &GDScriptTextDocument::willSaveWaitUntil);
ClassDB::bind_method(D_METHOD("didSave", "params"), &GDScriptTextDocument::didSave);
ClassDB::bind_method(D_METHOD("nativeSymbol", "params"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol", "params"), &GDScriptTextDocument::documentSymbol);
ClassDB::bind_method(D_METHOD("completion", "params"), &GDScriptTextDocument::completion);
ClassDB::bind_method(D_METHOD("resolve", "params"), &GDScriptTextDocument::resolve);
ClassDB::bind_method(D_METHOD("rename", "params"), &GDScriptTextDocument::rename);
ClassDB::bind_method(D_METHOD("prepareRename", "params"), &GDScriptTextDocument::prepareRename);
ClassDB::bind_method(D_METHOD("references", "params"), &GDScriptTextDocument::references);
ClassDB::bind_method(D_METHOD("foldingRange", "params"), &GDScriptTextDocument::foldingRange);
ClassDB::bind_method(D_METHOD("codeLens", "params"), &GDScriptTextDocument::codeLens);
ClassDB::bind_method(D_METHOD("documentLink", "params"), &GDScriptTextDocument::documentLink);
ClassDB::bind_method(D_METHOD("colorPresentation", "params"), &GDScriptTextDocument::colorPresentation);
ClassDB::bind_method(D_METHOD("hover", "params"), &GDScriptTextDocument::hover);
ClassDB::bind_method(D_METHOD("definition", "params"), &GDScriptTextDocument::definition);
ClassDB::bind_method(D_METHOD("declaration", "params"), &GDScriptTextDocument::declaration);
ClassDB::bind_method(D_METHOD("signatureHelp", "params"), &GDScriptTextDocument::signatureHelp);
#endif // !DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose);
ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
ClassDB::bind_method(D_METHOD("willSaveWaitUntil"), &GDScriptTextDocument::willSaveWaitUntil);
ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave);
ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol);
ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion);
ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve);
ClassDB::bind_method(D_METHOD("rename"), &GDScriptTextDocument::rename);
ClassDB::bind_method(D_METHOD("prepareRename"), &GDScriptTextDocument::prepareRename);
ClassDB::bind_method(D_METHOD("references"), &GDScriptTextDocument::references);
ClassDB::bind_method(D_METHOD("foldingRange"), &GDScriptTextDocument::foldingRange);
ClassDB::bind_method(D_METHOD("codeLens"), &GDScriptTextDocument::codeLens);
ClassDB::bind_method(D_METHOD("documentLink"), &GDScriptTextDocument::documentLink);
ClassDB::bind_method(D_METHOD("colorPresentation"), &GDScriptTextDocument::colorPresentation);
ClassDB::bind_method(D_METHOD("hover"), &GDScriptTextDocument::hover);
ClassDB::bind_method(D_METHOD("definition"), &GDScriptTextDocument::definition);
ClassDB::bind_method(D_METHOD("declaration"), &GDScriptTextDocument::declaration);
ClassDB::bind_method(D_METHOD("signatureHelp"), &GDScriptTextDocument::signatureHelp);
ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor"), &GDScriptTextDocument::show_native_symbol_in_editor);
}
void GDScriptTextDocument::didOpen(const Variant &p_param) {
@ -128,6 +122,21 @@ void GDScriptTextDocument::notify_client_show_symbol(const LSP::DocumentSymbol *
GDScriptLanguageProtocol::get_singleton()->notify_client("gdscript/show_native_symbol", symbol->to_json(true));
}
void GDScriptTextDocument::initialize() {
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
for (const KeyValue<StringName, ClassMembers> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members) {
const ClassMembers &members = E.value;
for (const KeyValue<String, const LSP::DocumentSymbol *> &F : members) {
const LSP::DocumentSymbol *symbol = members.get(F.key);
LSP::CompletionItem item = symbol->make_completion_item();
item.data = JOIN_SYMBOLS(String(E.key), F.key);
native_member_completions.push_back(item.to_json());
}
}
}
}
Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) {
Variant ret;
@ -156,25 +165,6 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
return arr;
}
Array GDScriptTextDocument::documentHighlight(const Dictionary &p_params) {
Array arr;
LSP::TextDocumentPositionParams params;
params.load(p_params);
const LSP::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params);
if (symbol) {
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(params.textDocument.uri);
Vector<LSP::Location> usages = GDScriptLanguageProtocol::get_singleton()->get_workspace()->find_usages_in_file(*symbol, path);
for (const LSP::Location &usage : usages) {
LSP::DocumentHighlight highlight;
highlight.range = usage.range;
arr.push_back(highlight.to_json());
}
}
return arr;
}
Array GDScriptTextDocument::completion(const Dictionary &p_params) {
Array arr;
@ -299,6 +289,33 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
if (data.get_type() == Variant::DICTIONARY) {
params.load(p_params["data"]);
symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params, item.label, item.kind == LSP::CompletionItemKind::Method || item.kind == LSP::CompletionItemKind::Function);
} else if (data.is_string()) {
String query = data;
Vector<String> param_symbols = query.split(SYMBOL_SEPARATOR, false);
if (param_symbols.size() >= 2) {
StringName class_name = param_symbols[0];
const String &member_name = param_symbols[param_symbols.size() - 1];
String inner_class_name;
if (param_symbols.size() >= 3) {
inner_class_name = param_symbols[1];
}
if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members.getptr(class_name)) {
if (const LSP::DocumentSymbol *const *member = members->getptr(member_name)) {
symbol = *member;
}
}
if (!symbol) {
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(class_name);
if (parser) {
symbol = parser->get_member_symbol(member_name, inner_class_name);
}
}
}
}
if (symbol) {

View file

@ -44,6 +44,8 @@ protected:
Ref<FileAccess> file_checker;
Array native_member_completions;
private:
Array find_symbols(const LSP::TextDocumentPositionParams &p_location, List<const LSP::DocumentSymbol *> &r_list);
void notify_client_show_symbol(const LSP::DocumentSymbol *symbol);
@ -60,7 +62,6 @@ public:
Variant nativeSymbol(const Dictionary &p_params);
Array documentSymbol(const Dictionary &p_params);
Array documentHighlight(const Dictionary &p_params);
Array completion(const Dictionary &p_params);
Dictionary resolve(const Dictionary &p_params);
Dictionary rename(const Dictionary &p_params);
@ -75,5 +76,7 @@ public:
Variant declaration(const Dictionary &p_params);
Variant signatureHelp(const Dictionary &p_params);
void initialize();
GDScriptTextDocument();
};

View file

@ -35,26 +35,25 @@
#include "gdscript_language_protocol.h"
#include "core/config/project_settings.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include "editor/doc/doc_tools.h"
#include "editor/doc/editor_help.h"
#include "editor/editor_node.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/settings/editor_settings.h"
#include "scene/resources/packed_scene.h"
void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("apply_new_signal", "obj", "function", "args"), &GDScriptWorkspace::apply_new_signal);
ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal);
ClassDB::bind_method(D_METHOD("get_file_path", "uri"), &GDScriptWorkspace::get_file_path);
ClassDB::bind_method(D_METHOD("get_file_uri", "path"), &GDScriptWorkspace::get_file_uri);
ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics);
ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("didDeleteFiles", "params"), &GDScriptWorkspace::didDeleteFiles);
ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::didDeleteFiles);
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script);
ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics);
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_script);
#endif
}
@ -593,6 +592,51 @@ void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
GDScriptLanguageProtocol::get_singleton()->notify_client("textDocument/publishDiagnostics", params);
}
void GDScriptWorkspace::_get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners) {
if (!efsd) {
return;
}
for (int i = 0; i < efsd->get_subdir_count(); i++) {
_get_owners(efsd->get_subdir(i), p_path, owners);
}
for (int i = 0; i < efsd->get_file_count(); i++) {
Vector<String> deps = efsd->get_file_deps(i);
bool found = false;
for (int j = 0; j < deps.size(); j++) {
if (deps[j] == p_path) {
found = true;
break;
}
}
if (!found) {
continue;
}
owners.push_back(efsd->get_file_path(i));
}
}
Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) {
Node *owner_scene_node = nullptr;
List<String> owners;
_get_owners(EditorFileSystem::get_singleton()->get_filesystem(), p_path, owners);
for (const String &owner : owners) {
NodePath owner_path = owner;
Ref<Resource> owner_res = ResourceLoader::load(String(owner_path));
if (Object::cast_to<PackedScene>(owner_res.ptr())) {
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
owner_scene_node = owner_packed_scene->instantiate();
break;
}
}
return owner_scene_node;
}
void GDScriptWorkspace::completion(const LSP::CompletionParams &p_params, List<ScriptLanguage::CodeCompletionOption> *r_options) {
String path = get_file_path(p_params.textDocument.uri);
String call_hint;
@ -600,7 +644,7 @@ void GDScriptWorkspace::completion(const LSP::CompletionParams &p_params, List<S
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
if (parser) {
Node *owner_scene_node = GDScriptLanguageProtocol::get_singleton()->get_scene_cache()->get(path);
Node *owner_scene_node = _get_owner_scene_node(path);
Array stack;
Node *current = nullptr;
@ -626,6 +670,9 @@ void GDScriptWorkspace::completion(const LSP::CompletionParams &p_params, List<S
String code = parser->get_text_for_completion(p_params.position);
GDScriptLanguage::get_singleton()->complete_code(code, path, current, r_options, forced, call_hint);
if (owner_scene_node) {
memdelete(owner_scene_node);
}
}
}

View file

@ -30,18 +30,22 @@
#pragma once
#include "core/error/error_macros.h"
#include "gdscript_extend_parser.h"
#include "godot_lsp.h"
#include "core/error/error_macros.h"
#include "core/variant/variant.h"
#include "editor/file_system/editor_file_system.h"
class GDScriptWorkspace : public RefCounted {
GDCLASS(GDScriptWorkspace, RefCounted);
private:
void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners);
Node *_get_owner_scene_node(String p_path);
#ifndef DISABLE_DEPRECATED
void didDeleteFiles(const Dictionary &p_params) {}
void didDeleteFiles() {}
Error parse_script(const String &p_path, const String &p_content) {
WARN_DEPRECATED;
return Error::FAILED;

View file

@ -34,15 +34,6 @@
#include "core/object/class_db.h"
#include "core/templates/list.h"
// Enable additional LSP related logging.
//#define DEBUG_LSP
#ifdef DEBUG_LSP
#define LOG_LSP(...) print_line("[ LSP -", __FILE__, ":", __LINE__, "-", __func__, "] -", ##__VA_ARGS__)
#else
#define LOG_LSP(...)
#endif
namespace LSP {
typedef String DocumentUri;
@ -1077,12 +1068,8 @@ struct CompletionItem {
dict["insertText"] = insertText;
}
if (resolved) {
if (!detail.is_empty()) {
dict["detail"] = detail;
}
if (!documentation.value.is_empty()) {
dict["documentation"] = documentation.to_json();
}
dict["detail"] = detail;
dict["documentation"] = documentation.to_json();
dict["deprecated"] = deprecated;
dict["preselect"] = preselect;
if (!sortText.is_empty()) {
@ -1298,6 +1285,48 @@ struct DocumentSymbol {
}
return markdown;
}
_FORCE_INLINE_ CompletionItem make_completion_item(bool resolved = false) const {
LSP::CompletionItem item;
item.label = name;
if (resolved) {
item.documentation = render();
}
switch (kind) {
case LSP::SymbolKind::Enum:
item.kind = LSP::CompletionItemKind::Enum;
break;
case LSP::SymbolKind::Class:
item.kind = LSP::CompletionItemKind::Class;
break;
case LSP::SymbolKind::Property:
item.kind = LSP::CompletionItemKind::Property;
break;
case LSP::SymbolKind::Method:
case LSP::SymbolKind::Function:
item.kind = LSP::CompletionItemKind::Method;
break;
case LSP::SymbolKind::Event:
item.kind = LSP::CompletionItemKind::Event;
break;
case LSP::SymbolKind::Constant:
item.kind = LSP::CompletionItemKind::Constant;
break;
case LSP::SymbolKind::Variable:
item.kind = LSP::CompletionItemKind::Variable;
break;
case LSP::SymbolKind::File:
item.kind = LSP::CompletionItemKind::File;
break;
default:
item.kind = LSP::CompletionItemKind::Text;
break;
}
return item;
}
};
struct ApplyWorkspaceEditParams {
@ -1737,7 +1766,7 @@ struct ServerCapabilities {
/**
* The server provides document highlight support.
*/
bool documentHighlightProvider = true;
bool documentHighlightProvider = false;
/**
* The server provides document symbol support.
@ -2094,26 +2123,4 @@ static String marked_documentation(const String &p_bbcode) {
}
return markdown;
}
/**
* A document highlight is a range inside a text document which deserves
* special attention. Usually a document highlight is visualized by changing
* the background color of its range.
*/
struct DocumentHighlight {
/**
* The range this highlight applies to.
*/
Range range;
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["range"] = range.to_json();
return dict;
}
_FORCE_INLINE_ void load(const Dictionary &p_params) {
range.load(p_params["range"]);
}
};
} // namespace LSP

Some files were not shown because too many files have changed in this diff Show more