diff --git a/BeefBuild/BeefProj.toml b/BeefBuild/BeefProj.toml index 115aad91..bbb65e5a 100644 --- a/BeefBuild/BeefProj.toml +++ b/BeefBuild/BeefProj.toml @@ -7,7 +7,7 @@ StartupObject = "BeefBuild.Program" [Platform.Windows] Description = "BeefBuild" -FileVersion = "0.43.1" +FileVersion = "0.43.2" [Configs.Debug.Win32] TargetName = "" diff --git a/BeefLibs/Beefy2D/BeefProj.toml b/BeefLibs/Beefy2D/BeefProj.toml index cb191cbf..e9270495 100644 --- a/BeefLibs/Beefy2D/BeefProj.toml +++ b/BeefLibs/Beefy2D/BeefProj.toml @@ -15,6 +15,9 @@ CLibType = "Static" BeefLibType = "Static" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64_d.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64_d.pdb\")"] +[Configs.Debug.Linux64] +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/libBeefySysLib_d.so\")"] + [Configs.Release.Win32] OtherLinkFlags = "" PreprocessorMacros = ["RELEASE", "BF32"] @@ -23,6 +26,9 @@ PreprocessorMacros = ["RELEASE", "BF32"] OtherLinkFlags = "$(LinkFlags) $(ProjectDir)/dist/BeefySysLib64.lib" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.pdb\")"] +[Configs.Release.Linux64] +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/libBeefySysLib.so\")"] + [Configs.Paranoid.Win32] CLibType = "Static" BeefLibType = "Static" @@ -38,3 +44,15 @@ BeefLibType = "Static" [Configs.Test.Win64] CLibType = "Static" BeefLibType = "Static" + +[Configs.DebugOpt.Win32] +OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/BeefySysLib32_d.lib\"" +BeefLibType = "Static" +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib32_d.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib32_d.pdb\")"] +PreprocessorMacros = ["DEBUG", "BF32"] + +[Configs.DebugOpt.Win64] +OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/BeefySysLib64.lib\"" +CLibType = "Static" +BeefLibType = "Static" +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.pdb\")"] diff --git a/BeefLibs/Beefy2D/src/BFApp.bf b/BeefLibs/Beefy2D/src/BFApp.bf index 34dc1b5a..2fd528e6 100644 --- a/BeefLibs/Beefy2D/src/BFApp.bf +++ b/BeefLibs/Beefy2D/src/BFApp.bf @@ -727,7 +727,7 @@ namespace Beefy 0.10f, 0.00f, 1.05f, 0, 0, 0, 0, 1);*/ - mGraphics.SetShaderConstantData(0, &mColorMatrix.ValueRef, mColorMatrixDataDef); + mGraphics.SetVertexShaderConstantData(0, &mColorMatrix.ValueRef, mColorMatrixDataDef); } window.Draw(mGraphics); window.PostDraw(mGraphics); diff --git a/BeefLibs/Beefy2D/src/Utils.bf b/BeefLibs/Beefy2D/src/Utils.bf index 06fd2d66..299a1ac0 100644 --- a/BeefLibs/Beefy2D/src/Utils.bf +++ b/BeefLibs/Beefy2D/src/Utils.bf @@ -295,11 +295,13 @@ namespace Beefy public static Result WriteTextFile(StringView path, StringView text) { - var stream = scope FileStream(); - if (stream.Create(path) case .Err) - { + var stream = scope UnbufferedFileStream(); + if (stream.Open(path, .OpenOrCreate, .Write) case .Err) return .Err; - } + + if (stream.SetLength(text.Length) case .Err) + return .Err; + if (stream.WriteStrUnsized(text) case .Err) return .Err; diff --git a/BeefLibs/Beefy2D/src/geom/BoundingBox.bf b/BeefLibs/Beefy2D/src/geom/BoundingBox.bf new file mode 100644 index 00000000..63214a50 --- /dev/null +++ b/BeefLibs/Beefy2D/src/geom/BoundingBox.bf @@ -0,0 +1,14 @@ +// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA), +// released under the Microsoft Public License + +namespace Beefy.geom +{ + class BoundingBox + { + public Vector3 Min; + public Vector3 Max; + public const int CornerCount = 8; + + + } +} diff --git a/BeefLibs/Beefy2D/src/geom/Matrix4.bf b/BeefLibs/Beefy2D/src/geom/Matrix4.bf index c2c5cbb2..7a1a2294 100644 --- a/BeefLibs/Beefy2D/src/geom/Matrix4.bf +++ b/BeefLibs/Beefy2D/src/geom/Matrix4.bf @@ -55,6 +55,19 @@ namespace Beefy.geom this.m33 = m33; } + public static Matrix4 CreateFromColumnMajor( + float m00, float m10, float m20, float m30, + float m01, float m11, float m21, float m31, + float m02, float m12, float m22, float m32, + float m03, float m13, float m23, float m33) + { + return .( + m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33); + } + public static Matrix4 CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance) { Matrix4 matrix; @@ -190,25 +203,23 @@ namespace Beefy.geom public static Matrix4 Multiply(Matrix4 m1, Matrix4 m2) { Matrix4 r; - r.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20 + m1.m03 * m2.m30; - r.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21 + m1.m03 * m2.m31; - r.m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22 + m1.m03 * m2.m32; - r.m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03 * m2.m33; - r.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20 + m1.m13 * m2.m30; - r.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13 * m2.m31; - r.m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13 * m2.m32; - r.m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13 * m2.m33; - - r.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20 + m1.m23 * m2.m30; - r.m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23 * m2.m31; - r.m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23 * m2.m32; - r.m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 * m2.m33; - - r.m30 = m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20 + m1.m33 * m2.m30; - r.m31 = m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33 * m2.m31; - r.m32 = m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33 * m2.m32; - r.m33 = m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33 * m2.m33; + r.m00 = (((m1.m00 * m2.m00) + (m1.m10 * m2.m01)) + (m1.m20 * m2.m02)) + (m1.m30 * m2.m03); + r.m10 = (((m1.m00 * m2.m10) + (m1.m10 * m2.m11)) + (m1.m20 * m2.m12)) + (m1.m30 * m2.m13); + r.m20 = (((m1.m00 * m2.m20) + (m1.m10 * m2.m21)) + (m1.m20 * m2.m22)) + (m1.m30 * m2.m23); + r.m30 = (((m1.m00 * m2.m30) + (m1.m10 * m2.m31)) + (m1.m20 * m2.m32)) + (m1.m30 * m2.m33); + r.m01 = (((m1.m01 * m2.m00) + (m1.m11 * m2.m01)) + (m1.m21 * m2.m02)) + (m1.m31 * m2.m03); + r.m11 = (((m1.m01 * m2.m10) + (m1.m11 * m2.m11)) + (m1.m21 * m2.m12)) + (m1.m31 * m2.m13); + r.m21 = (((m1.m01 * m2.m20) + (m1.m11 * m2.m21)) + (m1.m21 * m2.m22)) + (m1.m31 * m2.m23); + r.m31 = (((m1.m01 * m2.m30) + (m1.m11 * m2.m31)) + (m1.m21 * m2.m32)) + (m1.m31 * m2.m33); + r.m02 = (((m1.m02 * m2.m00) + (m1.m12 * m2.m01)) + (m1.m22 * m2.m02)) + (m1.m32 * m2.m03); + r.m12 = (((m1.m02 * m2.m10) + (m1.m12 * m2.m11)) + (m1.m22 * m2.m12)) + (m1.m32 * m2.m13); + r.m22 = (((m1.m02 * m2.m20) + (m1.m12 * m2.m21)) + (m1.m22 * m2.m22)) + (m1.m32 * m2.m23); + r.m32 = (((m1.m02 * m2.m30) + (m1.m12 * m2.m31)) + (m1.m22 * m2.m32)) + (m1.m32 * m2.m33); + r.m03 = (((m1.m03 * m2.m00) + (m1.m13 * m2.m01)) + (m1.m23 * m2.m02)) + (m1.m33 * m2.m03); + r.m13 = (((m1.m03 * m2.m10) + (m1.m13 * m2.m11)) + (m1.m23 * m2.m12)) + (m1.m33 * m2.m13); + r.m23 = (((m1.m03 * m2.m20) + (m1.m13 * m2.m21)) + (m1.m23 * m2.m22)) + (m1.m33 * m2.m23); + r.m33 = (((m1.m03 * m2.m30) + (m1.m13 * m2.m31)) + (m1.m23 * m2.m32)) + (m1.m33 * m2.m33); return r; } @@ -489,5 +500,730 @@ namespace Beefy.geom r20, r21, r22, r23, 0, 0, 0, 1); } + + public static void Invert(Matrix4 matrix, out Matrix4 result) + { + float num1 = matrix.m00; + float num2 = matrix.m10; + float num3 = matrix.m20; + float num4 = matrix.m30; + float num5 = matrix.m01; + float num6 = matrix.m11; + float num7 = matrix.m21; + float num8 = matrix.m31; + float num9 = matrix.m02; + float num10 = matrix.m12; + float num11 = matrix.m22; + float num12 = matrix.m32; + float num13 = matrix.m03; + float num14 = matrix.m13; + float num15 = matrix.m23; + float num16 = matrix.m33; + float num17 = (float) ((double) num11 * (double) num16 - (double) num12 * (double) num15); + float num18 = (float) ((double) num10 * (double) num16 - (double) num12 * (double) num14); + float num19 = (float) ((double) num10 * (double) num15 - (double) num11 * (double) num14); + float num20 = (float) ((double) num9 * (double) num16 - (double) num12 * (double) num13); + float num21 = (float) ((double) num9 * (double) num15 - (double) num11 * (double) num13); + float num22 = (float) ((double) num9 * (double) num14 - (double) num10 * (double) num13); + float num23 = (float) ((double) num6 * (double) num17 - (double) num7 * (double) num18 + (double) num8 * (double) num19); + float num24 = (float) -((double) num5 * (double) num17 - (double) num7 * (double) num20 + (double) num8 * (double) num21); + float num25 = (float) ((double) num5 * (double) num18 - (double) num6 * (double) num20 + (double) num8 * (double) num22); + float num26 = (float) -((double) num5 * (double) num19 - (double) num6 * (double) num21 + (double) num7 * (double) num22); + float num27 = (float) (1.0 / ((double) num1 * (double) num23 + (double) num2 * (double) num24 + (double) num3 * (double) num25 + (double) num4 * (double) num26)); + + result.m00 = num23 * num27; + result.m01 = num24 * num27; + result.m02 = num25 * num27; + result.m03 = num26 * num27; + result.m10 = (float)(-((double) num2 * (double) num17 - (double) num3 * (double) num18 + (double) num4 * (double) num19) * num27); + result.m11 = (float) ((double) num1 * (double) num17 - (double) num3 * (double) num20 + (double) num4 * (double) num21) * num27; + result.m12 = (float)(-((double) num1 * (double) num18 - (double) num2 * (double) num20 + (double) num4 * (double) num22) * num27); + result.m13 = (float) ((double) num1 * (double) num19 - (double) num2 * (double) num21 + (double) num3 * (double) num22) * num27; + float num28 = (float) ((double) num7 * (double) num16 - (double) num8 * (double) num15); + float num29 = (float) ((double) num6 * (double) num16 - (double) num8 * (double) num14); + float num30 = (float) ((double) num6 * (double) num15 - (double) num7 * (double) num14); + float num31 = (float) ((double) num5 * (double) num16 - (double) num8 * (double) num13); + float num32 = (float) ((double) num5 * (double) num15 - (double) num7 * (double) num13); + float num33 = (float) ((double) num5 * (double) num14 - (double) num6 * (double) num13); + result.m20 = (float) ((double) num2 * (double) num28 - (double) num3 * (double) num29 + (double) num4 * (double) num30) * num27; + result.m21 = (float)(-((double) num1 * (double) num28 - (double) num3 * (double) num31 + (double) num4 * (double) num32) * num27); + result.m22 = (float) ((double) num1 * (double) num29 - (double) num2 * (double) num31 + (double) num4 * (double) num33) * num27; + result.m23 = (float)(-((double) num1 * (double) num30 - (double) num2 * (double) num32 + (double) num3 * (double) num33) * num27); + float num34 = (float) ((double) num7 * (double) num12 - (double) num8 * (double) num11); + float num35 = (float) ((double) num6 * (double) num12 - (double) num8 * (double) num10); + float num36 = (float) ((double) num6 * (double) num11 - (double) num7 * (double) num10); + float num37 = (float) ((double) num5 * (double) num12 - (double) num8 * (double) num9); + float num38 = (float) ((double) num5 * (double) num11 - (double) num7 * (double) num9); + float num39 = (float) ((double) num5 * (double) num10 - (double) num6 * (double) num9); + result.m30 = (float)(-((double) num2 * (double) num34 - (double) num3 * (double) num35 + (double) num4 * (double) num36) * num27); + result.m31 = (float) ((double) num1 * (double) num34 - (double) num3 * (double) num37 + (double) num4 * (double) num38) * num27; + result.m32 = (float)(-((double) num1 * (double) num35 - (double) num2 * (double) num37 + (double) num4 * (double) num39) * num27); + result.m33 = (float) ((double) num1 * (double) num36 - (double) num2 * (double) num38 + (double) num3 * (double) num39) * num27; + + /* + + + /// + // Use Laplace expansion theorem to calculate the inverse of a 4x4 matrix + // + // 1. Calculate the 2x2 determinants needed the 4x4 determinant based on the 2x2 determinants + // 3. Create the adjugate matrix, which satisfies: A * adj(A) = det(A) * I + // 4. Divide adjugate matrix with the determinant to find the inverse + + float det1, det2, det3, det4, det5, det6, det7, det8, det9, det10, det11, det12; + float detMatrix; + FindDeterminants(ref matrix, out detMatrix, out det1, out det2, out det3, out det4, out det5, out det6, + out det7, out det8, out det9, out det10, out det11, out det12); + + float invDetMatrix = 1f / detMatrix; + + Matrix ret; // Allow for matrix and result to point to the same structure + + ret.M11 = (matrix.M22*det12 - matrix.M23*det11 + matrix.M24*det10) * invDetMatrix; + ret.M12 = (-matrix.M12*det12 + matrix.M13*det11 - matrix.M14*det10) * invDetMatrix; + ret.M13 = (matrix.M42*det6 - matrix.M43*det5 + matrix.M44*det4) * invDetMatrix; + ret.M14 = (-matrix.M32*det6 + matrix.M33*det5 - matrix.M34*det4) * invDetMatrix; + ret.M21 = (-matrix.M21*det12 + matrix.M23*det9 - matrix.M24*det8) * invDetMatrix; + ret.M22 = (matrix.M11*det12 - matrix.M13*det9 + matrix.M14*det8) * invDetMatrix; + ret.M23 = (-matrix.M41*det6 + matrix.M43*det3 - matrix.M44*det2) * invDetMatrix; + ret.M24 = (matrix.M31*det6 - matrix.M33*det3 + matrix.M34*det2) * invDetMatrix; + ret.M31 = (matrix.M21*det11 - matrix.M22*det9 + matrix.M24*det7) * invDetMatrix; + ret.M32 = (-matrix.M11*det11 + matrix.M12*det9 - matrix.M14*det7) * invDetMatrix; + ret.M33 = (matrix.M41*det5 - matrix.M42*det3 + matrix.M44*det1) * invDetMatrix; + ret.M34 = (-matrix.M31*det5 + matrix.M32*det3 - matrix.M34*det1) * invDetMatrix; + ret.M41 = (-matrix.M21*det10 + matrix.M22*det8 - matrix.M23*det7) * invDetMatrix; + ret.M42 = (matrix.M11*det10 - matrix.M12*det8 + matrix.M13*det7) * invDetMatrix; + ret.M43 = (-matrix.M41*det4 + matrix.M42*det2 - matrix.M43*det1) * invDetMatrix; + ret.M44 = (matrix.M31*det4 - matrix.M32*det2 + matrix.M33*det1) * invDetMatrix; + + result = ret; + */ + } + + public static Matrix4 Invert(Matrix4 matrix) + { + Invert(matrix, var outMatrix); + return outMatrix; + } + + /// + /// Decomposes this matrix to translation, rotation and scale elements. Returns true if matrix can be decomposed; false otherwise. + /// + /// Scale vector as an output parameter. + /// Rotation quaternion as an output parameter. + /// Translation vector as an output parameter. + /// true if matrix can be decomposed; false otherwise. + public bool Decompose( + out Vector3 scale, + out Quaternion rotation, + out Vector3 translation + ) { + translation.mX = m03; + translation.mY = m13; + translation.mZ = m23; + + float xs = (Math.Sign(m00 * m10 * m20 * m30) < 0) ? -1 : 1; + float ys = (Math.Sign(m01 * m11 * m21 * m31) < 0) ? -1 : 1; + float zs = (Math.Sign(m02 * m12 * m22 * m32) < 0) ? -1 : 1; + + scale.mX = xs * (float) Math.Sqrt(m00 * m00 + m10 * m10 + m20 * m20); + scale.mY = ys * (float) Math.Sqrt(m01 * m01 + m11 * m11 + m21 * m21); + scale.mZ = zs * (float) Math.Sqrt(m02 * m02 + m12 * m12 + m22 * m22); + + if (Math.WithinEpsilon(scale.mX, 0.0f) || + Math.WithinEpsilon(scale.mY, 0.0f) || + Math.WithinEpsilon(scale.mZ, 0.0f) ) + { + rotation = Quaternion.Identity; + return false; + } + + Matrix4 m1 = Matrix4.CreateFromColumnMajor( + m00 / scale.mX, m10 / scale.mX, m20 / scale.mX, 0, + m01 / scale.mY, m11 / scale.mY, m21 / scale.mY, 0, + m02 / scale.mZ, m12 / scale.mZ, m22 / scale.mZ, 0, + 0, 0, 0, 1 + ); + + rotation = Quaternion.CreateFromRotationMatrix(m1); + return true; + } + + /// + /// Returns a determinant of this matrix. + /// + /// Determinant of this matrix + /// See more about determinant here - http://en.wikipedia.org/wiki/Determinant. + /// + public float Determinant() + { + float num18 = (m22 * m33) - (m32 * m23); + float num17 = (m12 * m33) - (m32 * m13); + float num16 = (m12 * m23) - (m22 * m13); + float num15 = (m02 * m33) - (m32 * m03); + float num14 = (m02 * m23) - (m22 * m03); + float num13 = (m02 * m13) - (m12 * m03); + return ( + ( + ( + (m00 * (((m11 * num18) - (m21 * num17)) + (m31 * num16))) - + (m10 * (((m01 * num18) - (m21 * num15)) + (m31 * num14))) + ) + (m20 * (((m01 * num17) - (m11 * num15)) + (m31 * num13))) + ) - (m30 * (((m01 * num16) - (m11 * num14)) + (m21 * num13))) + ); + } + + /// + /// Creates a new matrix for spherical billboarding that rotates around specified object position. + /// + /// Position of billboard object. It will rotate around that vector. + /// The camera position. + /// The camera up vector. + /// Optional camera forward vector. + /// The matrix for spherical billboarding. + public static Matrix4 CreateBillboard( + Vector3 objectPosition, + Vector3 cameraPosition, + Vector3 cameraUpVector, + Nullable cameraForwardVector + ) { + Matrix4 result; + + // Delegate to the other overload of the function to do the work + CreateBillboard( + objectPosition, + cameraPosition, + cameraUpVector, + cameraForwardVector, + out result + ); + + return result; + } + + /// + /// Creates a new matrix for spherical billboarding that rotates around specified object position. + /// + /// Position of billboard object. It will rotate around that vector. + /// The camera position. + /// The camera up vector. + /// Optional camera forward vector. + /// The matrix for spherical billboarding as an output parameter. + public static void CreateBillboard( + Vector3 objectPosition, + Vector3 cameraPosition, + Vector3 cameraUpVector, + Vector3? cameraForwardVector, + out Matrix4 result + ) { + Vector3 vector; + Vector3 vector2; + Vector3 vector3; + vector.mX = objectPosition.mX - cameraPosition.mX; + vector.mY = objectPosition.mY - cameraPosition.mY; + vector.mZ = objectPosition.mZ - cameraPosition.mZ; + float num = vector.LengthSquared; + if (num < 0.0001f) + { + vector = cameraForwardVector.HasValue ? + -cameraForwardVector.Value : + Vector3.Forward; + } + else + { + vector *= (float) (1f / ((float) Math.Sqrt((double) num))); + } + vector3 = Vector3.Cross(cameraUpVector, vector); + vector3.Normalize(); + vector2 = Vector3.Cross(vector, vector3); + result.m00 = vector3.mX; + result.m10 = vector3.mY; + result.m20 = vector3.mZ; + result.m30 = 0; + result.m01 = vector2.mX; + result.m11 = vector2.mY; + result.m21 = vector2.mZ; + result.m31 = 0; + result.m02 = vector.mX; + result.m12 = vector.mY; + result.m22 = vector.mZ; + result.m32 = 0; + result.m03 = objectPosition.mX; + result.m13 = objectPosition.mY; + result.m23 = objectPosition.mZ; + result.m33 = 1; + } + + /// + /// Creates a new matrix for cylindrical billboarding that rotates around specified axis. + /// + /// Object position the billboard will rotate around. + /// Camera position. + /// Axis of billboard for rotation. + /// Optional camera forward vector. + /// Optional object forward vector. + /// The matrix for cylindrical billboarding. + public static Matrix4 CreateConstrainedBillboard( + Vector3 objectPosition, + Vector3 cameraPosition, + Vector3 rotateAxis, + Nullable cameraForwardVector, + Nullable objectForwardVector + ) { + Matrix4 result; + CreateConstrainedBillboard( + objectPosition, + cameraPosition, + rotateAxis, + cameraForwardVector, + objectForwardVector, + out result + ); + return result; + } + + /// + /// Creates a new matrix for cylindrical billboarding that rotates around specified axis. + /// + /// Object position the billboard will rotate around. + /// Camera position. + /// Axis of billboard for rotation. + /// Optional camera forward vector. + /// Optional object forward vector. + /// The matrix for cylindrical billboarding as an output parameter. + public static void CreateConstrainedBillboard( + Vector3 objectPosition, + Vector3 cameraPosition, + Vector3 rotateAxis, + Vector3? cameraForwardVector, + Vector3? objectForwardVector, + out Matrix4 result + ) { + float num; + Vector3 vector; + Vector3 vector2; + Vector3 vector3; + vector2.mX = objectPosition.mX - cameraPosition.mX; + vector2.mY = objectPosition.mY - cameraPosition.mY; + vector2.mZ = objectPosition.mZ - cameraPosition.mZ; + float num2 = vector2.LengthSquared; + if (num2 < 0.0001f) + { + vector2 = cameraForwardVector.HasValue ? + -cameraForwardVector.Value : + Vector3.Forward; + } + else + { + vector2 *= (float) (1f / ((float) Math.Sqrt((double) num2))); + } + Vector3 vector4 = rotateAxis; + num = Vector3.Dot(rotateAxis, vector2); + if (Math.Abs(num) > 0.9982547f) + { + if (objectForwardVector.HasValue) + { + vector = objectForwardVector.Value; + num = Vector3.Dot(rotateAxis, vector); + if (Math.Abs(num) > 0.9982547f) + { + num = ( + (rotateAxis.mX * Vector3.Forward.mX) + + (rotateAxis.mY * Vector3.Forward.mY) + ) + (rotateAxis.mZ * Vector3.Forward.mZ); + vector = (Math.Abs(num) > 0.9982547f) ? + Vector3.Right : + Vector3.Forward; + } + } + else + { + num = ( + (rotateAxis.mX * Vector3.Forward.mX) + + (rotateAxis.mY * Vector3.Forward.mY) + ) + (rotateAxis.mZ * Vector3.Forward.mZ); + vector = (Math.Abs(num) > 0.9982547f) ? + Vector3.Right : + Vector3.Forward; + } + vector3 = Vector3.Cross(rotateAxis, vector); + vector3.Normalize(); + vector = Vector3.Cross(vector3, rotateAxis); + vector.Normalize(); + } + else + { + vector3 = Vector3.Cross(rotateAxis, vector2); + vector3.Normalize(); + vector = Vector3.Cross(vector3, vector4); + vector.Normalize(); + } + + result.m00 = vector3.mX; + result.m10 = vector3.mY; + result.m20 = vector3.mZ; + result.m30 = 0; + result.m01 = vector4.mX; + result.m11 = vector4.mY; + result.m21 = vector4.mZ; + result.m31 = 0; + result.m02 = vector.mX; + result.m12 = vector.mY; + result.m22 = vector.mZ; + result.m32 = 0; + result.m03 = objectPosition.mX; + result.m13 = objectPosition.mY; + result.m23 = objectPosition.mZ; + result.m33 = 1; + } + + /// + /// Creates a new matrix which contains the rotation moment around specified axis. + /// + /// The axis of rotation. + /// The angle of rotation in radians. + /// The rotation matrix. + public static Matrix4 CreateFromAxisAngle(Vector3 axis, float angle) + { + Matrix4 result; + CreateFromAxisAngle(axis, angle, out result); + return result; + } + + /// + /// Creates a new matrix which contains the rotation moment around specified axis. + /// + /// The axis of rotation. + /// The angle of rotation in radians. + /// The rotation matrix as an output parameter. + public static void CreateFromAxisAngle( + Vector3 axis, + float angle, + out Matrix4 result + ) { + float x = axis.mX; + float y = axis.mY; + float z = axis.mZ; + float num2 = (float) Math.Sin((double) angle); + float num = (float) Math.Cos((double) angle); + float num11 = x * x; + float num10 = y * y; + float num9 = z * z; + float num8 = x * y; + float num7 = x * z; + float num6 = y * z; + result.m00 = num11 + (num * (1f - num11)); + result.m10 = (num8 - (num * num8)) + (num2 * z); + result.m20 = (num7 - (num * num7)) - (num2 * y); + result.m30 = 0; + result.m01 = (num8 - (num * num8)) - (num2 * z); + result.m11 = num10 + (num * (1f - num10)); + result.m21 = (num6 - (num * num6)) + (num2 * x); + result.m31 = 0; + result.m02 = (num7 - (num * num7)) + (num2 * y); + result.m12 = (num6 - (num * num6)) - (num2 * x); + result.m22 = num9 + (num * (1f - num9)); + result.m32 = 0; + result.m03 = 0; + result.m13 = 0; + result.m23 = 0; + result.m33 = 1; + } + + /// + /// Creates a new rotation matrix from a . + /// + /// of rotation moment. + /// The rotation matrix. + public static Matrix4 CreateFromQuaternion(Quaternion quaternion) + { + Matrix4 result; + CreateFromQuaternion(quaternion, out result); + return result; + } + + /// + /// Creates a new rotation matrix from a . + /// + /// of rotation moment. + /// The rotation matrix as an output parameter. + public static void CreateFromQuaternion(Quaternion quaternion, out Matrix4 result) + { + float num9 = quaternion.mX * quaternion.mX; + float num8 = quaternion.mY * quaternion.mY; + float num7 = quaternion.mZ * quaternion.mZ; + float num6 = quaternion.mX * quaternion.mY; + float num5 = quaternion.mZ * quaternion.mW; + float num4 = quaternion.mZ * quaternion.mX; + float num3 = quaternion.mY * quaternion.mW; + float num2 = quaternion.mY * quaternion.mZ; + float num = quaternion.mX * quaternion.mW; + result.m00 = 1f - (2f * (num8 + num7)); + result.m10 = 2f * (num6 + num5); + result.m20 = 2f * (num4 - num3); + result.m30 = 0f; + result.m01 = 2f * (num6 - num5); + result.m11 = 1f - (2f * (num7 + num9)); + result.m21 = 2f * (num2 + num); + result.m31 = 0f; + result.m02 = 2f * (num4 + num3); + result.m12 = 2f * (num2 - num); + result.m22 = 1f - (2f * (num8 + num9)); + result.m32 = 0f; + result.m03 = 0f; + result.m13 = 0f; + result.m23 = 0f; + result.m33 = 1f; + } + + /// Creates a new rotation matrix from the specified yaw, pitch and roll values. + /// @param yaw The yaw rotation value in radians. + /// @param pitch The pitch rotation value in radians. + /// @param roll The roll rotation value in radians. + /// @returns The rotation matrix + /// @remarks For more information about yaw, pitch and roll visit http://en.wikipedia.org/wiki/Euler_angles. + public static Matrix4 CreateFromYawPitchRoll(float yaw, float pitch, float roll) + { + Matrix4 matrix; + CreateFromYawPitchRoll(yaw, pitch, roll, out matrix); + return matrix; + } + + /// Creates a new rotation matrix from the specified yaw, pitch and roll values. + /// @param yaw The yaw rotation value in radians. + /// @param pitch The pitch rotation value in radians. + /// @param roll The roll rotation value in radians. + /// @param result The rotation matrix as an output parameter. + /// @remarks>For more information about yaw, pitch and roll visit http://en.wikipedia.org/wiki/Euler_angles. + public static void CreateFromYawPitchRoll( + float yaw, + float pitch, + float roll, + out Matrix4 result + ) { + Quaternion quaternion; + Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll, out quaternion); + CreateFromQuaternion(quaternion, out result); + } + + /// Creates a new viewing matrix. + /// @param cameraPosition Position of the camera. + /// @param cameraTarget Lookup vector of the camera. + /// @param cameraUpVector The direction of the upper edge of the camera. + /// @returns The viewing matrix. + public static Matrix4 CreateLookAt( + Vector3 cameraPosition, + Vector3 cameraTarget, + Vector3 cameraUpVector + ) { + Matrix4 matrix; + CreateLookAt(cameraPosition, cameraTarget, cameraUpVector, out matrix); + return matrix; + } + + /// Creates a new viewing matrix. + /// @param cameraPosition Position of the camera. + /// @param cameraTarget Lookup vector of the camera. + /// @param cameraUpVector The direction of the upper edge of the camera. + /// @param result The viewing matrix as an output parameter. + public static void CreateLookAt( + Vector3 cameraPosition, + Vector3 cameraTarget, + Vector3 cameraUpVector, + out Matrix4 result + ) { + Vector3 vectorA = Vector3.Normalize(cameraPosition - cameraTarget); + Vector3 vectorB = Vector3.Normalize(Vector3.Cross(cameraUpVector, vectorA)); + Vector3 vectorC = Vector3.Cross(vectorA, vectorB); + result.m00 = vectorB.mX; + result.m10 = vectorC.mX; + result.m20 = vectorA.mX; + result.m30 = 0f; + result.m01 = vectorB.mY; + result.m11 = vectorC.mY; + result.m21 = vectorA.mY; + result.m31 = 0f; + result.m02 = vectorB.mZ; + result.m12 = vectorC.mZ; + result.m22 = vectorA.mZ; + result.m32 = 0f; + result.m03 = -Vector3.Dot(vectorB, cameraPosition); + result.m13 = -Vector3.Dot(vectorC, cameraPosition); + result.m23 = -Vector3.Dot(vectorA, cameraPosition); + result.m33 = 1f; + } + + /// Creates a new projection matrix for orthographic view. + /// @param width Width of the viewing volume. + /// @param height Height of the viewing volume. + /// @param zNearPlane Depth of the near plane. + /// @param zFarPlane Depth of the far plane. + /// @returns The new projection matrix for orthographic view. + public static Matrix4 CreateOrthographic( + float width, + float height, + float zNearPlane, + float zFarPlane + ) { + Matrix4 matrix; + CreateOrthographic(width, height, zNearPlane, zFarPlane, out matrix); + return matrix; + } + + /// Creates a new projection matrix for orthographic view. + /// @param width Width of the viewing volume. + /// @param height Height of the viewing volume. + /// @param zNearPlane Depth of the near plane. + /// @param zFarPlane Depth of the far plane. + /// @param result The new projection matrix for orthographic view as an output parameter. + public static void CreateOrthographic( + float width, + float height, + float zNearPlane, + float zFarPlane, + out Matrix4 result + ) { + result.m00 = 2f / width; + result.m10 = result.m20 = result.m30 = 0f; + result.m11 = 2f / height; + result.m01 = result.m21 = result.m31 = 0f; + result.m22 = 1f / (zNearPlane - zFarPlane); + result.m02 = result.m12 = result.m32 = 0f; + result.m03 = result.m13 = 0f; + result.m23 = zNearPlane / (zNearPlane - zFarPlane); + result.m33 = 1f; + } + + /// Creates a new projection matrix for customized orthographic view. + /// @param left Lower x-value at the near plane. + /// @param right Upper x-value at the near plane. + /// @param bottom Lower y-coordinate at the near plane. + /// @param top Upper y-value at the near plane. + /// @param zNearPlane Depth of the near plane. + /// @param zFarPlane Depth of the far plane. + /// @returns The new projection matrix for customized orthographic view. + public static Matrix4 CreateOrthographicOffCenter( + float left, + float right, + float bottom, + float top, + float zNearPlane, + float zFarPlane + ) { + Matrix4 matrix; + CreateOrthographicOffCenter( + left, + right, + bottom, + top, + zNearPlane, + zFarPlane, + out matrix + ); + return matrix; + } + + /// Creates a new projection matrix for customized orthographic view. + /// @param left Lower x-value at the near plane. + /// @param right Upper x-value at the near plane. + /// @param bottom Lower y-coordinate at the near plane. + /// @param top Upper y-value at the near plane. + /// @param zNearPlane Depth of the near plane. + /// @param zFarPlane Depth of the far plane. + /// @param result The new projection matrix for customized orthographic view as an output parameter. + public static void CreateOrthographicOffCenter( + float left, + float right, + float bottom, + float top, + float zNearPlane, + float zFarPlane, + out Matrix4 result + ) + { + result.m00 = (float) (2.0 / ((double) right - (double) left)); + result.m10 = 0.0f; + result.m20 = 0.0f; + result.m30 = 0.0f; + result.m01 = 0.0f; + result.m11 = (float) (2.0 / ((double) top - (double) bottom)); + result.m21 = 0.0f; + result.m31 = 0.0f; + result.m02 = 0.0f; + result.m12 = 0.0f; + result.m22 = (float) (1.0 / ((double) zNearPlane - (double) zFarPlane)); + result.m32 = 0.0f; + result.m03 = (float) ( + ((double) left + (double) right) / + ((double) left - (double) right) + ); + result.m13 = (float) ( + ((double) top + (double) bottom) / + ((double) bottom - (double) top) + ); + result.m23 = (float) ( + (double) zNearPlane / + ((double) zNearPlane - (double) zFarPlane) + ); + result.m33 = 1.0f; + } + + /// Creates a new matrix that flattens geometry into a specified as if casting a shadow from a specified light source. + /// @param lightDirection A vector specifying the direction from which the light that will cast the shadow is coming. + /// @param plane The plane onto which the new matrix should flatten geometry so as to cast a shadow. + /// @returns>A matrix that can be used to flatten geometry onto the specified plane from the specified direction. + public static Matrix4 CreateShadow(Vector3 lightDirection, Plane plane) + { + Matrix4 result; + result = CreateShadow(lightDirection, plane); + return result; + } + + /// Creates a new matrix that flattens geometry into a specified as if casting a shadow from a specified light source. + /// @param lightDirection A vector specifying the direction from which the light that will cast the shadow is coming. + /// @param plane The plane onto which the new matrix should flatten geometry so as to cast a shadow. + /// @param result A matrix that can be used to flatten geometry onto the specified plane from the specified direction as an output parameter. + public static void CreateShadow( + Vector3 lightDirection, + Plane plane, + out Matrix4 result) + { + float dot = ( + (plane.Normal.mX * lightDirection.mX) + + (plane.Normal.mY * lightDirection.mY) + + (plane.Normal.mZ * lightDirection.mZ) + ); + float x = -plane.Normal.mX; + float y = -plane.Normal.mY; + float z = -plane.Normal.mZ; + float d = -plane.D; + + result.m00 = (x * lightDirection.mX) + dot; + result.m10 = x * lightDirection.mY; + result.m20 = x * lightDirection.mZ; + result.m30 = 0; + result.m01 = y * lightDirection.mX; + result.m11 = (y * lightDirection.mY) + dot; + result.m21 = y * lightDirection.mZ; + result.m31 = 0; + result.m02 = z * lightDirection.mX; + result.m12 = z * lightDirection.mY; + result.m22 = (z * lightDirection.mZ) + dot; + result.m32 = 0; + result.m03 = d * lightDirection.mX; + result.m13 = d * lightDirection.mY; + result.m23 = d * lightDirection.mZ; + result.m33 = dot; + } + + public override void ToString(System.String strBuffer) + { + for (int row < 4) + for (int col < 4) + { +#unwarn + strBuffer.AppendF($"M{row+1}{col+1}:{((float*)&this)[row+col*4]}\n"); + } + } } } diff --git a/BeefLibs/Beefy2D/src/geom/Plane.bf b/BeefLibs/Beefy2D/src/geom/Plane.bf new file mode 100644 index 00000000..8e52b3d7 --- /dev/null +++ b/BeefLibs/Beefy2D/src/geom/Plane.bf @@ -0,0 +1,291 @@ +// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA), +// released under the Microsoft Public License + +using System; + +namespace Beefy.geom +{ + /// + /// Defines the intersection between a and a bounding volume. + /// + public enum PlaneIntersectionType + { + /// + /// There is no intersection, the bounding volume is in the negative half space of the plane. + /// + Front, + /// + /// There is no intersection, the bounding volume is in the positive half space of the plane. + /// + Back, + /// + /// The plane is intersected. + /// + Intersecting + } + + public struct Plane + { + public Vector3 Normal; + public float D; + + public this(Vector4 value) + : this(Vector3(value.mX, value.mY, value.mZ), value.mW) + { + } + + public this(Vector3 normal, float d) + { + Normal = normal; + D = d; + } + + public this(Vector3 a, Vector3 b, Vector3 c) + { + Vector3 ab = b - a; + Vector3 ac = c - a; + + Vector3 cross = Vector3.Cross(ab, ac); + Vector3.Normalize(cross, out Normal); + D = -(Vector3.Dot(Normal, a)); + } + + public this(float a, float b, float c, float d) + : this(Vector3(a, b, c), d) + { + + } + + public float Dot(Vector4 value) + { + return ( + (this.Normal.mX * value.mX) + + (this.Normal.mY * value.mY) + + (this.Normal.mZ * value.mZ) + + (this.D * value.mW) + ); + } + + public void Dot(ref Vector4 value, out float result) + { + result = ( + (this.Normal.mX * value.mX) + + (this.Normal.mY * value.mY) + + (this.Normal.mZ * value.mZ) + + (this.D * value.mW) + ); + } + + public float DotCoordinate(Vector3 value) + { + return ( + (this.Normal.mX * value.mX) + + (this.Normal.mY * value.mY) + + (this.Normal.mZ * value.mZ) + + this.D + ); + } + + public void DotCoordinate(ref Vector3 value, out float result) + { + result = ( + (this.Normal.mX * value.mX) + + (this.Normal.mY * value.mY) + + (this.Normal.mZ * value.mZ) + + this.D + ); + } + + public float DotNormal(Vector3 value) + { + return ( + (this.Normal.mX * value.mX) + + (this.Normal.mY * value.mY) + + (this.Normal.mZ * value.mZ) + ); + } + + public void DotNormal(Vector3 value, out float result) + { + result = ( + (this.Normal.mX * value.mX) + + (this.Normal.mY * value.mY) + + (this.Normal.mZ * value.mZ) + ); + } + + public void Normalize() mut + { + float length = Normal.Length; + float factor = 1.0f / length; + Normal = Vector3.Multiply(Normal, factor); + D = D * factor; + } + + /*public PlaneIntersectionType Intersects(BoundingBox box) + { + return box.Intersects(this); + } + + public void Intersects(ref BoundingBox box, out PlaneIntersectionType result) + { + box.Intersects(ref this, out result); + } + + public PlaneIntersectionType Intersects(BoundingSphere sphere) + { + return sphere.Intersects(this); + } + + public void Intersects(ref BoundingSphere sphere, out PlaneIntersectionType result) + { + sphere.Intersects(ref this, out result); + } + + public PlaneIntersectionType Intersects(BoundingFrustum frustum) + { + return frustum.Intersects(this); + }*/ + + #endregion + + #region Internal Methods + + internal PlaneIntersectionType Intersects(ref Vector3 point) + { + float distance; + DotCoordinate(ref point, out distance); + if (distance > 0) + { + return PlaneIntersectionType.Front; + } + if (distance < 0) + { + return PlaneIntersectionType.Back; + } + return PlaneIntersectionType.Intersecting; + } + + #endregion + + #region Public Static Methods + + public static Plane Normalize(Plane value) + { + Plane ret; + Normalize(value, out ret); + return ret; + } + + public static void Normalize(Plane value, out Plane result) + { + float length = value.Normal.Length; + float factor = 1.0f / length; + result.Normal = Vector3.Multiply(value.Normal, factor); + result.D = value.D * factor; + } + + /// + /// Transforms a normalized plane by a matrix. + /// + /// The normalized plane to transform. + /// The transformation matrix. + /// The transformed plane. + public static Plane Transform(Plane plane, Matrix4 matrix) + { + Plane result; + Transform(plane, matrix, out result); + return result; + } + + /// + /// Transforms a normalized plane by a matrix. + /// + /// The normalized plane to transform. + /// The transformation matrix. + /// The transformed plane. + public static void Transform( + Plane plane, + Matrix4 matrix, + out Plane result + ) { + /* See "Transforming Normals" in + * http://www.glprogramming.com/red/appendixf.html + * for an explanation of how this works. + */ + Matrix4 transformedMatrix; + transformedMatrix = Matrix4.Invert(matrix); + transformedMatrix = Matrix4.Transpose(transformedMatrix); + Vector4 vector = Vector4(plane.Normal, plane.D); + Vector4 transformedVector; + Vector4.Transform( + vector, + transformedMatrix, + out transformedVector + ); + result = Plane(transformedVector); + } + + /// + /// Transforms a normalized plane by a quaternion rotation. + /// + /// The normalized plane to transform. + /// The quaternion rotation. + /// The transformed plane. + public static Plane Transform(Plane plane, Quaternion rotation) + { + Plane result; + Transform(plane, rotation, out result); + return result; + } + + /// + /// Transforms a normalized plane by a quaternion rotation. + /// + /// The normalized plane to transform. + /// The quaternion rotation. + /// The transformed plane. + public static void Transform( + Plane plane, + Quaternion rotation, + out Plane result + ) { + result.Normal = Vector3.Transform( + plane.Normal, + rotation + ); + result.D = plane.D; + } + + #endregion + + #region Public Static Operators and Override Methods + + public static bool operator !=(Plane plane1, Plane plane2) + { + return !plane1.Equals(plane2); + } + + public static bool operator ==(Plane plane1, Plane plane2) + { + return plane1.Equals(plane2); + } + + public bool Equals(Plane other) + { + return (Normal == other.Normal && D == other.D); + } + + public int GetHashCode() + { + return Normal.GetHashCode() ^ D.GetHashCode(); + } + + public override void ToString(String str) + { + str.AppendF($"{Normal:{Normal} D:{D}}"); + } + + #endregion + } +} diff --git a/BeefLibs/Beefy2D/src/geom/Quaternion.bf b/BeefLibs/Beefy2D/src/geom/Quaternion.bf index c5f5d5e2..e7b2c7b9 100644 --- a/BeefLibs/Beefy2D/src/geom/Quaternion.bf +++ b/BeefLibs/Beefy2D/src/geom/Quaternion.bf @@ -1,3 +1,6 @@ +// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA), +// released under the Microsoft Public License + using System; using Beefy.gfx; @@ -189,43 +192,50 @@ namespace Beefy.geom public static void CreateFromRotationMatrix(ref Matrix4 matrix, out Quaternion result) { - float num8 = (matrix.m11 + matrix.m22) + matrix.m33; - if (num8 > 0f) - { - float num = (float)Math.Sqrt((double)(num8 + 1f)); - result.mW = num * 0.5f; - num = 0.5f / num; - result.mX = (matrix.m23 - matrix.m32) * num; - result.mY = (matrix.m31 - matrix.m13) * num; - result.mZ = (matrix.m12 - matrix.m21) * num; - } - else if ((matrix.m11 >= matrix.m22) && (matrix.m11 >= matrix.m33)) - { - float num7 = (float)Math.Sqrt((double)(((1f + matrix.m11) - matrix.m22) - matrix.m33)); - float num4 = 0.5f / num7; - result.mX = 0.5f * num7; - result.mY = (matrix.m12 + matrix.m21) * num4; - result.mZ = (matrix.m13 + matrix.m31) * num4; - result.mW = (matrix.m23 - matrix.m32) * num4; - } - else if (matrix.m22 > matrix.m33) - { - float num6 = (float)Math.Sqrt((double)(((1f + matrix.m22) - matrix.m11) - matrix.m33)); - float num3 = 0.5f / num6; - result.mX = (matrix.m21 + matrix.m12) * num3; - result.mY = 0.5f * num6; - result.mZ = (matrix.m32 + matrix.m23) * num3; - result.mW = (matrix.m31 - matrix.m13) * num3; - } - else - { - float num5 = (float)Math.Sqrt((double)(((1f + matrix.m33) - matrix.m11) - matrix.m22)); - float num2 = 0.5f / num5; - result.mX = (matrix.m31 + matrix.m13) * num2; - result.mY = (matrix.m32 + matrix.m23) * num2; - result.mZ = 0.5f * num5; - result.mW = (matrix.m12 - matrix.m21) * num2; - } + float sqrt; + float half; + float scale = matrix.m00 + matrix.m11 + matrix.m22; + + if (scale > 0.0f) + { + sqrt = (float) Math.Sqrt(scale + 1.0f); + result.mW = sqrt * 0.5f; + sqrt = 0.5f / sqrt; + + result.mX = (matrix.m21 - matrix.m12) * sqrt; + result.mY = (matrix.m02 - matrix.m20) * sqrt; + result.mZ = (matrix.m10 - matrix.m01) * sqrt; + } + else if ((matrix.m00 >= matrix.m11) && (matrix.m00 >= matrix.m22)) + { + sqrt = (float) Math.Sqrt(1.0f + matrix.m00 - matrix.m11 - matrix.m22); + half = 0.5f / sqrt; + + result.mX = 0.5f * sqrt; + result.mY = (matrix.m10 + matrix.m01) * half; + result.mZ = (matrix.m20 + matrix.m02) * half; + result.mW = (matrix.m21 - matrix.m12) * half; + } + else if (matrix.m11 > matrix.m22) + { + sqrt = (float) Math.Sqrt(1.0f + matrix.m11 - matrix.m00 - matrix.m22); + half = 0.5f/sqrt; + + result.mX = (matrix.m01 + matrix.m10)*half; + result.mY = 0.5f*sqrt; + result.mZ = (matrix.m12 + matrix.m21)*half; + result.mW = (matrix.m02 - matrix.m20)*half; + } + else + { + sqrt = (float) Math.Sqrt(1.0f + matrix.m22 - matrix.m00 - matrix.m11); + half = 0.5f / sqrt; + + result.mX = (matrix.m02 + matrix.m20) * half; + result.mY = (matrix.m12 + matrix.m21) * half; + result.mZ = 0.5f * sqrt; + result.mW = (matrix.m10 - matrix.m01) * half; + } } public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll) diff --git a/BeefLibs/Beefy2D/src/geom/Vector3.bf b/BeefLibs/Beefy2D/src/geom/Vector3.bf index be89f716..4ee85123 100644 --- a/BeefLibs/Beefy2D/src/geom/Vector3.bf +++ b/BeefLibs/Beefy2D/src/geom/Vector3.bf @@ -1,3 +1,6 @@ +// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA), +// released under the Microsoft Public License + using System; using System.Collections; using System.Text; @@ -131,7 +134,7 @@ namespace Beefy.geom { Vector3 newVec; Normalize(vector, out newVec); - return vector; + return newVec; } public static void Normalize(Vector3 value, out Vector3 result) @@ -173,7 +176,7 @@ namespace Beefy.geom return new Vector2D((float)Math.Cos(angle) * length, (float)Math.Sin(angle) * length); }*/ - public static Vector3 Transform(Vector3 vec, Matrix4 matrix) + public static Vector3 TransformW(Vector3 vec, Matrix4 matrix) { Vector3 result; float fInvW = 1.0f / (matrix.m30 * vec.mX + matrix.m31 * vec.mY + matrix.m32 * vec.mZ + matrix.m33); @@ -181,9 +184,19 @@ namespace Beefy.geom result.mX = (matrix.m00 * vec.mX + matrix.m01 * vec.mY + matrix.m02 * vec.mZ + matrix.m03) * fInvW; result.mY = (matrix.m10 * vec.mX + matrix.m11 * vec.mY + matrix.m12 * vec.mZ + matrix.m13) * fInvW; result.mZ = (matrix.m20 * vec.mX + matrix.m21 * vec.mY + matrix.m22 * vec.mZ + matrix.m23) * fInvW; + return result; } + public static Vector3 Transform(Vector3 vec, Matrix4 matrix) + { + Vector3 result; + result.mX = (vec.mX * matrix.m00) + (vec.mY * matrix.m01) + (vec.mZ * matrix.m02) + matrix.m03; + result.mY = (vec.mX * matrix.m10) + (vec.mY * matrix.m11) + (vec.mZ * matrix.m12) + matrix.m13; + result.mZ = (vec.mX * matrix.m20) + (vec.mY * matrix.m21) + (vec.mZ * matrix.m22) + matrix.m23; + return result; + } + /*public static void Transform(Vector3[] sourceArray, ref Matrix4 matrix, Vector3[] destinationArray) { //Debug.Assert(destinationArray.Length >= sourceArray.Length, "The destination array is smaller than the source array."); @@ -199,6 +212,51 @@ namespace Beefy.geom } }*/ + /// + /// Returns a Vector3 pointing in the opposite + /// direction of . + /// + /// The vector to negate. + /// The vector negation of . + public static Vector3 Negate(Vector3 value) + { + return .(-value.mX, -value.mY, -value.mZ); + } + + /// + /// Stores a Vector3 pointing in the opposite + /// direction of in . + /// + /// The vector to negate. + /// The vector that the negation of will be stored in. + public static void Negate(Vector3 value, out Vector3 result) + { + result.mX = -value.mX; + result.mY = -value.mY; + result.mZ = -value.mZ; + } + + /// + /// Creates a new that contains a multiplication of two vectors. + /// + /// Source . + /// Source . + /// The result of the vector multiplication. + public static Vector3 Multiply(Vector3 value1, Vector3 value2) + { + return .(value1.mX * value2.mX, value1.mY * value2.mY, value1.mZ * value2.mZ); + } + + public static Vector3 Multiply(Vector3 value1, float value2) + { + return .(value1.mX * value2, value1.mY * value2, value1.mZ * value2); + } + + public void Normalize() mut + { + Normalize(this, out this); + } + public static Vector3 Transform(Vector3 vec, Quaternion quat) { Matrix4 matrix = quat.ToMatrix(); @@ -234,6 +292,11 @@ namespace Beefy.geom return Vector3(vec1.mX - vec2.mX, vec1.mY - vec2.mY, vec1.mZ - vec2.mZ); } + public static Vector3 operator -(Vector3 vec1) + { + return Vector3(-vec1.mX, -vec1.mY, -vec1.mZ); + } + public static Vector3 operator *(Vector3 vec, float scale) { return Vector3(vec.mX * scale, vec.mY * scale, vec.mZ * scale); diff --git a/BeefLibs/Beefy2D/src/geom/Vector4.bf b/BeefLibs/Beefy2D/src/geom/Vector4.bf new file mode 100644 index 00000000..9f15bc6f --- /dev/null +++ b/BeefLibs/Beefy2D/src/geom/Vector4.bf @@ -0,0 +1,1435 @@ +// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA), +// released under the Microsoft Public License + +using System; + +namespace Beefy.geom +{ + public struct Vector4 : IHashable + { + #region Public Static Properties + + /// + /// Returns a with components 0, 0, 0, 0. + /// + public static Vector4 Zero + { + get + { + return zero; + } + } + + /// + /// Returns a with components 1, 1, 1, 1. + /// + public static Vector4 One + { + get + { + return unit; + } + } + + /// + /// Returns a with components 1, 0, 0, 0. + /// + public static Vector4 UnitX + { + get + { + return unitX; + } + } + + /// + /// Returns a with components 0, 1, 0, 0. + /// + public static Vector4 UnitY + { + get + { + return unitY; + } + } + + /// + /// Returns a with components 0, 0, 1, 0. + /// + public static Vector4 UnitZ + { + get + { + return unitZ; + } + } + + /// + /// Returns a with components 0, 0, 0, 1. + /// + public static Vector4 UnitW + { + get + { + return unitW; + } + } + + /// + /// The x coordinate of this . + /// + public float mX; + + /// + /// The y coordinate of this . + /// + public float mY; + + /// + /// The z coordinate of this . + /// + public float mZ; + + /// + /// The w coordinate of this . + /// + public float mW; + + #endregion + + #region Private Static Fields + + private static Vector4 zero = .(); // Not readonly for performance -flibit + private static readonly Vector4 unit = .(1f, 1f, 1f, 1f); + private static readonly Vector4 unitX = .(1f, 0f, 0f, 0f); + private static readonly Vector4 unitY = .(0f, 1f, 0f, 0f); + private static readonly Vector4 unitZ = .(0f, 0f, 1f, 0f); + private static readonly Vector4 unitW = .(0f, 0f, 0f, 1f); + + #endregion + + #region Public Constructors + + public this() + { + mX = 0; + mY = 0; + mZ = 0; + mW = 0; + } + + /// + /// Constructs a 3d vector with X, Y, Z and W from four values. + /// + /// The x coordinate in 4d-space. + /// The y coordinate in 4d-space. + /// The z coordinate in 4d-space. + /// The w coordinate in 4d-space. + public this(float x, float y, float z, float w) + { + this.mX = x; + this.mY = y; + this.mZ = z; + this.mW = w; + } + + /// + /// Constructs a 3d vector with X and Z from and Z and W from the scalars. + /// + /// The x and y coordinates in 4d-space. + /// The z coordinate in 4d-space. + /// The w coordinate in 4d-space. + public this(Vector2 value, float z, float w) + { + this.mX = value.mX; + this.mY = value.mY; + this.mZ = z; + this.mW = w; + } + + /// + /// Constructs a 3d vector with X, Y, Z from and W from a scalar. + /// + /// The x, y and z coordinates in 4d-space. + /// The w coordinate in 4d-space. + public this(Vector3 value, float w) + { + this.mX = value.mX; + this.mY = value.mY; + this.mZ = value.mZ; + this.mW = w; + } + + /// + /// Constructs a 4d vector with X, Y, Z and W set to the same value. + /// + /// The x, y, z and w coordinates in 4d-space. + public this(float value) + { + this.mX = value; + this.mY = value; + this.mZ = value; + this.mW = value; + } + + #endregion + + #region Public Methods + + /// + /// Compares whether current instance is equal to specified . + /// + /// The to compare. + /// true if the instances are equal; false otherwise. + public bool Equals(Vector4 other) + { + return ( mX == other.mX && + mY == other.mY && + mZ == other.mZ && + mW == other.mW ); + } + + /// + /// Gets the hash code of this . + /// + /// Hash code of this . + public int GetHashCode() + { + return mW.GetHashCode() + mX.GetHashCode() + mY.GetHashCode() + mZ.GetHashCode(); + } + + /// + /// Returns the length of this . + /// + /// The length of this . + public float Length() + { + return (float) Math.Sqrt((mX * mX) + (mY * mY) + (mZ * mZ) + (mW * mW)); + } + + /// + /// Returns the squared length of this . + /// + /// The squared length of this . + public float LengthSquared() + { + return (mX * mX) + (mY * mY) + (mZ * mZ) + (mW * mW); + } + + /// + /// Turns this to a unit vector with the same direction. + /// + public void Normalize() mut + { + float factor = 1.0f / (float) Math.Sqrt( + (mX * mX) + + (mY * mY) + + (mZ * mZ) + + (mW * mW) + ); + mX *= factor; + mY *= factor; + mZ *= factor; + mW *= factor; + } + + public override void ToString(String str) + { + str.AppendF($"({mX}, {mY}, {mZ}, {mW})"); + } + + /// + /// Performs vector addition on and . + /// + /// The first vector to add. + /// The second vector to add. + /// The result of the vector addition. + public static Vector4 Add(Vector4 value1, Vector4 value2) + { + return .( + value1.mX + value2.mX, + value1.mY + value2.mY, + value1.mZ + value2.mZ, + value1.mW + value2.mW); + } + + /// + /// Performs vector addition on and + /// , storing the result of the + /// addition in . + /// + /// The first vector to add. + /// The second vector to add. + /// The result of the vector addition. + public static void Add(ref Vector4 value1, ref Vector4 value2, out Vector4 result) + { + result.mW = value1.mW + value2.mW; + result.mX = value1.mX + value2.mX; + result.mY = value1.mY + value2.mY; + result.mZ = value1.mZ + value2.mZ; + } + + /// + /// Creates a new that contains the cartesian coordinates of a vector specified in barycentric coordinates and relative to 4d-triangle. + /// + /// The first vector of 4d-triangle. + /// The second vector of 4d-triangle. + /// The third vector of 4d-triangle. + /// Barycentric scalar b2 which represents a weighting factor towards second vector of 4d-triangle. + /// Barycentric scalar b3 which represents a weighting factor towards third vector of 4d-triangle. + /// The cartesian translation of barycentric coordinates. + public static Vector4 Barycentric( + Vector4 value1, + Vector4 value2, + Vector4 value3, + float amount1, + float amount2 + ) { + return Vector4( + Math.Barycentric(value1.mX, value2.mX, value3.mX, amount1, amount2), + Math.Barycentric(value1.mY, value2.mY, value3.mY, amount1, amount2), + Math.Barycentric(value1.mZ, value2.mZ, value3.mZ, amount1, amount2), + Math.Barycentric(value1.mW, value2.mW, value3.mW, amount1, amount2) + ); + } + + /// + /// Creates a new that contains the cartesian coordinates of a vector specified in barycentric coordinates and relative to 4d-triangle. + /// + /// The first vector of 4d-triangle. + /// The second vector of 4d-triangle. + /// The third vector of 4d-triangle. + /// Barycentric scalar b2 which represents a weighting factor towards second vector of 4d-triangle. + /// Barycentric scalar b3 which represents a weighting factor towards third vector of 4d-triangle. + /// The cartesian translation of barycentric coordinates as an output parameter. + public static void Barycentric( + ref Vector4 value1, + ref Vector4 value2, + ref Vector4 value3, + float amount1, + float amount2, + out Vector4 result + ) { + result.mX = Math.Barycentric(value1.mX, value2.mX, value3.mX, amount1, amount2); + result.mY = Math.Barycentric(value1.mY, value2.mY, value3.mY, amount1, amount2); + result.mZ = Math.Barycentric(value1.mZ, value2.mZ, value3.mZ, amount1, amount2); + result.mW = Math.Barycentric(value1.mW, value2.mW, value3.mW, amount1, amount2); + } + + /// + /// Creates a new that contains CatmullRom interpolation of the specified vectors. + /// + /// The first vector in interpolation. + /// The second vector in interpolation. + /// The third vector in interpolation. + /// The fourth vector in interpolation. + /// Weighting factor. + /// The result of CatmullRom interpolation. + public static Vector4 CatmullRom( + Vector4 value1, + Vector4 value2, + Vector4 value3, + Vector4 value4, + float amount + ) { + return Vector4( + Math.CatmullRom(value1.mX, value2.mX, value3.mX, value4.mX, amount), + Math.CatmullRom(value1.mY, value2.mY, value3.mY, value4.mY, amount), + Math.CatmullRom(value1.mZ, value2.mZ, value3.mZ, value4.mZ, amount), + Math.CatmullRom(value1.mW, value2.mW, value3.mW, value4.mW, amount) + ); + } + + /// + /// Creates a new that contains CatmullRom interpolation of the specified vectors. + /// + /// The first vector in interpolation. + /// The second vector in interpolation. + /// The third vector in interpolation. + /// The fourth vector in interpolation. + /// Weighting factor. + /// The result of CatmullRom interpolation as an output parameter. + public static void CatmullRom( + ref Vector4 value1, + ref Vector4 value2, + ref Vector4 value3, + ref Vector4 value4, + float amount, + out Vector4 result + ) { + result.mX = Math.CatmullRom(value1.mX, value2.mX, value3.mX, value4.mX, amount); + result.mY = Math.CatmullRom(value1.mY, value2.mY, value3.mY, value4.mY, amount); + result.mZ = Math.CatmullRom(value1.mZ, value2.mZ, value3.mZ, value4.mZ, amount); + result.mW = Math.CatmullRom(value1.mW, value2.mW, value3.mW, value4.mW, amount); + } + + /// + /// Clamps the specified value within a range. + /// + /// The value to clamp. + /// The min value. + /// The max value. + /// The clamped value. + public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max) + { + return Vector4( + Math.Clamp(value1.mX, min.mX, max.mX), + Math.Clamp(value1.mY, min.mY, max.mY), + Math.Clamp(value1.mZ, min.mZ, max.mZ), + Math.Clamp(value1.mW, min.mW, max.mW) + ); + } + + /// + /// Clamps the specified value within a range. + /// + /// The value to clamp. + /// The min value. + /// The max value. + /// The clamped value as an output parameter. + public static void Clamp( + Vector4 value1, + Vector4 min, + Vector4 max, + out Vector4 result + ) { + result.mX = Math.Clamp(value1.mX, min.mX, max.mX); + result.mY = Math.Clamp(value1.mY, min.mY, max.mY); + result.mZ = Math.Clamp(value1.mZ, min.mZ, max.mZ); + result.mW = Math.Clamp(value1.mW, min.mW, max.mW); + } + + /// + /// Returns the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between two vectors. + public static float Distance(Vector4 value1, Vector4 value2) + { + return (float) Math.Sqrt(DistanceSquared(value1, value2)); + } + + /// + /// Returns the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between two vectors as an output parameter. + public static void Distance(ref Vector4 value1, ref Vector4 value2, out float result) + { + result = (float) Math.Sqrt(DistanceSquared(value1, value2)); + } + + /// + /// Returns the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between two vectors. + public static float DistanceSquared(Vector4 value1, Vector4 value2) + { + return ( + (value1.mW - value2.mW) * (value1.mW - value2.mW) + + (value1.mX - value2.mX) * (value1.mX - value2.mX) + + (value1.mY - value2.mY) * (value1.mY - value2.mY) + + (value1.mZ - value2.mZ) * (value1.mZ - value2.mZ) + ); + } + + /// + /// Returns the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between two vectors as an output parameter. + public static void DistanceSquared( + ref Vector4 value1, + ref Vector4 value2, + out float result + ) { + result = ( + (value1.mW - value2.mW) * (value1.mW - value2.mW) + + (value1.mX - value2.mX) * (value1.mX - value2.mX) + + (value1.mY - value2.mY) * (value1.mY - value2.mY) + + (value1.mZ - value2.mZ) * (value1.mZ - value2.mZ) + ); + } + + /// + /// Divides the components of a by the components of another . + /// + /// Source . + /// Divisor . + /// The result of dividing the vectors. + public static Vector4 Divide(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW /= value2.mW; + value1.mX /= value2.mX; + value1.mY /= value2.mY; + value1.mZ /= value2.mZ; + return value1; + } + + /// + /// Divides the components of a by a scalar. + /// + /// Source . + /// Divisor scalar. + /// The result of dividing a vector by a scalar. + public static Vector4 Divide(Vector4 value1, float divider) + { + var value1; + float factor = 1f / divider; + value1.mW *= factor; + value1.mX *= factor; + value1.mY *= factor; + value1.mZ *= factor; + return value1; + } + + /// + /// Divides the components of a by a scalar. + /// + /// Source . + /// Divisor scalar. + /// The result of dividing a vector by a scalar as an output parameter. + public static void Divide(ref Vector4 value1, float divider, out Vector4 result) + { + float factor = 1f / divider; + result.mW = value1.mW * factor; + result.mX = value1.mX * factor; + result.mY = value1.mY * factor; + result.mZ = value1.mZ * factor; + } + + /// + /// Divides the components of a by the components of another . + /// + /// Source . + /// Divisor . + /// The result of dividing the vectors as an output parameter. + public static void Divide( + ref Vector4 value1, + ref Vector4 value2, + out Vector4 result + ) { + result.mW = value1.mW / value2.mW; + result.mX = value1.mX / value2.mX; + result.mY = value1.mY / value2.mY; + result.mZ = value1.mZ / value2.mZ; + } + + /// + /// Returns a dot product of two vectors. + /// + /// The first vector. + /// The second vector. + /// The dot product of two vectors. + public static float Dot(Vector4 vector1, Vector4 vector2) + { + return ( + vector1.mX * vector2.mX + + vector1.mY * vector2.mY + + vector1.mZ * vector2.mZ + + vector1.mW * vector2.mW + ); + } + + /// + /// Returns a dot product of two vectors. + /// + /// The first vector. + /// The second vector. + /// The dot product of two vectors as an output parameter. + public static void Dot(ref Vector4 vector1, ref Vector4 vector2, out float result) + { + result = ( + (vector1.mX * vector2.mX) + + (vector1.mY * vector2.mY) + + (vector1.mZ * vector2.mZ) + + (vector1.mW * vector2.mW) + ); + } + + /// + /// Creates a new that contains hermite spline interpolation. + /// + /// The first position vector. + /// The first tangent vector. + /// The second position vector. + /// The second tangent vector. + /// Weighting factor. + /// The hermite spline interpolation vector. + public static Vector4 Hermite( + Vector4 value1, + Vector4 tangent1, + Vector4 value2, + Vector4 tangent2, + float amount + ) { + return Vector4( + Math.Hermite(value1.mX, tangent1.mX, value2.mX, tangent2.mX, amount), + Math.Hermite(value1.mY, tangent1.mY, value2.mY, tangent2.mY, amount), + Math.Hermite(value1.mZ, tangent1.mZ, value2.mZ, tangent2.mZ, amount), + Math.Hermite(value1.mW, tangent1.mW, value2.mW, tangent2.mW, amount) + ); + } + + /// + /// Creates a new that contains hermite spline interpolation. + /// + /// The first position vector. + /// The first tangent vector. + /// The second position vector. + /// The second tangent vector. + /// Weighting factor. + /// The hermite spline interpolation vector as an output parameter. + public static void Hermite( + Vector4 value1, + Vector4 tangent1, + Vector4 value2, + Vector4 tangent2, + float amount, + out Vector4 result + ) { + result.mW = Math.Hermite(value1.mW, tangent1.mW, value2.mW, tangent2.mW, amount); + result.mX = Math.Hermite(value1.mX, tangent1.mX, value2.mX, tangent2.mX, amount); + result.mY = Math.Hermite(value1.mY, tangent1.mY, value2.mY, tangent2.mY, amount); + result.mZ = Math.Hermite(value1.mZ, tangent1.mZ, value2.mZ, tangent2.mZ, amount); + } + + /// + /// Creates a new that contains linear interpolation of the specified vectors. + /// + /// The first vector. + /// The second vector. + /// Weighting value(between 0.0 and 1.0). + /// The result of linear interpolation of the specified vectors. + public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount) + { + return Vector4( + Math.Lerp(value1.mX, value2.mX, amount), + Math.Lerp(value1.mY, value2.mY, amount), + Math.Lerp(value1.mZ, value2.mZ, amount), + Math.Lerp(value1.mW, value2.mW, amount) + ); + } + + /// + /// Creates a new that contains linear interpolation of the specified vectors. + /// + /// The first vector. + /// The second vector. + /// Weighting value(between 0.0 and 1.0). + /// The result of linear interpolation of the specified vectors as an output parameter. + public static void Lerp( + ref Vector4 value1, + ref Vector4 value2, + float amount, + out Vector4 result + ) { + result.mX = Math.Lerp(value1.mX, value2.mX, amount); + result.mY = Math.Lerp(value1.mY, value2.mY, amount); + result.mZ = Math.Lerp(value1.mZ, value2.mZ, amount); + result.mW = Math.Lerp(value1.mW, value2.mW, amount); + } + + /// + /// Creates a new that contains a maximal values from the two vectors. + /// + /// The first vector. + /// The second vector. + /// The with maximal values from the two vectors. + public static Vector4 Max(Vector4 value1, Vector4 value2) + { + return Vector4( + Math.Max(value1.mX, value2.mX), + Math.Max(value1.mY, value2.mY), + Math.Max(value1.mZ, value2.mZ), + Math.Max(value1.mW, value2.mW) + ); + } + + /// + /// Creates a new that contains a maximal values from the two vectors. + /// + /// The first vector. + /// The second vector. + /// The with maximal values from the two vectors as an output parameter. + public static void Max(ref Vector4 value1, ref Vector4 value2, out Vector4 result) + { + result.mX = Math.Max(value1.mX, value2.mX); + result.mY = Math.Max(value1.mY, value2.mY); + result.mZ = Math.Max(value1.mZ, value2.mZ); + result.mW = Math.Max(value1.mW, value2.mW); + } + + /// + /// Creates a new that contains a minimal values from the two vectors. + /// + /// The first vector. + /// The second vector. + /// The with minimal values from the two vectors. + public static Vector4 Min(Vector4 value1, Vector4 value2) + { + return Vector4( + Math.Min(value1.mX, value2.mX), + Math.Min(value1.mY, value2.mY), + Math.Min(value1.mZ, value2.mZ), + Math.Min(value1.mW, value2.mW) + ); + } + + /// + /// Creates a new that contains a minimal values from the two vectors. + /// + /// The first vector. + /// The second vector. + /// The with minimal values from the two vectors as an output parameter. + public static void Min(ref Vector4 value1, ref Vector4 value2, out Vector4 result) + { + result.mX = Math.Min(value1.mX, value2.mX); + result.mY = Math.Min(value1.mY, value2.mY); + result.mZ = Math.Min(value1.mZ, value2.mZ); + result.mW = Math.Min(value1.mW, value2.mW); + } + + /// + /// Creates a new that contains a multiplication of two vectors. + /// + /// Source . + /// Source . + /// The result of the vector multiplication. + public static Vector4 Multiply(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW *= value2.mW; + value1.mX *= value2.mX; + value1.mY *= value2.mY; + value1.mZ *= value2.mZ; + return value1; + } + + /// + /// Creates a new that contains a multiplication of and a scalar. + /// + /// Source . + /// Scalar value. + /// The result of the vector multiplication with a scalar. + public static Vector4 Multiply(Vector4 value1, float scaleFactor) + { + var value1; + value1.mW *= scaleFactor; + value1.mX *= scaleFactor; + value1.mY *= scaleFactor; + value1.mZ *= scaleFactor; + return value1; + } + + /// + /// Creates a new that contains a multiplication of and a scalar. + /// + /// Source . + /// Scalar value. + /// The result of the multiplication with a scalar as an output parameter. + public static void Multiply(Vector4 value1, float scaleFactor, out Vector4 result) + { + result.mW = value1.mW * scaleFactor; + result.mX = value1.mX * scaleFactor; + result.mY = value1.mY * scaleFactor; + result.mZ = value1.mZ * scaleFactor; + } + + /// + /// Creates a new that contains a multiplication of two vectors. + /// + /// Source . + /// Source . + /// The result of the vector multiplication as an output parameter. + public static void Multiply(ref Vector4 value1, ref Vector4 value2, out Vector4 result) + { + result.mW = value1.mW * value2.mW; + result.mX = value1.mX * value2.mX; + result.mY = value1.mY * value2.mY; + result.mZ = value1.mZ * value2.mZ; + } + + /// + /// Creates a new that contains the specified vector inversion. + /// + /// Source . + /// The result of the vector inversion. + public static Vector4 Negate(Vector4 value) + { + return Vector4(-value.mX, -value.mY, -value.mZ, -value.mW); + } + + /// + /// Creates a new that contains the specified vector inversion. + /// + /// Source . + /// The result of the vector inversion as an output parameter. + public static void Negate(ref Vector4 value, out Vector4 result) + { + result.mX = -value.mX; + result.mY = -value.mY; + result.mZ = -value.mZ; + result.mW = -value.mW; + } + + /// + /// Creates a new that contains a normalized values from another vector. + /// + /// Source . + /// Unit vector. + public static Vector4 Normalize(Vector4 vector) + { + float factor = 1.0f / (float) Math.Sqrt( + (vector.mX * vector.mX) + + (vector.mY * vector.mY) + + (vector.mZ * vector.mZ) + + (vector.mW * vector.mW) + ); + return Vector4( + vector.mX * factor, + vector.mY * factor, + vector.mZ * factor, + vector.mW * factor + ); + } + + /// + /// Creates a new that contains a normalized values from another vector. + /// + /// Source . + /// Unit vector as an output parameter. + public static void Normalize(Vector4 vector, out Vector4 result) + { + float factor = 1.0f / (float) Math.Sqrt( + (vector.mX * vector.mX) + + (vector.mY * vector.mY) + + (vector.mZ * vector.mZ) + + (vector.mW * vector.mW) + ); + result.mX = vector.mX * factor; + result.mY = vector.mY * factor; + result.mZ = vector.mZ * factor; + result.mW = vector.mW * factor; + } + + /// + /// Creates a new that contains cubic interpolation of the specified vectors. + /// + /// Source . + /// Source . + /// Weighting value. + /// Cubic interpolation of the specified vectors. + public static Vector4 SmoothStep(Vector4 value1, Vector4 value2, float amount) + { + return Vector4( + Math.SmoothStep(value1.mX, value2.mX, amount), + Math.SmoothStep(value1.mY, value2.mY, amount), + Math.SmoothStep(value1.mZ, value2.mZ, amount), + Math.SmoothStep(value1.mW, value2.mW, amount) + ); + } + + /// + /// Creates a new that contains cubic interpolation of the specified vectors. + /// + /// Source . + /// Source . + /// Weighting value. + /// Cubic interpolation of the specified vectors as an output parameter. + public static void SmoothStep( + Vector4 value1, + Vector4 value2, + float amount, + out Vector4 result + ) { + result.mX = Math.SmoothStep(value1.mX, value2.mX, amount); + result.mY = Math.SmoothStep(value1.mY, value2.mY, amount); + result.mZ = Math.SmoothStep(value1.mZ, value2.mZ, amount); + result.mW = Math.SmoothStep(value1.mW, value2.mW, amount); + } + + /// + /// Creates a new that contains subtraction of on from a another. + /// + /// Source . + /// Source . + /// The result of the vector subtraction. + public static Vector4 Subtract(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW -= value2.mW; + value1.mX -= value2.mX; + value1.mY -= value2.mY; + value1.mZ -= value2.mZ; + return value1; + } + + /// + /// Creates a new that contains subtraction of on from a another. + /// + /// Source . + /// Source . + /// The result of the vector subtraction as an output parameter. + public static void Subtract(Vector4 value1, Vector4 value2, out Vector4 result) + { + result.mW = value1.mW - value2.mW; + result.mX = value1.mX - value2.mX; + result.mY = value1.mY - value2.mY; + result.mZ = value1.mZ - value2.mZ; + } + + /// + /// Creates a new that contains a transformation of 2d-vector by the specified . + /// + /// Source . + /// The transformation . + /// Transformed . + public static Vector4 Transform(Vector2 position, Matrix4 matrix) + { + Vector4 result; + Transform(position, matrix, out result); + return result; + } + + /// + /// Creates a new that contains a transformation of 3d-vector by the specified . + /// + /// Source . + /// The transformation . + /// Transformed . + public static Vector4 Transform(Vector3 position, Matrix4 matrix) + { + Vector4 result; + Transform(position, matrix, out result); + return result; + } + + /// + /// Creates a new that contains a transformation of 4d-vector by the specified . + /// + /// Source . + /// The transformation . + /// Transformed . + public static Vector4 Transform(Vector4 vector, Matrix4 matrix) + { + return Transform(vector, matrix); + } + + /// + /// Creates a new that contains a transformation of 2d-vector by the specified . + /// + /// Source . + /// The transformation . + /// Transformed as an output parameter. + public static void Transform(Vector2 position, Matrix4 matrix, out Vector4 result) + { + result = Vector4( + (position.mX * matrix.m00) + (position.mY * matrix.m01) + matrix.m03, + (position.mX * matrix.m10) + (position.mY * matrix.m11) + matrix.m13, + (position.mX * matrix.m20) + (position.mY * matrix.m21) + matrix.m23, + (position.mX * matrix.m30) + (position.mY * matrix.m31) + matrix.m33 + ); + } + + /// + /// Creates a new that contains a transformation of 3d-vector by the specified . + /// + /// Source . + /// The transformation . + /// Transformed as an output parameter. + public static void Transform(Vector3 position, Matrix4 matrix, out Vector4 result) + { + float x = ( + (position.mX * matrix.m00) + + (position.mY * matrix.m01) + + (position.mZ * matrix.m02) + + matrix.m03 + ); + float y = ( + (position.mX * matrix.m10) + + (position.mY * matrix.m11) + + (position.mZ * matrix.m12) + + matrix.m13 + ); + float z = ( + (position.mX * matrix.m20) + + (position.mY * matrix.m21) + + (position.mZ * matrix.m22) + + matrix.m23 + ); + float w = ( + (position.mX * matrix.m30) + + (position.mY * matrix.m31) + + (position.mZ * matrix.m32) + + matrix.m33 + ); + result.mX = x; + result.mY = y; + result.mZ = z; + result.mW = w; + } + + /// + /// Creates a new that contains a transformation of 4d-vector by the specified . + /// + /// Source . + /// The transformation . + /// Transformed as an output parameter. + public static void Transform(Vector4 vector, Matrix4 matrix, out Vector4 result) + { + float x = ( + (vector.mX * matrix.m00) + + (vector.mY * matrix.m01) + + (vector.mZ * matrix.m02) + + (vector.mW * matrix.m03) + ); + float y = ( + (vector.mX * matrix.m10) + + (vector.mY * matrix.m11) + + (vector.mZ * matrix.m12) + + (vector.mW * matrix.m13) + ); + float z = ( + (vector.mX * matrix.m20) + + (vector.mY * matrix.m21) + + (vector.mZ * matrix.m22) + + (vector.mW * matrix.m23) + ); + float w = ( + (vector.mX * matrix.m30) + + (vector.mY * matrix.m31) + + (vector.mZ * matrix.m32) + + (vector.mW * matrix.m33) + ); + result.mX = x; + result.mY = y; + result.mZ = z; + result.mW = w; + } + + /// + /// Apply transformation on all vectors within array of by the specified and places the results in an another array. + /// + /// Source array. + /// The transformation . + /// Destination array. + public static void Transform( + Vector4[] sourceArray, + Matrix4 matrix, + Vector4[] destinationArray + ) { + if (sourceArray == null) + { + Runtime.FatalError("sourceArray"); + } + if (destinationArray == null) + { + Runtime.FatalError("destinationArray"); + } + if (destinationArray.Count < sourceArray.Count) + { + Runtime.FatalError( + "destinationArray is too small to contain the result." + ); + } + for (int i = 0; i < sourceArray.Count; i += 1) + { + Transform( + sourceArray[i], + matrix, + out destinationArray[i] + ); + } + } + + /// + /// Apply transformation on vectors within array of by the specified and places the results in an another array. + /// + /// Source array. + /// The starting index of transformation in the source array. + /// The transformation . + /// Destination array. + /// The starting index in the destination array, where the first should be written. + /// The number of vectors to be transformed. + public static void Transform( + Vector4[] sourceArray, + int sourceIndex, + Matrix4 matrix, + Vector4[] destinationArray, + int destinationIndex, + int length + ) { + if (sourceArray == null) + { + Runtime.FatalError("sourceArray"); + } + if (destinationArray == null) + { + Runtime.FatalError("destinationArray"); + } + if (destinationIndex + length > destinationArray.Count) + { + Runtime.FatalError( + "destinationArray is too small to contain the result." + ); + } + if (sourceIndex + length > sourceArray.Count) + { + Runtime.FatalError( + "The combination of sourceIndex and length was greater than sourceArray.Length." + ); + } + for (int i = 0; i < length; i += 1) + { + Transform( + sourceArray[i + sourceIndex], + matrix, + out destinationArray[i + destinationIndex] + ); + } + } + + /// + /// Creates a new that contains a transformation of 2d-vector by the specified . + /// + /// Source . + /// The which contains rotation transformation. + /// Transformed . + public static Vector4 Transform(Vector2 value, Quaternion rotation) + { + Vector4 result; + Transform(value, rotation, out result); + return result; + } + + /// + /// Creates a new that contains a transformation of 3d-vector by the specified . + /// + /// Source . + /// The which contains rotation transformation. + /// Transformed . + public static Vector4 Transform(Vector3 value, Quaternion rotation) + { + Vector4 result; + Transform(value, rotation, out result); + return result; + } + + /// + /// Creates a new that contains a transformation of 4d-vector by the specified . + /// + /// Source . + /// The which contains rotation transformation. + /// Transformed . + public static Vector4 Transform(Vector4 value, Quaternion rotation) + { + Vector4 result; + Transform(value, rotation, out result); + return result; + } + + /// + /// Creates a new that contains a transformation of 2d-vector by the specified . + /// + /// Source . + /// The which contains rotation transformation. + /// Transformed as an output parameter. + public static void Transform( + Vector2 value, + Quaternion rotation, + out Vector4 result + ) { + double xx = rotation.mX + rotation.mX; + double yy = rotation.mY + rotation.mY; + double zz = rotation.mZ + rotation.mZ; + double wxx = rotation.mW * xx; + double wyy = rotation.mW * yy; + double wzz = rotation.mW * zz; + double xxx = rotation.mX * xx; + double xyy = rotation.mX * yy; + double xzz = rotation.mX * zz; + double yyy = rotation.mY * yy; + double yzz = rotation.mY * zz; + double zzz = rotation.mZ * zz; + result.mX = (float) ( + (double) value.mX * (1.0 - yyy - zzz) + + (double) value.mY * (xyy - wzz) + ); + result.mY = (float) ( + (double) value.mX * (xyy + wzz) + + (double) value.mY * (1.0 - xxx - zzz) + ); + result.mZ = (float) ( + (double) value.mX * (xzz - wyy) + + (double) value.mY * (yzz + wxx) + ); + result.mW = 1.0f; + } + + /// + /// Creates a new that contains a transformation of 3d-vector by the specified . + /// + /// Source . + /// The which contains rotation transformation. + /// Transformed as an output parameter. + public static void Transform( + Vector3 value, + Quaternion rotation, + out Vector4 result + ) { + double xx = rotation.mX + rotation.mX; + double yy = rotation.mY + rotation.mY; + double zz = rotation.mZ + rotation.mZ; + double wxx = rotation.mW * xx; + double wyy = rotation.mW * yy; + double wzz = rotation.mW * zz; + double xxx = rotation.mX * xx; + double xyy = rotation.mX * yy; + double xzz = rotation.mX * zz; + double yyy = rotation.mY * yy; + double yzz = rotation.mY * zz; + double zzz = rotation.mZ * zz; + result.mX = (float) ( + (double) value.mX * (1.0 - yyy - zzz) + + (double) value.mY * (xyy - wzz) + + (double) value.mZ * (xzz + wyy) + ); + result.mY = (float) ( + (double) value.mX * (xyy + wzz) + + (double) value.mY * (1.0 - xxx - zzz) + + (double) value.mZ * (yzz - wxx) + ); + result.mZ = (float) ( + (double) value.mX * (xzz - wyy) + + (double) value.mY * (yzz + wxx) + + (double) value.mZ * (1.0 - xxx - yyy) + ); + result.mW = 1.0f; + } + + /// + /// Creates a new that contains a transformation of 4d-vector by the specified . + /// + /// Source . + /// The which contains rotation transformation. + /// Transformed as an output parameter. + public static void Transform( + Vector4 value, + Quaternion rotation, + out Vector4 result + ) { + double xx = rotation.mX + rotation.mX; + double yy = rotation.mY + rotation.mY; + double zz = rotation.mZ + rotation.mZ; + double wxx = rotation.mW * xx; + double wyy = rotation.mW * yy; + double wzz = rotation.mW * zz; + double xxx = rotation.mX * xx; + double xyy = rotation.mX * yy; + double xzz = rotation.mX * zz; + double yyy = rotation.mY * yy; + double yzz = rotation.mY * zz; + double zzz = rotation.mZ * zz; + result.mX = (float) ( + (double) value.mX * (1.0 - yyy - zzz) + + (double) value.mY * (xyy - wzz) + + (double) value.mZ * (xzz + wyy) + ); + result.mY = (float) ( + (double) value.mX * (xyy + wzz) + + (double) value.mY * (1.0 - xxx - zzz) + + (double) value.mZ * (yzz - wxx) + ); + result.mZ = (float) ( + (double) value.mX * (xzz - wyy) + + (double) value.mY * (yzz + wxx) + + (double) value.mZ * (1.0 - xxx - yyy) + ); + result.mW = value.mW; + } + + /// + /// Apply transformation on all vectors within array of by the specified and places the results in an another array. + /// + /// Source array. + /// The which contains rotation transformation. + /// Destination array. + public static void Transform( + Vector4[] sourceArray, + Quaternion rotation, + Vector4[] destinationArray + ) { + if (sourceArray == null) + { + Runtime.FatalError("sourceArray"); + } + if (destinationArray == null) + { + Runtime.FatalError("destinationArray"); + } + if (destinationArray.Count < sourceArray.Count) + { + Runtime.FatalError( + "destinationArray is too small to contain the result." + ); + } + for (int i = 0; i < sourceArray.Count; i += 1) + { + Transform( + sourceArray[i], + rotation, + out destinationArray[i] + ); + } + } + + /// + /// Apply transformation on vectors within array of by the specified and places the results in an another array. + /// + /// Source array. + /// The starting index of transformation in the source array. + /// The which contains rotation transformation. + /// Destination array. + /// The starting index in the destination array, where the first should be written. + /// The number of vectors to be transformed. + public static void Transform( + Vector4[] sourceArray, + int sourceIndex, + Quaternion rotation, + Vector4[] destinationArray, + int destinationIndex, + int length + ) { + if (sourceArray == null) + { + Runtime.FatalError("sourceArray"); + } + if (destinationArray == null) + { + Runtime.FatalError("destinationArray"); + } + if (destinationIndex + length > destinationArray.Count) + { + Runtime.FatalError( + "destinationArray is too small to contain the result." + ); + } + if (sourceIndex + length > sourceArray.Count) + { + Runtime.FatalError( + "The combination of sourceIndex and length was greater than sourceArray.Length." + ); + } + for (int i = 0; i < length; i += 1) + { + Transform( + sourceArray[i + sourceIndex], + rotation, + out destinationArray[i + destinationIndex] + ); + } + } + + #endregion + + #region Public Static Operators + + public static Vector4 operator -(Vector4 value) + { + return Vector4(-value.mX, -value.mY, -value.mZ, -value.mW); + } + + public static bool operator ==(Vector4 value1, Vector4 value2) + { + return ( value1.mX == value2.mX && + value1.mY == value2.mY && + value1.mZ == value2.mZ && + value1.mW == value2.mW ); + } + + public static bool operator !=(Vector4 value1, Vector4 value2) + { + return !(value1 == value2); + } + + public static Vector4 operator +(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW += value2.mW; + value1.mX += value2.mX; + value1.mY += value2.mY; + value1.mZ += value2.mZ; + return value1; + } + + public static Vector4 operator -(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW -= value2.mW; + value1.mX -= value2.mX; + value1.mY -= value2.mY; + value1.mZ -= value2.mZ; + return value1; + } + + public static Vector4 operator *(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW *= value2.mW; + value1.mX *= value2.mX; + value1.mY *= value2.mY; + value1.mZ *= value2.mZ; + return value1; + } + + public static Vector4 operator *(Vector4 value1, float scaleFactor) + { + var value1; + value1.mW *= scaleFactor; + value1.mX *= scaleFactor; + value1.mY *= scaleFactor; + value1.mZ *= scaleFactor; + return value1; + } + + public static Vector4 operator *(float scaleFactor, Vector4 value1) + { + var value1; + value1.mW *= scaleFactor; + value1.mX *= scaleFactor; + value1.mY *= scaleFactor; + value1.mZ *= scaleFactor; + return value1; + } + + public static Vector4 operator /(Vector4 value1, Vector4 value2) + { + var value1; + value1.mW /= value2.mW; + value1.mX /= value2.mX; + value1.mY /= value2.mY; + value1.mZ /= value2.mZ; + return value1; + } + + public static Vector4 operator /(Vector4 value1, float divider) + { + var value1; + float factor = 1f / divider; + value1.mW *= factor; + value1.mX *= factor; + value1.mY *= factor; + value1.mZ *= factor; + return value1; + } + + #endregion + } +} diff --git a/BeefLibs/Beefy2D/src/geom/Viewport.bf b/BeefLibs/Beefy2D/src/geom/Viewport.bf new file mode 100644 index 00000000..019f66b7 --- /dev/null +++ b/BeefLibs/Beefy2D/src/geom/Viewport.bf @@ -0,0 +1,59 @@ +// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA), +// released under the Microsoft Public License + +using Beefy.geom; +using System.Diagnostics; + +namespace Beefy.geom +{ + struct Viewport + { + private int mX; + private int mY; + private int mWidth; + private int mHeight; + private float mMinDepth; + private float mMaxDepth; + + public this(int x, int y, int width, int height,float minDepth,float maxDepth) + { + this.mX = x; + this.mY = y; + this.mWidth = width; + this.mHeight = height; + this.mMinDepth = minDepth; + this.mMaxDepth = maxDepth; + } + + /// Unprojects a Vector3 from screen space into world space. + /// @param source The Vector3 to unproject. + /// @param projection The projection + /// @param view The view + /// @param world The world + public Vector3 Unproject(Vector3 source, Matrix4 projection, Matrix4 view, Matrix4 world) + { + var source; + + Matrix4 matrix = Matrix4.Invert(Matrix4.Multiply(Matrix4.Multiply(world, view), projection)); + source.mX = (((source.mX - this.mX) / ((float) this.mWidth)) * 2f) - 1f; + source.mY = -((((source.mY - this.mY) / ((float) this.mHeight)) * 2f) - 1f); + source.mZ = (source.mZ - this.mMinDepth) / (this.mMaxDepth - this.mMinDepth); + Vector3 vector = Vector3.Transform(source, matrix); + float a = (((source.mX * matrix.m30) + (source.mY * matrix.m31)) + (source.mZ * matrix.m32)) + matrix.m33; + if (!WithinEpsilon(a, 1f)) + { + vector.mX = vector.mX / a; + vector.mY = vector.mY / a; + vector.mZ = vector.mZ / a; + } + return vector; + + } + + private static bool WithinEpsilon(float a, float b) + { + float num = a - b; + return ((-1.401298E-45f <= num) && (num <= float.Epsilon)); + } + } +} diff --git a/BeefLibs/Beefy2D/src/gfx/Font.bf b/BeefLibs/Beefy2D/src/gfx/Font.bf index f40459a4..79bc7390 100644 --- a/BeefLibs/Beefy2D/src/gfx/Font.bf +++ b/BeefLibs/Beefy2D/src/gfx/Font.bf @@ -112,13 +112,13 @@ namespace Beefy.gfx enum MarkPosition { AboveC, // Center - AboveR, // Left edge of mark aligned on center of char8 - AboveE, // Center of mark aligned on right edge of char8 + AboveR, // Left edge of mark aligned on center of char + AboveE, // Center of mark aligned on right edge of char BelowC, BelowR, OverC, OverE, - TopR, // Center of edge aligned to top of char8 + TopR, // Center of edge aligned to top of char } const int32 LOW_CHAR_COUNT = 128; @@ -171,16 +171,33 @@ namespace Beefy.gfx { if (valType == 1) { - String fontName = new String(&fontNameArr); + String fontName = scope String(&fontNameArr); int parenPos = fontName.IndexOf(" ("); if (parenPos != -1) fontName.RemoveToEnd(parenPos); fontName.ToUpper(); - String fontPath = new String(&data); - if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) || (!sFontNameMap.TryAdd(fontName, fontPath))) + String fontPath = scope String(&data); + if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) && (!fontPath.EndsWith(".TTC", .OrdinalIgnoreCase))) + continue; + + if (fontName.Contains('&')) { - delete fontName; - delete fontPath; + int collectionIdx = 0; + for (var namePart in fontName.Split('&', .RemoveEmptyEntries)) + { + namePart.Trim(); + if (sFontNameMap.TryAddAlt(namePart, var keyPtr, var valuePtr)) + { + *keyPtr = new String(namePart); + *valuePtr = new $"{fontPath}@{collectionIdx}"; + collectionIdx++; + } + } + } + else if (sFontNameMap.TryAdd(fontName, var keyPtr, var valuePtr)) + { + *keyPtr = new String(fontName); + *valuePtr = new String(fontPath); } } } @@ -781,7 +798,7 @@ namespace Beefy.gfx else if (newMatrix.tx > clipRect.mX + clipRect.mWidth) { isFullyClipped = true; - if ((newMatrix.a > 0) && (fontMetrics == null)) // Forward? If so, all future char8s will clip + if ((newMatrix.a > 0) && (fontMetrics == null)) // Forward? If so, all future chars will clip break; } } diff --git a/BeefLibs/Beefy2D/src/gfx/Graphics.bf b/BeefLibs/Beefy2D/src/gfx/Graphics.bf index 06ccb0c0..ef3dbb3d 100644 --- a/BeefLibs/Beefy2D/src/gfx/Graphics.bf +++ b/BeefLibs/Beefy2D/src/gfx/Graphics.bf @@ -78,7 +78,11 @@ namespace Beefy.gfx protected DisposeProxy mClipDisposeProxy ~ delete _; const int32 CLIP_STACK_SIZE = 256; public Rect?[] mClipStack = new Rect?[CLIP_STACK_SIZE] ~ delete _; - + + public bool mTexWrap; + protected DisposeProxy mTexWrapDisableProxy ~ delete _; + protected DisposeProxy mTexWrapEnableProxy ~ delete _; + public int32 mClipStackIdx = 0; public Rect? mClipRect = null; @@ -106,6 +110,10 @@ namespace Beefy.gfx mClipDisposeProxy = new DisposeProxy(); mClipDisposeProxy.mDisposeProxyDelegate = new => PopClip; mRenderStateDisposeProxy = new DisposeProxy(); + mTexWrapDisableProxy = new DisposeProxy(); + mTexWrapDisableProxy.mDisposeProxyDelegate = new () => { PopTexWrap(false); }; + mTexWrapEnableProxy = new DisposeProxy(); + mTexWrapEnableProxy.mDisposeProxyDelegate = new () => { PopTexWrap(true); }; mWhiteDot = Image.LoadFromFile("!white"); @@ -341,15 +349,15 @@ namespace Beefy.gfx Rect rectThing = mClipRect.Value; mClipRect = rectThing; - var clipRenderState = AllocRenderState(mDefaultShader, mClipRect); + var clipRenderState = AllocRenderState(mDefaultShader, mClipRect, mTexWrap); //clipRenderState.ClipRect = mClipRect; - PushRenderState(clipRenderState); + PushRenderState(clipRenderState); return mClipDisposeProxy; } - RenderState AllocRenderState(Shader shader, Rect? clipRect) + RenderState AllocRenderState(Shader shader, Rect? clipRect, bool texWrap) { RenderState renderState = null; var curRenderState = mRenderStateStack[mRenderStateStackIdx]; @@ -365,6 +373,7 @@ namespace Beefy.gfx } else renderState = RenderState.Create(curRenderState); + renderState.TexWrap = texWrap; renderState.Shader = shader; renderState.ClipRect = clipRect; return renderState; @@ -375,16 +384,33 @@ namespace Beefy.gfx mClipStackIdx++; mClipStack[mClipStackIdx] = null; mClipRect = null; - var clipRenderState = AllocRenderState(mDefaultShader, null); + var clipRenderState = AllocRenderState(mDefaultShader, mClipRect, mTexWrap); //clipRenderState.ClipRect = null; PushRenderState(clipRenderState); return mClipDisposeProxy; } + public DisposeProxy PushTexWrap(bool texWrap) + { + bool prevTexWrap = mTexWrap; + mTexWrap = texWrap; + + var clipRenderState = AllocRenderState(mDefaultShader, mClipRect, mTexWrap); + PushRenderState(clipRenderState); + + return prevTexWrap ? mTexWrapEnableProxy : mTexWrapDisableProxy; + } + + protected void PopTexWrap(bool texWrap) + { + mTexWrap = texWrap; + PopRenderState(); + } + public void PushTextRenderState() { - var textRenderState = AllocRenderState(mTextShader, mClipRect); + var textRenderState = AllocRenderState(mTextShader, mClipRect, mTexWrap); //textRenderState.ClipRect = mClipRect; //textRenderState.Shader = mTextShader; PushRenderState(textRenderState); @@ -416,13 +442,16 @@ namespace Beefy.gfx //static unsafe extern void Gfx_DrawIndexedVertices2D(void* vtxData, int vtxCount, int* idxData, int idxCount, float a, float b, float c, float d, float tx, float ty, float z); [CallingConvention(.Stdcall), CLink] - static extern void Gfx_DrawIndexedVertices2D(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount, float a, float b, float c, float d, float tx, float ty, float z); + static extern void Gfx_DrawIndexedVertices(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount); + + [CallingConvention(.Stdcall), CLink] + static extern void Gfx_DrawIndexedVertices2D(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount, float a, float b, float c, float d, float tx, float ty, float z); [CallingConvention(.Stdcall), CLink] - static extern void Gfx_SetShaderConstantData(int32 slotIdx, void* data, int32 size); + static extern void Gfx_SetShaderConstantData(int32 usageIdx, int32 slotIdx, void* data, int32 size); [CallingConvention(.Stdcall), CLink] - static extern void Gfx_SetShaderConstantDataTyped(int32 slotIdx, void* data, int32 size, int32* typeData, int32 typeCount); + static extern void Gfx_SetShaderConstantDataTyped(int usageIdx, int32 slotIdx, void* data, int32 size, int32* typeData, int32 typeCount); [CallingConvention(.Stdcall), CLink] static extern void Gfx_DrawQuads(void* textureSegment, Vertex3D* vertices, int32 vtxCount); @@ -768,32 +797,57 @@ namespace Beefy.gfx public void DrawIndexedVertices(VertexDefinition vertexDef, void* vertices, int vtxCount, uint16[] indices) { - Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices.CArray(), (int32)indices.Count, - mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth); + if (vertexDef.mPosition2DOffset != -1) + { + Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices.CArray(), (int32)indices.Count, + mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth); + } + else + { + Gfx_DrawIndexedVertices(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices.CArray(), (int32)indices.Count); + } } public void DrawIndexedVertices(VertexDefinition vertexDef, void* vertices, int vtxCount, uint16* indices, int idxCount) { - Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices, (int32)idxCount, - mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth); + if (vertexDef.mPosition2DOffset != -1) + { + Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices, (int32)idxCount, + mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth); + } + else + { + Gfx_DrawIndexedVertices(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices, (int32)idxCount); + } } - public void SetShaderConstantData(int slotIdx, void* data, int size) + public void SetVertexShaderConstantData(int slotIdx, void* data, int size) { - Gfx_SetShaderConstantData((int32)slotIdx, data, (int32)size); + Gfx_SetShaderConstantData(0, (int32)slotIdx, data, (int32)size); } - public void SetShaderConstantData(int32 slotIdx, void* data, ConstantDataDefinition constantDataDefinition) + public void SetPixelShaderConstantData(int slotIdx, void* data, int size) + { + Gfx_SetShaderConstantData(1, (int32)slotIdx, data, (int32)size); + } + + public void SetVertexShaderConstantData(int32 slotIdx, void* data, ConstantDataDefinition constantDataDefinition) { int32* dataTypesPtr = (int32*)constantDataDefinition.mDataTypes.CArray(); - Gfx_SetShaderConstantDataTyped(slotIdx, data, constantDataDefinition.mDataSize, dataTypesPtr, (int32)constantDataDefinition.mDataTypes.Count); + Gfx_SetShaderConstantDataTyped(0, slotIdx, data, constantDataDefinition.mDataSize, dataTypesPtr, (int32)constantDataDefinition.mDataTypes.Count); } - public void SetShaderConstantData(int32 slotIdx, Matrix4 matrix) + public void SetVertexShaderConstantData(int32 slotIdx, Matrix4 matrix) + { + var mtx = matrix; + Gfx_SetShaderConstantData(0, slotIdx, &mtx, (int32)sizeof(Matrix4)); + } + + public void SetPixelShaderConstantData(int32 slotIdx, Matrix4 matrix) { var mtx = matrix; - Gfx_SetShaderConstantData(slotIdx, &mtx, (int32)sizeof(Matrix4)); - } + Gfx_SetShaderConstantData(1, slotIdx, &mtx, (int32)sizeof(Matrix4)); + } public float DrawString(StringView theString, float x, float y, FontAlign alignment = FontAlign.Left, float width = 0, FontOverflowMode overflowMode = FontOverflowMode.Overflow, FontMetrics* fontMetrics = null) { @@ -811,12 +865,12 @@ namespace Beefy.gfx float d = m.d * height; Gfx_AllocTris(image.mNativeTextureSegment, 6); - Gfx_SetDrawVertex(0, m.tx, m.ty, 0, u1, 0, mColor); - Gfx_SetDrawVertex(1, m.tx + a, m.ty + b, 0, u2, 0, mColor); - Gfx_SetDrawVertex(2, m.tx + c, m.ty + d, 0, u1, 1, mColor); + Gfx_SetDrawVertex(0, m.tx, m.ty, 0, u1, v1, mColor); + Gfx_SetDrawVertex(1, m.tx + a, m.ty + b, 0, u2, v1, mColor); + Gfx_SetDrawVertex(2, m.tx + c, m.ty + d, 0, u1, v2, mColor); Gfx_CopyDrawVertex(3, 2); Gfx_CopyDrawVertex(4, 1); - Gfx_SetDrawVertex(5, m.tx + (a + c), m.ty + (b + d), 0, u2, 1, mColor); + Gfx_SetDrawVertex(5, m.tx + (a + c), m.ty + (b + d), 0, u2, v2, mColor); } // Untranslated diff --git a/BeefLibs/Beefy2D/src/gfx/Image.bf b/BeefLibs/Beefy2D/src/gfx/Image.bf index 0835d251..48b84d7a 100644 --- a/BeefLibs/Beefy2D/src/gfx/Image.bf +++ b/BeefLibs/Beefy2D/src/gfx/Image.bf @@ -210,6 +210,25 @@ namespace Beefy.gfx } } } + + public Image[] CreateImageCels(int32 cols, int32 rows) + { + int32 celW = mSrcWidth / cols; + int32 celH = mSrcHeight / rows; + + Debug.Assert(celW * cols == mSrcWidth); + Debug.Assert(celH * rows == mSrcHeight); + + Image[] celImages = new .[cols * rows]; + for (int32 row = 0; row < rows; row++) + { + for (int32 col = 0; col < cols; col++) + { + celImages[row * cols + col] = CreateImageSegment(col * celW, row * celH, celW, celH); + } + } + return celImages; + } } #else public class Image : IDrawable diff --git a/BeefLibs/Beefy2D/src/gfx/Model.bf b/BeefLibs/Beefy2D/src/gfx/Model.bf index 591d2780..a587b106 100644 --- a/BeefLibs/Beefy2D/src/gfx/Model.bf +++ b/BeefLibs/Beefy2D/src/gfx/Model.bf @@ -48,7 +48,13 @@ namespace Beefy.gfx #if !STUDIO_CLIENT extension ModelDef - { + { + public enum ModelCreateFlags + { + None = 0, + NoSetRenderState = 1 + } + public class Animation { public void* mNativeModelDefAnimation; @@ -90,14 +96,32 @@ namespace Beefy.gfx public void* mNativeModelDef; public float mFrameRate; public int32 mJointCount; - public Animation[] mAnims; - public Dictionary mAnimMap = new Dictionary(); + public Animation[] mAnims ~ DeleteContainerAndItems!(_); + public Dictionary mAnimMap = new Dictionary() ~ DeleteDictionaryAndKeys!(_); [CallingConvention(.Stdcall), CLink] - extern static void* Res_OpenFBX(String fileName, void* nativeVertexDef); + extern static void* Res_OpenFBX(char8* fileName, void* nativeVertexDef); + + [CallingConvention(.Stdcall), CLink] + extern static void* Res_OpenGLTF(char8* fileName, char8* baseDir, void* nativeVertexDef); + + [CallingConvention(.Stdcall), CLink] + extern static void* Res_OpenModel(char8* fileName, char8* baseDir, void* nativeVertexDef); [CallingConvention(.Stdcall), CLink] - extern static void* ModelDef_CreateModelInstance(void* nativeModel); + extern static void* ModelDef_CreateModelInstance(void* nativeModel, ModelCreateFlags flags); + + [CallingConvention(.Stdcall), CLink] + extern static void ModelDef_Compact(void* nativeModel); + + [CallingConvention(.Stdcall), CLink] + extern static void ModelDef_SetBaseDir(void* nativeModel, char8* baseDir); + + [CallingConvention(.Stdcall), CLink] + extern static char8* ModelDef_GetInfo(void* nativeModel); + + [CallingConvention(.Stdcall), CLink] + extern static void ModelDef_GetBounds(void* nativeModel, out Vector3 min, out Vector3 max); [CallingConvention(.Stdcall), CLink] extern static float ModelDef_GetFrameRate(void* nativeModel); @@ -111,6 +135,15 @@ namespace Beefy.gfx [CallingConvention(.Stdcall), CLink] extern static void* ModelDef_GetAnimation(void* nativeModel, int32 animIdx); + [CallingConvention(.Stdcall), CLink] + extern static void ModelDef_SetTextures(void* nativeModel, int32 meshIdx, int32 primitivesIdx, char8** paths, int32 pathCount); + + [CallingConvention(.Stdcall), CLink] + extern static bool ModelDef_RayIntersect(void* nativeModel, Matrix4 worldMtx, Vector3 origin, Vector3 vec, out Vector3 outIntersect, out float outDistance); + + [CallingConvention(.Stdcall), CLink] + extern static Span Res_SerializeModel(void* nativeModel); + this(void* nativeModelDef) { mNativeModelDef = nativeModelDef; @@ -129,17 +162,23 @@ namespace Beefy.gfx } } - public static ModelDef LoadModel(String fileName) + public static ModelDef LoadModel(String fileName, String baseDir) { - void* nativeModelDef = Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mNativeVertexDefinition); + void* nativeModelDef = null; + if (fileName.EndsWith(".gltf", .OrdinalIgnoreCase)) + nativeModelDef = Res_OpenGLTF(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition); + else if (fileName.EndsWith(".fbx", .OrdinalIgnoreCase)) + nativeModelDef = Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mNativeVertexDefinition); + else + nativeModelDef = Res_OpenModel(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition); if (nativeModelDef == null) return null; return new ModelDef(nativeModelDef); } - public ModelInstance CreateInstance() + public ModelInstance CreateInstance(ModelCreateFlags flags = .None) { - void* nativeModelInstance = ModelDef_CreateModelInstance(mNativeModelDef); + void* nativeModelInstance = ModelDef_CreateModelInstance(mNativeModelDef, flags); if (nativeModelInstance == null) return null; var modelInstance = new ModelInstance(nativeModelInstance, this); @@ -150,6 +189,42 @@ namespace Beefy.gfx { return mAnimMap[name]; } + + public void GetInfo(String str) + { + str.Append(ModelDef_GetInfo(mNativeModelDef)); + } + + public void GetBounds(out Vector3 min, out Vector3 max) + { + ModelDef_GetBounds(mNativeModelDef, out min, out max); + } + + public void Compact() + { + ModelDef_Compact(mNativeModelDef); + } + + public void SetBaseDir(StringView baseDir) + { + ModelDef_SetBaseDir(mNativeModelDef, baseDir.ToScopeCStr!()); + } + + public void SetTextures(int meshIdx, int primitivesIdx, Span paths) + { + ModelDef_SetTextures(mNativeModelDef, (.)meshIdx, (.)primitivesIdx, paths.Ptr, (.)paths.Length); + } + + public void Serialize(List data) + { + var span = Res_SerializeModel(mNativeModelDef); + data.AddRange(span); + } + + public bool RayIntersect(Matrix4 worldMtx, Vector3 origin, Vector3 vec, out Vector3 outIntersect, out float outDistance) + { + return ModelDef_RayIntersect(mNativeModelDef, worldMtx, origin, vec, out outIntersect, out outDistance); + } } public class ModelInstance : RenderCmd diff --git a/BeefLibs/Beefy2D/src/gfx/RenderState.bf b/BeefLibs/Beefy2D/src/gfx/RenderState.bf index b467da1d..4eb3b4fe 100644 --- a/BeefLibs/Beefy2D/src/gfx/RenderState.bf +++ b/BeefLibs/Beefy2D/src/gfx/RenderState.bf @@ -19,6 +19,12 @@ namespace Beefy.gfx Always } + public enum Topology + { + TriangleList, + LineList + } + #if !STUDIO_CLIENT public class RenderState { @@ -31,6 +37,12 @@ namespace Beefy.gfx [CallingConvention(.Stdcall), CLink] static extern void RenderState_SetClip(void* renderState, float x, float y, float width, float height); + [CallingConvention(.Stdcall), CLink] + static extern void RenderState_SetTexWrap(void* renderState, bool texWrap); + + [CallingConvention(.Stdcall), CLink] + static extern void RenderState_SetWireframe(void* renderState, bool wireframe); + [CallingConvention(.Stdcall), CLink] static extern void RenderState_DisableClip(void* renderState); @@ -43,6 +55,9 @@ namespace Beefy.gfx [CallingConvention(.Stdcall), CLink] static extern void RenderState_SetDepthWrite(void* nativeRenderState, int32 depthWrite); + [CallingConvention(.Stdcall), CLink] + static extern void RenderState_SetTopology(void* nativeRenderState, int32 topology); + public void* mNativeRenderState; public bool mIsFromDefaultRenderState; @@ -103,7 +118,31 @@ namespace Beefy.gfx else RenderState_DisableClip(mNativeRenderState); } - } + } + + public bool TexWrap + { + set + { + RenderState_SetTexWrap(mNativeRenderState, value); + } + } + + public bool Wireframe + { + set + { + RenderState_SetWireframe(mNativeRenderState, value); + } + } + + public Topology Topology + { + set + { + RenderState_SetTopology(mNativeRenderState, (.)value); + } + } } #else public class RenderState diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf index 41cd7c18..b8175ffa 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf @@ -424,9 +424,9 @@ namespace Beefy.theme.dark if (selEnd > selStart) { - String selPrevString = scope String(selStart); + String selPrevString = new:ScopedAlloc! String(selStart); selPrevString.Append(sectionText, 0, selStart); - String selIncludeString = scope String(selEnd); + String selIncludeString = new:ScopedAlloc! String(selEnd); selIncludeString.Append(sectionText, 0, selEnd); float selStartX = GetTabbedWidth(selPrevString, curX); @@ -473,7 +473,7 @@ namespace Beefy.theme.dark if (isInside) { - String subText = scope String(mCursorTextPos - lineDrawStart); + String subText = new:ScopedAlloc! String(mCursorTextPos - lineDrawStart); subText.Append(sectionText, 0, mCursorTextPos - lineDrawStart); aX = GetTabbedWidth(subText, curX); } @@ -587,7 +587,7 @@ namespace Beefy.theme.dark if (char8Count < lineText.Length) { - String subString = scope String(char8Count); + String subString = new:ScopedAlloc! String(char8Count); subString.Append(lineText, 0, char8Count); float subWidth = GetTabbedWidth(subString, 0); diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf index 794d2c8d..5f1dd96e 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf @@ -398,12 +398,12 @@ namespace Beefy.theme.dark func(mRightTab); } - public override TabButton AddTab(String label, float width, Widget content, bool ownsContent) + public override TabButton AddTab(String label, float width, Widget content, bool ownsContent, int insertIdx) { float useWidth = width; if (useWidth == 0) useWidth = DarkTheme.sDarkTheme.mSmallFont.GetWidth(label) + GS!(30); - return base.AddTab(label, useWidth, content, ownsContent); + return base.AddTab(label, useWidth, content, ownsContent, insertIdx); } public override void RemoveTab(TabButton tabButton, bool deleteTab = true) diff --git a/BeefLibs/Beefy2D/src/utils/Compression.bf b/BeefLibs/Beefy2D/src/utils/Compression.bf new file mode 100644 index 00000000..1ec1fe3d --- /dev/null +++ b/BeefLibs/Beefy2D/src/utils/Compression.bf @@ -0,0 +1,54 @@ +using System; +using System.Collections; + +namespace utils +{ + class Compression + { + [CallingConvention(.Stdcall), CLink] + extern static bool Compression_Compress(void* ptr, int size, void** outPtr, int* outSize); + + [CallingConvention(.Stdcall), CLink] + extern static bool Compression_Decompress(void* ptr, int size, void** outPtr, int* outSize); + + public static Result Compress(Span inData, List outData) + { + void* outPtr = null; + int outSize = 0; + if (!Compression_Compress(inData.Ptr, inData.Length, &outPtr, &outSize)) + return .Err; + outData.AddRange(.((.)outPtr, outSize)); + return .Ok; + } + + public static Result Compress(Span inData, String outData) + { + void* outPtr = null; + int outSize = 0; + if (!Compression_Compress(inData.Ptr, inData.Length, &outPtr, &outSize)) + return .Err; + outData.Insert(outData.Length, StringView((.)outPtr, outSize)); + return .Ok; + } + + public static Result Decompress(Span inData, List outData) + { + void* outPtr = null; + int outSize = 0; + if (!Compression_Decompress(inData.Ptr, inData.Length, &outPtr, &outSize)) + return .Err; + outData.AddRange(.((.)outPtr, outSize)); + return .Ok; + } + + public static Result Decompress(Span inData, String outData) + { + void* outPtr = null; + int outSize = 0; + if (!Compression_Decompress(inData.Ptr, inData.Length, &outPtr, &outSize)) + return .Err; + outData.Insert(outData.Length, StringView((.)outPtr, outSize)); + return .Ok; + } + } +} diff --git a/BeefLibs/Beefy2D/src/utils/StructuredData.bf b/BeefLibs/Beefy2D/src/utils/StructuredData.bf index bd631001..bf265510 100644 --- a/BeefLibs/Beefy2D/src/utils/StructuredData.bf +++ b/BeefLibs/Beefy2D/src/utils/StructuredData.bf @@ -405,7 +405,7 @@ namespace Beefy.utils return; switch (obj.GetType()) { - case typeof(Int32): val = (int32)obj; + case typeof(Int64): val = (.)(int64)obj; default: } } @@ -417,7 +417,7 @@ namespace Beefy.utils return; switch (obj.GetType()) { - case typeof(Int32): val = (int32)obj; + case typeof(Int64): val = (int64)obj; case typeof(Float): val = (float)obj; default: } @@ -470,6 +470,7 @@ namespace Beefy.utils Object val = Get(name); outString.Clear(); + if (val is uint64) val.ToString(outString); @@ -493,58 +494,112 @@ namespace Beefy.utils return; } - public int32 GetInt(String name, int32 theDefault = 0) - { - Object aVal = Get(name); - if ((aVal == null) || (!(aVal is int32))) - return theDefault; - return (int32)aVal; - } - - public int64 GetLong(String name, int64 theDefault = 0) - { - Object aVal = Get(name); - - if (aVal is int32) - return (int64)(int32)aVal; - - if ((aVal == null) || (!(aVal is int64))) - return theDefault; - return (int64)aVal; - } - - public uint64 GetULong(String name, uint64 theDefault = 0) - { - Object aVal = Get(name); - - if (aVal is int32) - return (uint64)(int32)aVal; - - if ((aVal == null) || (!(aVal is uint64))) - return theDefault; - return (uint64)aVal; - } - - public float GetFloat(String name, float theDefault = 0) + public int32 GetInt(String name, int32 defaultVal = 0) { Object val = Get(name); - if (val == null) - return theDefault; + if (val == null) + return defaultVal; switch (val.GetType()) { - case typeof(Float): return (float)val; - case typeof(Int32): return (int32)val; - case typeof(Int): return (int)val; - default: return theDefault; + case typeof(Float): return (.)(float)val; + case typeof(Int32): return (.)(int32)val; + case typeof(Int64): return (.)(int64)val; + case typeof(Int): return (.)(int)val; + case typeof(String): + if (int32.Parse((String)val) case .Ok(var fVal)) + return (.)fVal; + return defaultVal; + case typeof(StringView): + if (int32.Parse((StringView)val) case .Ok(var fVal)) + return (.)fVal; + return defaultVal; + default: return defaultVal; } } - public bool GetBool(String name, bool theDefault = false) + public int64 GetLong(String name, int64 defaultVal = 0) { - Object aVal = Get(name); - if ((aVal == null) || (!(aVal is bool))) - return theDefault; - return (bool)aVal; + Object val = Get(name); + if (val == null) + return defaultVal; + switch (val.GetType()) + { + case typeof(Float): return (.)(float)val; + case typeof(Int32): return (.)(int32)val; + case typeof(Int64): return (.)(int64)val; + case typeof(Int): return (.)(int)val; + case typeof(String): + if (int64.Parse((String)val) case .Ok(var parsedVal)) + return (.)parsedVal; + return defaultVal; + case typeof(StringView): + if (int64.Parse((StringView)val) case .Ok(var parsedVal)) + return (.)parsedVal; + return defaultVal; + default: return defaultVal; + } + } + + public uint64 GetULong(String name, uint64 defaultVal = 0) + { + Object val = Get(name); + if (val == null) + return defaultVal; + switch (val.GetType()) + { + case typeof(Float): return (.)(float)val; + case typeof(Int32): return (.)(int32)val; + case typeof(Int64): return (.)(int64)val; + case typeof(Int): return (.)(int)val; + case typeof(String): + if (int64.Parse((String)val) case .Ok(var parsedVal)) + return (.)parsedVal; + return defaultVal; + case typeof(StringView): + if (int64.Parse((StringView)val) case .Ok(var parsedVal)) + return (.)parsedVal; + return defaultVal; + default: return defaultVal; + } + } + + public float GetFloat(String name, float defaultVal = 0) + { + Object val = Get(name); + if (val == null) + return defaultVal; + switch (val.GetType()) + { + case typeof(Float): return (.)(float)val; + case typeof(Int32): return (.)(int32)val; + case typeof(Int64): return (.)(int64)val; + case typeof(Int): return (.)(int)val; + case typeof(String): + if (float.Parse((String)val) case .Ok(var parsedVal)) + return parsedVal; + return defaultVal; + case typeof(StringView): + if (float.Parse((StringView)val) case .Ok(var parsedVal)) + return parsedVal; + return defaultVal; + default: return defaultVal; + } + } + + public bool GetBool(String name, bool defaultVal = false) + { + Object val = Get(name); + if (val == null) + return defaultVal; + switch (val.GetType()) + { + case typeof(Boolean): return (bool)val; + case typeof(String): + if (bool.Parse((String)val) case .Ok(var parsedVal)) + return (.)parsedVal; + return defaultVal; + default: return defaultVal; + } } public T GetEnum(String name, T defaultVal = default(T)) where T : enum @@ -634,9 +689,9 @@ namespace Beefy.utils public int32 GetCurInt(int32 theDefault = 0) { Object aVal = GetCurrent(); - if ((aVal == null) || (!(aVal is int32))) + if ((aVal == null) || (!(aVal is int64))) return theDefault; - return (int32)aVal; + return (.)(int64)aVal; } public uint32 GetCurUInt(uint32 theDefault = 0) @@ -1786,7 +1841,7 @@ namespace Beefy.utils } else { - var parseVal = int32.Parse(strView); + var parseVal = int64.Parse(strView); if (parseVal case .Ok(var intVal)) aValue = new:mBumpAllocator box intVal; else @@ -2187,7 +2242,7 @@ namespace Beefy.utils } } - switch (Int32.Parse(value)) + switch (Int64.Parse(value)) { case .Err: return null; case .Ok(let num): return new:mBumpAllocator box num; @@ -2344,6 +2399,223 @@ namespace Beefy.utils } } + Result LoadXMLHelper(String contentStr, Values values, ref int32 idx, ref int32 lineNum) + { + LoadSection loadRoot = scope LoadSection(); + loadRoot.mSectionDict = new Dictionary(); + loadRoot.mCurrentEntry = CurrentEntry(values); + //LoadSection loadSection = loadRoot; + //CurrentEntry currentEntry = default; + + char8* cPtr = contentStr.CStr(); + + char8 NextChar() + { + char8 c = cPtr[idx]; + if (c != 0) + idx++; + return c; + } + + char8* GetCharPtr() + { + return &cPtr[idx]; + } + + char8 PeekNextChar() + { + return cPtr[idx]; + } + + void EatWhiteSpace() + { + while (true) + { + char8 nextC = PeekNextChar(); + if ((nextC != ' ') && (nextC != '\t')) + return; + idx++; + } + } + + void ReadSection(ref CurrentEntry arrayEntry) + { + char8* dataStart = null; + + void FlushData() + { + if (dataStart != null) + { + StringView valueSV = StringView(dataStart, GetCharPtr() - dataStart - 1); + valueSV.Trim(); + String value = new:mBumpAllocator String(valueSV); + DoAdd(ref arrayEntry, value); + } + + dataStart = null; + } + + MainLoop: while (true) + { + char8 c = NextChar(); + if (c == 0) + { + break; + } + + if (c == '<') + { + FlushData(); + + EatWhiteSpace(); + c = PeekNextChar(); + if (c == '/') + { + // Is closing + while (true) + { + c = NextChar(); + if ((c == 0) || (c == '>')) + return; + } + } + + NamedValues childNamedValues = null; + CurrentEntry childTableEntry = default; + + void EnsureChildEntry() + { + if (childNamedValues != null) + return; + childNamedValues = new:mBumpAllocator NamedValues(); + childTableEntry = CurrentEntry(childNamedValues); + DoAdd(ref arrayEntry, childNamedValues); + } + + char8* namePtr = null; + char8* nameEndPtr = null; + char8* equalPtr = null; + char8* valuePtr = null; + + while (true) + { + c = NextChar(); + if (c.IsWhiteSpace) + { + if ((namePtr != null) && (nameEndPtr == null)) + nameEndPtr = GetCharPtr() - 1; + continue; + } + + if (valuePtr != null) + { + if (c == '"') + { + EnsureChildEntry(); + StringView name = StringView(namePtr, nameEndPtr - namePtr + 1); + name.Trim(); + StringView valueSV = StringView(valuePtr, GetCharPtr() - valuePtr - 1); + String value = new:mBumpAllocator String(valueSV); + DoAdd(ref childTableEntry, name, value); + + namePtr = null; + nameEndPtr = null; + equalPtr = null; + valuePtr = null; + continue; + } + continue; + } + + if ((c == '?') || (c == '/')) + { + // Wait for close. Not nested. + while (true) + { + c = NextChar(); + if ((c == 0) || (c == '>')) + continue MainLoop; + } + } + + if (c == '>') + { + // Closing, but we're nested + EnsureChildEntry(); + Values childArrayValues = new:mBumpAllocator Values(); + CurrentEntry childArrayEntry = CurrentEntry(childArrayValues); + DoAdd(ref childTableEntry, ".", childArrayValues); + + ReadSection(ref childArrayEntry); + continue MainLoop; + } + + if (namePtr == null) + { + namePtr = GetCharPtr() - 1; + continue; + } + + if (equalPtr == null) + { + if (c == '=') + { + equalPtr = GetCharPtr() - 1; + if (nameEndPtr == null) + nameEndPtr = equalPtr - 1; + continue; + } + } + else + { + if (c == '"') + { + valuePtr = GetCharPtr(); + continue; + } + } + + if (nameEndPtr == null) + continue; + + // Flush + StringView name = StringView(namePtr, nameEndPtr - namePtr + 1); + name.Trim(); + + if (name.IsEmpty) + continue; + + EnsureChildEntry(); + if (childTableEntry.mLastKey == -1) + { + Object value = new:mBumpAllocator String(name); + DoAdd(ref childTableEntry, "", value); + } + else + { + Object value = new:mBumpAllocator box true; + DoAdd(ref childTableEntry, name, value); + } + + namePtr = null; + nameEndPtr = null; + idx--; + continue; + } + } + + if ((!c.IsWhiteSpace) && (dataStart == null)) + dataStart = GetCharPtr() - 1; + } + + FlushData(); + } + + ReadSection(ref loadRoot.mCurrentEntry); + + return .Ok; + } + protected Result Load() { EnsureHasData(); @@ -2354,13 +2626,34 @@ namespace Beefy.utils mNextKeys.Reserve(guessItems); bool isJson = false; + bool isXml = false; + bool mayBeJsonArray = false; for (char8 c in mSource.RawChars) { if (c.IsWhiteSpace) continue; - if (c == '{') - isJson = true; - break; + + if (mayBeJsonArray) + { + if (c == '[') + continue; // Still ambiguous + if ((c == '{') || (c == '"')) + isJson = true; + break; + } + else + { + if (c == '{') + isJson = true; + if (c == '<') + isXml = true; + if (c == '[') + { + mayBeJsonArray = true; + continue; + } + break; + } } int32 aLineNum = 1; @@ -2373,6 +2666,14 @@ namespace Beefy.utils return .Err(err); objResult = result.Get(); } + else if (isXml) + { + var values = new:mBumpAllocator Values(); + let result = LoadXMLHelper(mSource, values, ref anIdx, ref aLineNum); + if (result case .Err(var err)) + return .Err(err); + objResult = values; + } else { var values = new:mBumpAllocator NamedValues(); diff --git a/BeefLibs/Beefy2D/src/widgets/EditWidget.bf b/BeefLibs/Beefy2D/src/widgets/EditWidget.bf index 70704f0d..cf92c840 100644 --- a/BeefLibs/Beefy2D/src/widgets/EditWidget.bf +++ b/BeefLibs/Beefy2D/src/widgets/EditWidget.bf @@ -459,7 +459,7 @@ namespace Beefy.widgets public UndoManager mUndoManager = new UndoManager() ~ delete _; public int32 mNextCharId = 1; // public int32 mCurTextVersionId = 1; // Changes when text is modified - //public int mCurComplexChangeId = 1; // Changes when text is modified by more than a single-char8acter insertion or deletion + //public int mCurComplexChangeId = 1; // Changes when text is modified by more than a single-character insertion or deletion public List mUsers = new List() ~ delete _; @@ -1985,6 +1985,9 @@ namespace Beefy.widgets { scope AutoBeefPerf("EWC.Undo"); + if (CheckReadOnly()) + return; + //Profiler.StartSampling(); if (WantsUndo) mData.mUndoManager.Undo(); @@ -1995,6 +1998,9 @@ namespace Beefy.widgets { scope AutoBeefPerf("EWC.Redo"); + if (CheckReadOnly()) + return; + if (WantsUndo) mData.mUndoManager.Redo(); } diff --git a/BeefLibs/Beefy2D/src/widgets/KeyCode.bf b/BeefLibs/Beefy2D/src/widgets/KeyCode.bf index 4369d2a3..74c32e7d 100644 --- a/BeefLibs/Beefy2D/src/widgets/KeyCode.bf +++ b/BeefLibs/Beefy2D/src/widgets/KeyCode.bf @@ -82,6 +82,8 @@ namespace Beefy.widgets F12 = 0x7B, Numlock = 0x90, Scroll = 0x91, + RAlt = 0xA5, + RMenu = 0xA5, Semicolon = 0xBA, Equals = 0xBB, Comma = 0xBC, diff --git a/BeefLibs/Beefy2D/src/widgets/ListView.bf b/BeefLibs/Beefy2D/src/widgets/ListView.bf index e4360ee8..839cc575 100644 --- a/BeefLibs/Beefy2D/src/widgets/ListView.bf +++ b/BeefLibs/Beefy2D/src/widgets/ListView.bf @@ -339,7 +339,7 @@ namespace Beefy.widgets } else { - if (item.Selected) + if ((item.Selected) && (item.mMouseOver)) { item.mOnMouseUp.AddFront(new => ItemMouseUpHandler); diff --git a/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf b/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf index 7e03fda2..afa18417 100644 --- a/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf +++ b/BeefLibs/Beefy2D/src/widgets/ScrollableWidget.bf @@ -103,13 +103,13 @@ namespace Beefy.widgets } } - public bool HorzScrollTo(double horzPos) + public bool HorzScrollTo(double horzPos, bool immediate = false) { double aHorzPos = Math.Max(0, Math.Min(horzPos, mScrollContent.mWidth - mScrollContentContainer.mWidth)); if (aHorzPos == mHorzPos.mDest) return false; - mHorzPos.Set(aHorzPos); + mHorzPos.Set(aHorzPos, immediate); if (mHorzScrollbar != null) { mHorzScrollbar.mContentPos = mHorzPos.v; diff --git a/BeefLibs/Beefy2D/src/widgets/TabbedView.bf b/BeefLibs/Beefy2D/src/widgets/TabbedView.bf index 59e00f54..6bc23dd0 100644 --- a/BeefLibs/Beefy2D/src/widgets/TabbedView.bf +++ b/BeefLibs/Beefy2D/src/widgets/TabbedView.bf @@ -162,7 +162,7 @@ namespace Beefy.widgets if ((mSrcDraggingWindow != null) && (mSrcDraggingWindow.mCaptureWidget != null)) mSrcDraggingWindow.ReleaseMouseCaptures(); - mTabbedView.mParentDockingFrame.GetRootDockingFrame().HideDragTarget(this, !mDragHelper.mAborted); + mTabbedView.mParentDockingFrame?.GetRootDockingFrame().HideDragTarget(this, !mDragHelper.mAborted); if (mNewDraggingWindow != null) { mNewDraggingWindow.mOnWindowLostFocus.Remove(scope => WindowDragLostFocusHandler, true); @@ -176,7 +176,7 @@ namespace Beefy.widgets public void MouseDrag(float x, float y, float dX, float dY) { - mTabbedView.mParentDockingFrame.GetRootDockingFrame().ShowDragTarget(this); + mTabbedView.mParentDockingFrame?.GetRootDockingFrame().ShowDragTarget(this); } public override void MouseUp(float x, float y, int32 btn) @@ -198,6 +198,8 @@ namespace Beefy.widgets public virtual bool IsTotalWindowContent() { + if (mTabbedView.mParentDockingFrame == null) + return false; return (mTabbedView.mParentDockingFrame.mParentDockingFrame == null) && (mTabbedView.mParentDockingFrame.GetDockedWindowCount() == 1) && (mTabbedView.GetTabCount() == 1) && @@ -318,7 +320,7 @@ namespace Beefy.widgets //tabbedView.mSharedData.mOpenNewWindowDelegate = mTabbedView.mSharedData.mOpenNewWindowDelegate; tabbedView.SetRequestedSize(mTabbedView.mWidth, mTabbedView.mHeight); mTabbedView.RemoveTab(this, false); - tabbedView.AddTab(this); + tabbedView.AddTab(this, 0); float rootX; float rootY; @@ -470,7 +472,7 @@ namespace Beefy.widgets return activeTab; } - public virtual TabButton AddTab(String label, float width, Widget content, bool ownsContent) + public virtual TabButton AddTab(String label, float width, Widget content, bool ownsContent, int insertIdx) { TabButton aTabButton = CreateTabButton(); aTabButton.mTabbedView = this; @@ -479,7 +481,7 @@ namespace Beefy.widgets aTabButton.mWantWidth = width; aTabButton.mHeight = mTabHeight; aTabButton.mContent = content; - AddTab(aTabButton); + AddTab(aTabButton, insertIdx); return aTabButton; } @@ -499,7 +501,7 @@ namespace Beefy.widgets return bestIdx; } - public virtual void AddTab(TabButton tabButton, int insertIdx = 0) + public virtual void AddTab(TabButton tabButton, int insertIdx) { AddWidget(tabButton); mTabs.Insert(insertIdx, tabButton); diff --git a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf index 5c434757..9aa86a93 100644 --- a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf +++ b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf @@ -121,7 +121,7 @@ namespace Beefy.widgets KeyFlags keyFlags = default; if (IsKeyDown(KeyCode.Shift)) keyFlags |= KeyFlags.Shift; - if (IsKeyDown(KeyCode.Control)) + if ((IsKeyDown(KeyCode.Control)) && (!IsKeyDown(KeyCode.RAlt))) keyFlags |= KeyFlags.Ctrl; if (IsKeyDown(KeyCode.Menu)) keyFlags |= KeyFlags.Alt; @@ -137,7 +137,7 @@ namespace Beefy.widgets { if (mRootWidget == null) return; - + base.Draw(g); mRootWidget.DrawAll(g); } diff --git a/BeefLibs/SDL2/src/SDLApp.bf b/BeefLibs/SDL2/src/SDLApp.bf index cc95a8dd..90026bbd 100644 --- a/BeefLibs/SDL2/src/SDLApp.bf +++ b/BeefLibs/SDL2/src/SDLApp.bf @@ -192,7 +192,8 @@ namespace SDL2 return; int32 channel = SDLMixer.PlayChannel(-1, sound.mChunk, 0); - //SDLMixer.SetPanning() + if (channel < 0) + return; SDLMixer.Volume(channel, (int32)(volume * 128)); } diff --git a/BeefLibs/corlib/src/Array.bf b/BeefLibs/corlib/src/Array.bf index bf197ba2..7a250fed 100644 --- a/BeefLibs/corlib/src/Array.bf +++ b/BeefLibs/corlib/src/Array.bf @@ -35,7 +35,8 @@ namespace System } public bool IsEmpty - { + { + [Inline] get { return mLength == 0; @@ -259,6 +260,46 @@ namespace System } } + public ref T this[Index index] + { + [Checked, Inline] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + if ((uint)idx >= (uint)mLength) + Internal.ThrowIndexOutOfRange(1); + return ref (&mFirstElement)[idx]; + } + + [Unchecked, Inline] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + return ref (&mFirstElement)[idx]; + } + } + + public Span this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + return Span(&mFirstElement, mLength)[range]; + } + } + [Inline] public T* CArray() { @@ -276,7 +317,7 @@ namespace System Debug.Assert(length >= 0); Debug.Assert((uint)srcOffset + (uint)length <= (uint)mLength); Debug.Assert((uint)dstOffset + (uint)length <= (uint)arrayTo.mLength); - Internal.MemCpy(&arrayTo.GetRef(dstOffset), &GetRef(srcOffset), strideof(T) * length, alignof(T)); + Internal.MemMove(&arrayTo.GetRef(dstOffset), &GetRef(srcOffset), strideof(T) * length, alignof(T)); } public void CopyTo(T2[] arrayTo, int srcOffset, int dstOffset, int length) where T2 : operator explicit T @@ -299,17 +340,18 @@ namespace System public void CopyTo(Span destination) { Debug.Assert(destination.[Friend]mLength >= mLength); - Internal.MemCpy(destination.Ptr, &GetRef(0), strideof(T) * mLength, alignof(T)); + Internal.MemMove(destination.Ptr, &GetRef(0), strideof(T) * mLength, alignof(T)); } public void CopyTo(Span destination, int srcOffset) { Debug.Assert((uint)srcOffset + (uint)destination.[Friend]mLength <= (uint)mLength); - Internal.MemCpy(destination.Ptr, &GetRef(srcOffset), strideof(T) * (destination.[Friend]mLength - srcOffset), alignof(T)); + Internal.MemMove(destination.Ptr, &GetRef(srcOffset), strideof(T) * (destination.[Friend]mLength - srcOffset), alignof(T)); } public void CopyTo(Span destination, int srcOffset) where T2 : operator explicit T { + //TODO: Handle src/dest overlap (MemMove) Debug.Assert((uint)srcOffset + (uint)destination.[Friend]mLength <= (uint)mLength); var ptr = destination.[Friend]mPtr; for (int i = 0; i < destination.[Friend]mLength; i++) diff --git a/BeefLibs/corlib/src/Char16.bf b/BeefLibs/corlib/src/Char16.bf index e9aa835e..48492aed 100644 --- a/BeefLibs/corlib/src/Char16.bf +++ b/BeefLibs/corlib/src/Char16.bf @@ -1,6 +1,6 @@ namespace System { - struct Char16 : char16, IHashable, IIsNaN + struct Char16 : char16, ICharacter, IHashable, IIsNaN { const int UNICODE_PLANE00_END = 0x00ffff; // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. diff --git a/BeefLibs/corlib/src/Char32.bf b/BeefLibs/corlib/src/Char32.bf index 0ea8a01a..fad3bf8d 100644 --- a/BeefLibs/corlib/src/Char32.bf +++ b/BeefLibs/corlib/src/Char32.bf @@ -1,6 +1,6 @@ namespace System { - struct Char32 : char32, IHashable, IIsNaN + struct Char32 : char32, ICharacter, IHashable, IIsNaN { public int GetHashCode() { diff --git a/BeefLibs/corlib/src/Char8.bf b/BeefLibs/corlib/src/Char8.bf index 56220618..4b19908f 100644 --- a/BeefLibs/corlib/src/Char8.bf +++ b/BeefLibs/corlib/src/Char8.bf @@ -1,7 +1,7 @@ namespace System { #unwarn - struct Char8 : char8, IHashable, IIsNaN + struct Char8 : char8, ICharacter, IHashable, IIsNaN { bool IIsNaN.IsNaN { diff --git a/BeefLibs/corlib/src/Collections/Dictionary.bf b/BeefLibs/corlib/src/Collections/Dictionary.bf index 32fca56a..493a3309 100644 --- a/BeefLibs/corlib/src/Collections/Dictionary.bf +++ b/BeefLibs/corlib/src/Collections/Dictionary.bf @@ -26,7 +26,7 @@ namespace System.Collections { public TKey mKey; // Key of entry public TValue mValue; // Value of entry - public int32 mHashCode; // Lower 31 bits of hash code, -1 if unused + public int_cosize mHashCode; // some bits of hash code, -1 if unused public int_cosize mNext; // Index of next entry, -1 if last } @@ -34,7 +34,7 @@ namespace System.Collections Entry* mEntries; int_cosize mAllocSize; int_cosize mCount; - int_cosize mFreeList; + int_cosize mFreeList; int_cosize mFreeCount; #if VERSION_DICTIONARY private int32 mVersion; @@ -258,21 +258,9 @@ namespace System.Collections public bool ContainsValue(TValue value) { - if (value == null) + for (int_cosize i = 0; i < mCount; i++) { - for (int_cosize i = 0; i < mCount; i++) - { - if (mEntries[i].mHashCode >= 0 && mEntries[i].mValue == null) return true; - } - } - else - { - //TODO: IMPORTANT! - /*EqualityComparer c = EqualityComparer.Default; - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true; - }*/ + if (mEntries[i].mHashCode >= 0 && mEntries[i].mValue == value) return true; } return false; } @@ -289,6 +277,19 @@ namespace System.Collections return false; } } + + public bool ContainsAlt((TAltKey key, TValue value) kvPair) where TAltKey : IHashable where bool : operator TKey == TAltKey + { + TValue value; + if (TryGetValueAlt(kvPair.key, out value)) + { + return value == kvPair.value; + } + else + { + return false; + } + } public void CopyTo(Span kvPair) { @@ -306,12 +307,21 @@ namespace System.Collections return Enumerator(this, Enumerator.[Friend]KeyValuePair); } + static int_cosize GetKeyHash(int hashCode) + { + if (sizeof(int) == 4) + return (int32)hashCode & 0x7FFFFFFF; + if (sizeof(int_cosize) == 8) + return (int_cosize)(hashCode & 0x7FFFFFFF'FFFFFFFFL); + return ((int32)hashCode ^ (int32)((int64)hashCode >> 33)) & 0x7FFFFFFF; + } + [DisableObjectAccessChecks] private int FindEntry(TKey key) { if (mBuckets != null) { - int hashCode = key.GetHashCode() & 0x7FFFFFFF; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); for (int i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext) { if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) return i; @@ -329,7 +339,7 @@ namespace System.Collections { if (mBuckets != null) { - int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext) { if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) return i; @@ -350,10 +360,10 @@ namespace System.Collections private void Insert(TKey key, TValue value, bool add) { if (mBuckets == null) Initialize(0); - int32 hashCode = (int32)key.GetHashCode() & 0x7FFFFFFF; - int_cosize targetBucket = hashCode % (int_cosize)mAllocSize; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); + int targetBucket = hashCode % mAllocSize; - for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext) + for (int i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext) { if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) { @@ -402,7 +412,7 @@ namespace System.Collections private bool Insert(TKey key, bool add, out TKey* keyPtr, out TValue* valuePtr, Entry** outOldData) { if (mBuckets == null) Initialize(0); - int32 hashCode = (int32)key.GetHashCode() & 0x7FFFFFFF; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); int_cosize targetBucket = hashCode % (int_cosize)mAllocSize; for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext) { @@ -458,9 +468,9 @@ namespace System.Collections private bool InsertAlt(TAltKey key, bool add, out TKey* keyPtr, out TValue* valuePtr, Entry** outOldData) where TAltKey : IHashable where bool : operator TKey == TAltKey { if (mBuckets == null) Initialize(0); - int32 hashCode = (int32)key.GetHashCode() & 0x7FFFFFFF; - int_cosize targetBucket = hashCode % (int_cosize)mAllocSize; - for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext) + int_cosize hashCode = GetKeyHash(key.GetHashCode()); + int targetBucket = hashCode % (int_cosize)mAllocSize; + for (int i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext) { if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) { @@ -541,7 +551,7 @@ namespace System.Collections { if (newEntries[i].mHashCode != -1) { - newEntries[i].mHashCode = (int32)newEntries[i].mKey.GetHashCode() & 0x7FFFFFFF; + newEntries[i].mHashCode = GetKeyHash(newEntries[i].mKey.GetHashCode()); } } } @@ -572,7 +582,7 @@ namespace System.Collections { if (mBuckets != null) { - int hashCode = key.GetHashCode() & 0x7FFFFFFF; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); int bucket = hashCode % (int_cosize)mAllocSize; int last = -1; for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext) @@ -609,7 +619,7 @@ namespace System.Collections { if (mBuckets != null) { - int hashCode = key.GetHashCode() & 0x7FFFFFFF; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); int bucket = hashCode % (int_cosize)mAllocSize; int last = -1; for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext) @@ -653,8 +663,8 @@ namespace System.Collections if (mBuckets != null) { - int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF; - int_cosize bucket = hashCode % (int_cosize)mAllocSize; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); + int bucket = hashCode % (int_cosize)mAllocSize; int_cosize last = -1; for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext) { @@ -694,8 +704,8 @@ namespace System.Collections if (mBuckets != null) { - int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF; - int_cosize bucket = hashCode % (int_cosize)mAllocSize; + int_cosize hashCode = GetKeyHash(key.GetHashCode()); + int bucket = hashCode % (int_cosize)mAllocSize; int_cosize last = -1; for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext) { @@ -742,6 +752,18 @@ namespace System.Collections return false; } + public bool TryGetValueAlt(TAltKey key, out TValue value) where TAltKey : IHashable where bool : operator TKey == TAltKey + { + int_cosize i = (int_cosize)FindEntryAlt(key); + if (i >= 0) + { + value = mEntries[i].mValue; + return true; + } + value = default(TValue); + return false; + } + public bool TryGet(TKey key, out TKey matchKey, out TValue value) { int_cosize i = (int_cosize)FindEntry(key); @@ -876,6 +898,16 @@ namespace System.Collections mDictionary.mEntries[mCurrentIndex].mValue = value; } + public void Remove() mut + { + int_cosize curIdx = mIndex - 1; + mDictionary.Remove(mDictionary.mEntries[curIdx].mKey); +#if VERSION_DICTIONARY + mVersion = mDictionary.mVersion; +#endif + mIndex = curIdx; + } + public void Reset() mut { #if VERSION_DICTIONARY @@ -1017,6 +1049,16 @@ namespace System.Collections { } + public void Remove() mut + { + int_cosize curIdx = mIndex - 1; + mDictionary.Remove(mDictionary.mEntries[curIdx].mKey); +#if VERSION_DICTIONARY + mVersion = mDictionary.mVersion; +#endif + mIndex = curIdx; + } + public void Reset() mut { #if VERSION_DICTIONARY @@ -1113,6 +1155,16 @@ namespace System.Collections { } + public void Remove() mut + { + int_cosize curIdx = mIndex - 1; + mDictionary.Remove(mDictionary.mEntries[curIdx].mKey); +#if VERSION_DICTIONARY + mVersion = mDictionary.mVersion; +#endif + mIndex = curIdx; + } + public void Reset() mut { #if VERSION_DICTIONARY diff --git a/BeefLibs/corlib/src/Collections/HashSet.bf b/BeefLibs/corlib/src/Collections/HashSet.bf index 3255166f..08c80602 100644 --- a/BeefLibs/corlib/src/Collections/HashSet.bf +++ b/BeefLibs/corlib/src/Collections/HashSet.bf @@ -196,7 +196,13 @@ namespace System.Collections return false; } + [Obsolete("Method renamed to ContainsAlt", false)] public bool ContainsWith(TAltKey item) where TAltKey : IHashable where bool : operator T == TAltKey + { + return ContainsAlt(item); + } + + public bool ContainsAlt(TAltKey item) where TAltKey : IHashable where bool : operator T == TAltKey { if (mBuckets != null) { @@ -346,6 +352,11 @@ namespace System.Collections return Add(item, out entryPtr); } + public bool TryAddAlt(TAltKey item, out T* entryPtr) where TAltKey : IHashable where bool : operator T == TAltKey + { + return AddAlt(item, out entryPtr); + } + public void CopyTo(T[] array) { CopyTo(array, 0, mCount); } public void CopyTo(T[] array, int32 arrayIndex, int32 count) @@ -632,6 +643,71 @@ namespace System.Collections mVersion++; #endif +#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE + if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(m_comparer)) { + m_comparer = (IEqualityComparer) HashHelpers.GetRandomizedEqualityComparer(m_comparer); + SetCapacity(m_buckets.Length, true); + } +#endif // FEATURE_RANDOMIZED_STRING_HASHING + + entryPtr = &mSlots[index].mValue; + return true; + } + + /// Adds value to HashSet if not contained already + /// @return true if added and false if already present + /// @param value value to find + /// @param entryPtr ponter to entry + public bool AddAlt(TAltKey value, out T* entryPtr) where TAltKey : IHashable where bool : operator T == TAltKey + { + if (mBuckets == null) + { + Initialize(0); + } + + int32 hashCode = (int32)InternalGetHashCodeAlt(value); + int32 bucket = hashCode % (int32)mBuckets.Count; +#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE + int collisionCount = 0; +#endif + for (int32 i = mBuckets[hashCode % mBuckets.Count] - 1; i >= 0; i = mSlots[i].mNext) + { + if (mSlots[i].mHashCode == hashCode && /*m_comparer.Equals*/(mSlots[i].mValue == value)) + { + entryPtr = &mSlots[i].mValue; + return false; + } +#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE + collisionCount++; +#endif + } + + int32 index; + if (mFreeList >= 0) + { + index = mFreeList; + mFreeList = mSlots[index].mNext; + } + else + { + if (mLastIndex == mSlots.Count) + { + IncreaseCapacity(); + // this will change during resize + bucket = hashCode % (int32)mBuckets.Count; + } + index = mLastIndex; + mLastIndex++; + } + mSlots[index].mHashCode = hashCode; + //mSlots[index].mValue = value; + mSlots[index].mNext = mBuckets[bucket] - 1; + mBuckets[bucket] = index + 1; + mCount++; +#if VERSION_HASHSET + mVersion++; +#endif + #if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(m_comparer)) { m_comparer = (IEqualityComparer) HashHelpers.GetRandomizedEqualityComparer(m_comparer); @@ -1071,6 +1147,15 @@ namespace System.Collections return item.GetHashCode() & Lower31BitMask; } + private int InternalGetHashCodeAlt(TAltKey item) where TAltKey : IHashable + { + if (item == null) + { + return 0; + } + return item.GetHashCode() & Lower31BitMask; + } + #endregion // used for set checking operations (using enumerables) that rely on counting diff --git a/BeefLibs/corlib/src/Collections/List.bf b/BeefLibs/corlib/src/Collections/List.bf index 605cb723..079972f2 100644 --- a/BeefLibs/corlib/src/Collections/List.bf +++ b/BeefLibs/corlib/src/Collections/List.bf @@ -148,6 +148,7 @@ namespace System.Collections public int Count { + [Inline] get { return mSize; @@ -210,6 +211,76 @@ namespace System.Collections } } + public ref T this[Index index] + { + [Checked] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mSize - offset; + } + Runtime.Assert((uint)idx < (uint)mSize); + return ref mItems[idx]; + } + + [Unchecked, Inline] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mSize - offset; + } + return ref mItems[idx]; + } + + [Checked] + set + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mSize - offset; + } + Runtime.Assert((uint)idx < (uint)mSize); + mItems[idx] = value; +#if VERSION_LIST + mVersion++; +#endif + } + + [Unchecked, Inline] + set + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mSize - offset; + } + mItems[idx] = value; +#if VERSION_LIST + mVersion++; +#endif + } + } + + public Span this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + return Span(mItems, mSize)[range]; + } + } + public ref T Front { get @@ -534,7 +605,7 @@ namespace System.Collections if (mSize == AllocSize) EnsureCapacity(mSize + 1, true); if (index < mSize) { - Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T)); + Internal.MemMove(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T)); } mItems[index] = item; mSize++; @@ -553,7 +624,7 @@ namespace System.Collections if (mSize + addCount > AllocSize) EnsureCapacity(mSize + addCount, true); if (index < mSize) { - Internal.MemCpy(mItems + index + addCount, mItems + index, (mSize - index) * strideof(T), alignof(T)); + Internal.MemMove(mItems + index + addCount, mItems + index, (mSize - index) * strideof(T), alignof(T)); } Internal.MemCpy(mItems + index, items.Ptr, addCount * strideof(T)); mSize += (int_cosize)addCount; @@ -567,7 +638,7 @@ namespace System.Collections Debug.Assert((uint)index < (uint)mSize); if (index < mSize - 1) { - Internal.MemCpy(mItems + index, mItems + index + 1, (mSize - index - 1) * strideof(T), alignof(T)); + Internal.MemMove(mItems + index, mItems + index + 1, (mSize - index - 1) * strideof(T), alignof(T)); } mSize--; #if VERSION_LIST @@ -930,6 +1001,59 @@ namespace System.Collections } } + extension List where T : String + { + public bool Contains(T item, StringComparison comparison) + { + if (item == null) + { + for (int i = 0; i < mSize; i++) + if (mItems[i] == null) + return true; + return false; + } + else + { + for (int i = 0; i < mSize; i++) + if (mItems[i].Equals(item, comparison)) + return true; + return false; + } + } + + public int IndexOf(T item, StringComparison comparison) + { + for (int i = 0; i < mSize; i++) + if (mItems[i].Equals(item, comparison)) + return i; + return -1; + } + + public int IndexOf(T item, int index, StringComparison comparison) + { + for (int i = index; i < mSize; i++) + if (mItems[i].Equals(item, comparison)) + return i; + return -1; + } + + public int IndexOf(T item, int index, int count, StringComparison comparison) + { + for (int i = index; i < index + count; i++) + if (mItems[i].Equals(item, comparison)) + return i; + return -1; + } + + public int LastIndexOf(T item, StringComparison comparison) + { + for (int i = mSize - 1; i >= 0; i--) + if (mItems[i].Equals(item, comparison)) + return i; + return -1; + } + } + class ListWithAlloc : List { IRawAllocator mAlloc; diff --git a/BeefLibs/corlib/src/Compiler.bf b/BeefLibs/corlib/src/Compiler.bf index 2ec01415..45a8e3c3 100644 --- a/BeefLibs/corlib/src/Compiler.bf +++ b/BeefLibs/corlib/src/Compiler.bf @@ -18,6 +18,12 @@ namespace System } } + public static class Options + { + [LinkName("#AllocStackCount")] + public static extern int32 AllocStackCount; + } + [LinkName("#CallerLineNum")] public static extern int CallerLineNum; @@ -37,7 +43,7 @@ namespace System public static extern String CallerProject; [LinkName("#CallerExpression")] - public static extern String[Int32.MaxValue] CallerExpression; + public static extern String[0x0FFFFFFF] CallerExpression; [LinkName("#ProjectName")] public static extern String ProjectName; diff --git a/BeefLibs/corlib/src/Console.bf b/BeefLibs/corlib/src/Console.bf index b48277e8..74613274 100644 --- a/BeefLibs/corlib/src/Console.bf +++ b/BeefLibs/corlib/src/Console.bf @@ -7,6 +7,12 @@ namespace System { public static class Console { + public enum CancelKind + { + CtrlC, + CtrlBreak + } + static Encoding InputEncoding = Encoding.ASCII; static Encoding OutputEncoding = Encoding.ASCII; @@ -16,6 +22,9 @@ namespace System static readonly ConsoleColor sOriginalForegroundColor = sForegroundColor; static readonly ConsoleColor sOriginalBackgroundColor = sBackgroundColor; + static Event sOnCancel ~ _.Dispose(); + static bool sCancelEventRegistered; + public static ConsoleColor ForegroundColor { get { return sForegroundColor; } @@ -42,16 +51,57 @@ namespace System public uint16[2] mMaximumWindowSize; } - [CLink, CallingConvention(.Stdcall)] - static extern int SetConsoleTextAttribute(void* hConsoleOutput, uint16 wAttributes); + [CRepr] + struct COORD : this(int16 X, int16 Y) + { + } - [CLink, CallingConvention(.Stdcall)] - static extern int GetConsoleScreenBufferInfo(void* hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo); - - [CLink, CallingConvention(.Stdcall)] - static extern void* GetStdHandle(uint32 nStdHandle); + public static ref Event OnCancel + { + get + { + if (!sCancelEventRegistered) + { + sCancelEventRegistered = true; +#if BF_PLATFORM_WINDOWS + SetConsoleCtrlHandler(=> ConsoleCtrlHandler, true); +#endif + } + return ref sOnCancel; + } + } #if BF_PLATFORM_WINDOWS + [CallingConvention(.Stdcall)] + public static Windows.IntBool ConsoleCtrlHandler(int32 ctrlType) + { + bool terminate = true; + if ((ctrlType == 0) || (ctrlType == 1)) + sOnCancel((.)ctrlType, ref terminate); + return terminate ? false : true; + } + + [CLink, CallingConvention(.Stdcall)] + static extern Windows.IntBool SetConsoleTextAttribute(Windows.Handle hConsoleOutput, uint16 wAttributes); + + [CLink, CallingConvention(.Stdcall)] + static extern Windows.IntBool GetConsoleScreenBufferInfo(Windows.Handle hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo); + + [CLink, CallingConvention(.Stdcall)] + static extern Windows.Handle GetStdHandle(uint32 nStdHandle); + + [CallingConvention(.Stdcall)] + function Windows.IntBool ConsoleCtrlHandler(int32 ctrlType); + [CLink, CallingConvention(.Stdcall)] + static extern Windows.IntBool SetConsoleCtrlHandler(ConsoleCtrlHandler handler, Windows.IntBool addHandler); + + [CLink, CallingConvention(.Stdcall)] + static extern Windows.IntBool FillConsoleOutputCharacterW(Windows.Handle hConsoleOutput, char16 cCharacter, uint32 nLength, COORD dwWriteCoord, uint32* lpNumberOfCharsWritten); + [CLink, CallingConvention(.Stdcall)] + static extern Windows.IntBool FillConsoleOutputAttribute(Windows.Handle hConsoleOutput, uint16 wAttribute, uint32 nLength, COORD dwWriteCoord, uint32* lpNumberOfAttrsWritten); + [CLink, CallingConvention(.Stdcall)] + static extern Windows.IntBool SetConsoleCursorPosition(Windows.Handle hConsoleOutput, COORD dwCursorPosition); + public static this() { let handle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -219,6 +269,49 @@ namespace System #else Write("\x1B[{}m", ForegroundColor.AnsiCode); Write("\x1B[{}m", BackgroundColor.AnsiCode + 10); +#endif + } + + public static void Clear() + { +#if BF_PLATFORM_WINDOWS + Windows.Handle hStdOut; + CONSOLE_SCREEN_BUFFER_INFO csbi; + uint32 count; + uint32 cellCount; + COORD homeCoords = .(0, 0); + + hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); + if (hStdOut == .InvalidHandle) + return; + + /* Get the number of cells in the current buffer */ + if (!GetConsoleScreenBufferInfo( hStdOut, out csbi )) + return; + cellCount = csbi.mSize[0] * csbi.mSize[1]; + + /* Fill the entire buffer with spaces */ + if (!FillConsoleOutputCharacterW( + hStdOut, + ' ', + cellCount, + homeCoords, + &count + )) return; + + /* Fill the entire buffer with the current colors and attributes */ + if (!FillConsoleOutputAttribute( + hStdOut, + csbi.mAttributes, + cellCount, + homeCoords, + &count + )) return; + + /* Move the cursor home */ + SetConsoleCursorPosition( hStdOut, homeCoords ); +#else + Write("\x1B[H\x1B[J"); #endif } } diff --git a/BeefLibs/corlib/src/Diagnostics/AsyncStreamReader.bf b/BeefLibs/corlib/src/Diagnostics/AsyncStreamReader.bf index 9126a1fb..940440a7 100644 --- a/BeefLibs/corlib/src/Diagnostics/AsyncStreamReader.bf +++ b/BeefLibs/corlib/src/Diagnostics/AsyncStreamReader.bf @@ -19,7 +19,7 @@ namespace System.Diagnostics private char8[] char8Buffer; // Record the number of valid bytes in the byteBuffer, for a few checks. - // This is the maximum number of char8s we can get from one call to + // This is the maximum number of chars we can get from one call to // ReadBuffer. Used so ReadBuffer can tell when to copy data into // a user's char8[] directly, instead of our internal char8[]. private int32 mMaxCharsPerBuffer; @@ -194,7 +194,7 @@ namespace System.Diagnostics int lineStart = 0; int len = sb.Length; - // skip a beginning '\n' char8acter of new block if last block ended + // skip a beginning '\n' character of new block if last block ended // with '\r' if (bLastCarriageReturn && (len > 0) && sb[0] == '\n') { @@ -206,7 +206,7 @@ namespace System.Diagnostics while (currentIndex < len) { char8 ch = sb[currentIndex]; - // Note the following common line feed char8s: + // Note the following common line feed chars: // \n - UNIX \r\n - DOS \r - Mac if (ch == '\r' || ch == '\n') { @@ -214,7 +214,7 @@ namespace System.Diagnostics s.Append(sb, lineStart, currentIndex - lineStart); lineStart = currentIndex + 1; - // skip the "\n" char8acter following "\r" char8acter + // skip the "\n" character following "\r" character if ((ch == '\r') && (lineStart < len) && (sb[lineStart] == '\n')) { lineStart++; @@ -232,7 +232,7 @@ namespace System.Diagnostics { bLastCarriageReturn = true; } - // Keep the rest char8acaters which can't form a new line in string builder. + // Keep the rest characaters which can't form a new line in string builder. if (lineStart < len) { if (lineStart == 0) diff --git a/BeefLibs/corlib/src/Diagnostics/Check.bf b/BeefLibs/corlib/src/Diagnostics/Check.bf index 0b26b71c..b041f6fb 100644 --- a/BeefLibs/corlib/src/Diagnostics/Check.bf +++ b/BeefLibs/corlib/src/Diagnostics/Check.bf @@ -1,6 +1,6 @@ namespace System.Diagnostics { - class Check + static class Check { [Unchecked, SkipCall] diff --git a/BeefLibs/corlib/src/Diagnostics/Contracts/Contracts.bf b/BeefLibs/corlib/src/Diagnostics/Contracts/Contracts.bf index e14d5a0c..e5f7d80c 100644 --- a/BeefLibs/corlib/src/Diagnostics/Contracts/Contracts.bf +++ b/BeefLibs/corlib/src/Diagnostics/Contracts/Contracts.bf @@ -1,6 +1,6 @@ namespace System.Diagnostics.Contracts { - class Contract + static class Contract { public enum ContractFailureKind { diff --git a/BeefLibs/corlib/src/Diagnostics/Debug.bf b/BeefLibs/corlib/src/Diagnostics/Debug.bf index d4df7bd4..e7a2a554 100644 --- a/BeefLibs/corlib/src/Diagnostics/Debug.bf +++ b/BeefLibs/corlib/src/Diagnostics/Debug.bf @@ -1,6 +1,6 @@ namespace System.Diagnostics { - class Debug + static class Debug { #if !DEBUG [SkipCall] @@ -44,6 +44,11 @@ namespace System.Diagnostics Write(line.Ptr, line.Length); } + public static void Write(StringView sv) + { + Write(sv.[Friend]mPtr, sv.[Friend]mLength); + } + public static void Write(String fmt, params Object[] args) { String str = scope String(4096); diff --git a/BeefLibs/corlib/src/Event.bf b/BeefLibs/corlib/src/Event.bf index dd0bb466..9e760c0d 100644 --- a/BeefLibs/corlib/src/Event.bf +++ b/BeefLibs/corlib/src/Event.bf @@ -136,7 +136,7 @@ namespace System } } - public void Remove(T compareDelegate, bool deleteDelegate = false) mut + public Result Remove(T compareDelegate, bool deleteDelegate = false) mut { Object data = Target; @@ -150,9 +150,7 @@ namespace System break; } if (idx == -1) - { - Runtime.FatalError("Not found"); - } + return .Err; if (deleteDelegate) delete list[idx]; @@ -177,18 +175,14 @@ namespace System else { T dlgMember = (T)data; - if (Delegate.Equals(compareDelegate, dlgMember)) - { - if (deleteDelegate) - delete dlgMember; - Target = null; - return; - } - else - { - Runtime.FatalError("Not found"); - } + if (!Delegate.Equals(compareDelegate, dlgMember)) + return .Err; + if (deleteDelegate) + delete dlgMember; + Target = null; } + + return .Ok; } public rettype(T) Invoke(params T p) mut diff --git a/BeefLibs/corlib/src/Float.bf b/BeefLibs/corlib/src/Float.bf index a3c7dc23..b8e1dd9a 100644 --- a/BeefLibs/corlib/src/Float.bf +++ b/BeefLibs/corlib/src/Float.bf @@ -175,6 +175,17 @@ namespace System { char8 c = val.Ptr[i]; + //Exponent prefix used in scientific notation. E.g. 1.2E5 + if ((c == 'e') || (c == 'E')) + { + //Error if there are no numbers after the prefix + if(i == val.Length - 1) + return .Err; + var exponent = Try!(int32.Parse(val.Substring(i + 1))); + result *= Math.Pow(10, (double)exponent); + break; + } + if (c == '.') { if (decimalMultiplier != 0) diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index dc32f4e2..231642a3 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -173,6 +173,10 @@ namespace System // or the memory would already be registered with the GC } + public static mixin Mark(T val) + { + } + public static mixin Mark(TSizedArray val) where Size : const int where TSizedArray : SizedArray { #if BF_ENABLE_REALTIME_LEAK_CHECK diff --git a/BeefLibs/corlib/src/Globalization/DateTimeFormatInfo.bf b/BeefLibs/corlib/src/Globalization/DateTimeFormatInfo.bf index 72944dfa..abed24a7 100644 --- a/BeefLibs/corlib/src/Globalization/DateTimeFormatInfo.bf +++ b/BeefLibs/corlib/src/Globalization/DateTimeFormatInfo.bf @@ -29,7 +29,7 @@ namespace System.Globalization // cache for the invariant culture. // invariantInfo is constant irrespective of your current culture. - private static volatile DateTimeFormatInfo invariantInfo; + private static volatile DateTimeFormatInfo invariantInfo ~ delete _; // an index which points to a record in Culture Data Table. private CultureData m_cultureData; @@ -139,6 +139,8 @@ namespace System.Globalization List ownedObjects = new .() ~ DeleteContainerAndItems!(_); public this() + : this(CultureInfo.InvariantCulture.[Friend]m_cultureData, + GregorianCalendar.[Friend]GetDefaultInstance()) { } diff --git a/BeefLibs/corlib/src/Globalization/GregorianCalendar.bf b/BeefLibs/corlib/src/Globalization/GregorianCalendar.bf index 9b07d146..b6d530b5 100644 --- a/BeefLibs/corlib/src/Globalization/GregorianCalendar.bf +++ b/BeefLibs/corlib/src/Globalization/GregorianCalendar.bf @@ -63,7 +63,7 @@ namespace System.Globalization { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 ) ~ delete _; - private static volatile Calendar s_defaultInstance; + private static volatile Calendar s_defaultInstance ~ delete _; #region Serialization diff --git a/BeefLibs/corlib/src/IComparable.bf b/BeefLibs/corlib/src/IComparable.bf index 70d38ec1..37aa3c39 100644 --- a/BeefLibs/corlib/src/IComparable.bf +++ b/BeefLibs/corlib/src/IComparable.bf @@ -23,6 +23,10 @@ namespace System { } + interface ICharacter + { + } + [Obsolete("Consider operator constraint such as `where bool : operator T == T`", false)] interface IOpEquals { diff --git a/BeefLibs/corlib/src/IO/BufferedStream.bf b/BeefLibs/corlib/src/IO/BufferedStream.bf index 8fc12e57..91bf3a37 100644 --- a/BeefLibs/corlib/src/IO/BufferedStream.bf +++ b/BeefLibs/corlib/src/IO/BufferedStream.bf @@ -68,7 +68,7 @@ namespace System.IO public override Result TryRead(Span data) { - int spaceLeft = (.)(mBufferEnd - mPos); + int64 spaceLeft = (.)(mBufferEnd - mPos); if (mPos < mBufferPos) spaceLeft = 0; if (data.Length <= spaceLeft) @@ -83,19 +83,17 @@ namespace System.IO var data; if (spaceLeft > 0) { - Internal.MemCpy(data.Ptr, mBuffer.Ptr + (mPos - mBufferPos), spaceLeft); + Internal.MemCpy(data.Ptr, mBuffer.Ptr + (mPos - mBufferPos), (.)spaceLeft); mPos += spaceLeft; - data.RemoveFromStart(spaceLeft); + data.RemoveFromStart((.)spaceLeft); } - if (mWriteDirtyPos >= 0) - Try!(Flush()); + Try!(Flush()); if ((mBuffer == null) || (data.Length > mBuffer.Count)) { - var result = TryReadUnderlying(mPos, data); - if (result case .Ok(let len)) - mPos += len; + let len = Try!(TryReadUnderlying(mPos, data)); + mPos += len; return (.)(mPos - readStart); } @@ -148,10 +146,9 @@ namespace System.IO if ((mBuffer == null) || (data.Length > mBuffer.Count)) { - var result = TryWriteUnderlying(mPos, data); - if (result case .Ok(let len)) - mPos += len; - writeCount += result; + let len = Try!(TryWriteUnderlying(mPos, data)); + mPos += len; + writeCount += len; return writeCount; } diff --git a/BeefLibs/corlib/src/IO/Directory.bf b/BeefLibs/corlib/src/IO/Directory.bf index 0966b08d..e9274784 100644 --- a/BeefLibs/corlib/src/IO/Directory.bf +++ b/BeefLibs/corlib/src/IO/Directory.bf @@ -240,7 +240,7 @@ namespace System.IO } } - struct FileEnumerator : IEnumerator + struct FileEnumerator : IEnumerator, IDisposable { String mSearchStr; Platform.BfpFindFileData* mFindFileData; diff --git a/BeefLibs/corlib/src/IO/DynMemStream.bf b/BeefLibs/corlib/src/IO/DynMemStream.bf index 1fd2e235..64db81ce 100644 --- a/BeefLibs/corlib/src/IO/DynMemStream.bf +++ b/BeefLibs/corlib/src/IO/DynMemStream.bf @@ -15,6 +15,11 @@ namespace System.IO mOwnsData = true; } + public this(List data) + { + mData = data; + } + public uint8* Ptr { get @@ -112,4 +117,100 @@ namespace System.IO mData.RemoveRange(0, count); } } + + class DynMemStreamSequential : Stream + { + List mData ~ { if (mOwnsData) delete _; }; + bool mOwnsData; + + public this() + { + mData = new .(); + mOwnsData = true; + } + + public this(List data) + { + mData = data; + } + + public uint8* Ptr + { + get + { + return mData.Ptr; + } + } + + public Span Content + { + get + { + return mData; + } + } + + public override int64 Position + { + get + { + return mData.Count; + } + + set + { + Runtime.FatalError(); + } + } + + public override int64 Length + { + get + { + return mData.Count; + } + } + + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanWrite + { + get + { + return true; + } + } + + public List TakeOwnership() + { + Debug.Assert(mOwnsData); + mOwnsData = false; + return mData; + } + + public override Result TryRead(Span data) + { + return .Err; + } + + public override Result TryWrite(Span data) + { + let count = data.Length; + if (count == 0) + return .Ok(0); + Internal.MemCpy(mData.GrowUnitialized(count), data.Ptr, count); + return .Ok(count); + } + + public override Result Close() + { + return .Ok; + } + } } diff --git a/BeefLibs/corlib/src/IO/File.bf b/BeefLibs/corlib/src/IO/File.bf index a3ae6cf2..87315500 100644 --- a/BeefLibs/corlib/src/IO/File.bf +++ b/BeefLibs/corlib/src/IO/File.bf @@ -23,7 +23,7 @@ namespace System.IO case FileReadError(FileReadError); } - class File + static class File { public static Result ReadAll(StringView path, List outData) { @@ -49,7 +49,7 @@ namespace System.IO public static Result WriteAll(StringView path, Span data, bool doAppend = false) { - FileStream fs = scope FileStream(); + UnbufferedFileStream fs = scope UnbufferedFileStream(); var result = fs.Open(path, doAppend ? .Append : .Create, .Write); if (result case .Err) return .Err; @@ -76,7 +76,7 @@ namespace System.IO public static Result WriteAllText(StringView path, StringView text, bool doAppend = false) { - FileStream fs = scope FileStream(); + UnbufferedFileStream fs = scope UnbufferedFileStream(); var result = fs.Open(path, doAppend ? .Append : .Create, .Write); if (result case .Err) return .Err; @@ -87,7 +87,7 @@ namespace System.IO public static Result WriteAllText(StringView path, StringView text, Encoding encoding) { - FileStream fs = scope FileStream(); + UnbufferedFileStream fs = scope UnbufferedFileStream(); int len = encoding.GetEncodedSize(text); uint8* data = new uint8[len]*; diff --git a/BeefLibs/corlib/src/IO/FileMode.bf b/BeefLibs/corlib/src/IO/FileMode.bf index 10719cad..4c1eee80 100644 --- a/BeefLibs/corlib/src/IO/FileMode.bf +++ b/BeefLibs/corlib/src/IO/FileMode.bf @@ -17,16 +17,16 @@ namespace System.IO /// Opens an existing file. Fails if the file does not exist. Open = 3, - // Opens the file if it exists. Otherwise, creates a new file. + /// Opens the file if it exists. Otherwise, creates a new file. OpenOrCreate = 4, - // Opens an existing file. Once opened, the file is truncated so that its - // size is zero bytes. The calling process must open the file with at least - // WRITE access. Fails if the file does not exist. + /// Opens an existing file. Once opened, the file is truncated so that its + /// size is zero bytes. The calling process must open the file with at least + /// WRITE access. Fails if the file does not exist. Truncate = 5, - // Opens the file if it exists and seeks to the end. Otherwise, - // creates a new file. + /// Opens the file if it exists and seeks to the end. Otherwise, + /// creates a new file. Append = 6, } } diff --git a/BeefLibs/corlib/src/IO/FileStream.bf b/BeefLibs/corlib/src/IO/FileStream.bf index c193b610..25cb1607 100644 --- a/BeefLibs/corlib/src/IO/FileStream.bf +++ b/BeefLibs/corlib/src/IO/FileStream.bf @@ -29,6 +29,16 @@ namespace System.IO } } + public int Handle + { + get + { + if (mBfpFile == null) + return 0; + return Platform.BfpFile_GetSystemHandle(mBfpFile); + } + } + public ~this() { Delete(); @@ -165,12 +175,13 @@ namespace System.IO createKind = .CreateIfNotExists; case .Create: createKind = .CreateAlways; + createFlags |= .Truncate; case .Open: createKind = .OpenExisting; case .OpenOrCreate: - createKind = .CreateAlways; + createKind = .OpenAlways; case .Truncate: - createKind = .CreateAlways; + createKind = .OpenExisting; createFlags |= .Truncate; case .Append: createKind = .CreateAlways; @@ -225,6 +236,32 @@ namespace System.IO return .Err; return .Ok; } + + public override Result SetLength(int64 length) + { + int64 pos = Position; + + if (pos != length) + Seek(length); + + Platform.BfpFileResult result = .Ok; + Platform.BfpFile_Truncate(mBfpFile, &result); + if (result != .Ok) + { + Seek(pos); + return .Err; + } + + if (pos != length) + { + if (pos < length) + Seek(pos); + else + Seek(0, .FromEnd); + } + + return .Ok; + } } class BufferedFileStream : BufferedStream @@ -233,6 +270,32 @@ namespace System.IO protected int64 mBfpFilePos; FileAccess mFileAccess; + public int Handle + { + get + { + if (mBfpFile == null) + return 0; + return Platform.BfpFile_GetSystemHandle(mBfpFile); + } + } + + public override bool CanRead + { + get + { + return mFileAccess.HasFlag(FileAccess.Read); + } + } + + public override bool CanWrite + { + get + { + return mFileAccess.HasFlag(FileAccess.Write); + } + } + public this() { @@ -254,22 +317,6 @@ namespace System.IO mFileAccess = access; } - public override bool CanRead - { - get - { - return mFileAccess.HasFlag(FileAccess.Read); - } - } - - public override bool CanWrite - { - get - { - return mFileAccess.HasFlag(FileAccess.Write); - } - } - public Result Create(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null) { return Open(path, FileMode.Create, access, share, bufferSize, options, secAttrs); @@ -317,7 +364,7 @@ namespace System.IO case .Open: createKind = .OpenExisting; case .OpenOrCreate: - createKind = .CreateAlways; + createKind = .OpenAlways; case .Truncate: createKind = .CreateAlways; createFlags |= .Truncate; @@ -386,15 +433,20 @@ namespace System.IO mUnderlyingLength = Platform.BfpFile_GetFileSize(mBfpFile); } + protected Result SeekUnderlying(int64 offset, Platform.BfpFileSeekKind seekKind = .Absolute) + { + int64 newPos = Platform.BfpFile_Seek(mBfpFile, offset, seekKind); + Result result = ((seekKind == .Absolute) && (newPos != offset)) ? .Err : .Ok; + if (result case .Ok) + mBfpFilePos = newPos; + return result; + } + protected override Result TryReadUnderlying(int64 pos, Span data) { if (mBfpFilePos != pos) - { - int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, .Absolute); - if (newPos != pos) - return .Err; - mBfpFilePos = pos; - } + Try!(SeekUnderlying(pos)); + Platform.BfpFileResult result = .Ok; int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result); if ((result != .Ok) && (result != .PartialData)) @@ -406,12 +458,8 @@ namespace System.IO protected override Result TryWriteUnderlying(int64 pos, Span data) { if (mBfpFilePos != pos) - { - int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, .Absolute); - if (newPos != pos) - return .Err; - mBfpFilePos = pos; - } + Try!(SeekUnderlying(pos)); + Platform.BfpFileResult result = .Ok; int numBytesRead = Platform.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result); if ((result != .Ok) && (result != .PartialData)) @@ -423,12 +471,7 @@ namespace System.IO public Result TryRead(Span data, int timeoutMS) { if (mBfpFilePos != mPos) - { - int64 newPos = Platform.BfpFile_Seek(mBfpFile, mPos, .Absolute); - if (newPos != mPos) - return .Err; - mBfpFilePos = mPos; - } + Try!(SeekUnderlying(mPos)); Platform.BfpFileResult result = .Ok; int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result); @@ -436,6 +479,40 @@ namespace System.IO return .Err; return numBytesRead; } + + public override Result SetLength(int64 length) + { + Try!(Flush()); + + int64 pos = Position; + + if (pos != length || pos != mBfpFilePos) + { + Try!(SeekUnderlying(length)); + mPos = length; + } + + Platform.BfpFileResult result = .Ok; + Platform.BfpFile_Truncate(mBfpFile, &result); + if (result != .Ok) + { + Try!(SeekUnderlying(pos)); + return .Err; + } + + mUnderlyingLength = length; + mPos = Math.Min(pos, Length); + + if (pos != length) + { + if (pos < length) + Try!(SeekUnderlying(pos)); + else + Try!(SeekUnderlying(0, .FromEnd)); + } + + return .Ok; + } } class FileStream : BufferedFileStream diff --git a/BeefLibs/corlib/src/IO/MemoryStream.bf b/BeefLibs/corlib/src/IO/MemoryStream.bf index 0980bb1c..53dbe501 100644 --- a/BeefLibs/corlib/src/IO/MemoryStream.bf +++ b/BeefLibs/corlib/src/IO/MemoryStream.bf @@ -4,7 +4,7 @@ namespace System.IO { class MemoryStream : Stream { - List mMemory = new List() ~ delete _; + List mMemory ~ delete _; int mPosition = 0; public override int64 Position @@ -44,6 +44,16 @@ namespace System.IO } } + public this() + { + mMemory = new List(); + } + + public this(List memory) + { + mMemory = memory; + } + public override Result TryRead(Span data) { let count = data.Length; diff --git a/BeefLibs/corlib/src/IO/Path.bf b/BeefLibs/corlib/src/IO/Path.bf index 0b9b7b4e..270f345c 100644 --- a/BeefLibs/corlib/src/IO/Path.bf +++ b/BeefLibs/corlib/src/IO/Path.bf @@ -15,7 +15,7 @@ namespace System.IO public const char8 DirectorySeparatorChar = '/'; #endif //BF_PLATFORM_WINDOWS - // Platform specific alternate directory separator char8acter. + // Platform specific alternate directory separator character. // This is backslash ('\') on Unix, and slash ('/') on Windows // and MacOS. // @@ -25,7 +25,7 @@ namespace System.IO public const char8 AltDirectorySeparatorChar = '\\'; #endif //BF_PLATFORM_WINDOWS - // Platform specific volume separator char8acter. This is colon (':') + // Platform specific volume separator character. This is colon (':') // on Windows and MacOS, and slash ('/') on Unix. This is mostly // useful for parsing paths like "c:\windows" or "MacVolume:System Folder". // @@ -37,7 +37,7 @@ namespace System.IO // Make this public sometime. // The max total path is 260, and the max individual component length is 255. - // For example, D:\<256 char8 file name> isn't legal, even though it's under 260 char8s. + // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars. protected const int32 MaxPath = 260; private const int32 MaxDirectoryLength = 255; @@ -297,7 +297,7 @@ namespace System.IO return .Ok; } - public static void InternalCombine(String target, params String[] components) + public static void InternalCombine(String target, params StringView[] components) { for (var component in components) { diff --git a/BeefLibs/corlib/src/IO/Stream.bf b/BeefLibs/corlib/src/IO/Stream.bf index 6798c4aa..a7bbd32a 100644 --- a/BeefLibs/corlib/src/IO/Stream.bf +++ b/BeefLibs/corlib/src/IO/Stream.bf @@ -97,25 +97,32 @@ namespace System.IO } } - //Read sized string from stream - public Result ReadStrSized32(int64 size, String output) + /// Read sized string from stream + public Result ReadStrSized32(int size, String output) { - if (size <= 0) + if (size < 0) return .Err; - for (int64 i = 0; i < size; i++) + int prevLen = output.Length; + char8* buf = output.PrepareBuffer(size); + switch (TryRead(.((uint8*)buf, size))) { - Result char = Read(); - if (char == .Err) - return .Err; - - output.Append(char); + case .Ok(let readLen): + if (readLen < size) + output.Length = prevLen + readLen; + return .Ok; + case .Err: + return .Err; } - - return .Ok; } - //Reads null terminated ASCII string from the stream. Null terminator is read from stream but isn't appended to output string + public Result ReadStrSized32(String output) + { + int size = Try!(Read()); + return ReadStrSized32(size, output); + } + + /// Reads null terminated ASCII string from the stream. Null terminator is read from stream but isn't appended to output string public Result ReadStrC(String output) { Result char0; @@ -197,6 +204,11 @@ namespace System.IO return .Ok; } + public virtual Result SetLength(int64 length) + { + return .Err; + } + public void Align(int alignSize) { int64 pos = Length; diff --git a/BeefLibs/corlib/src/IO/StreamReader.bf b/BeefLibs/corlib/src/IO/StreamReader.bf index 963d9f37..2c1257be 100644 --- a/BeefLibs/corlib/src/IO/StreamReader.bf +++ b/BeefLibs/corlib/src/IO/StreamReader.bf @@ -256,7 +256,7 @@ namespace System.IO { char8 ch = tmpCharBuffer[i]; - // Note the following common line feed char8s: + // Note the following common line feed chars: // \n - UNIX \r\n - DOS \r - Mac if (ch == '\r' || ch == '\n') { @@ -481,7 +481,7 @@ namespace System.IO } while (mCharLen == 0); - //Console.WriteLine("ReadBuffer called. char8s: "+char8Len); + //Console.WriteLine("ReadBuffer called. chars: "+char8Len); return mCharLen; } @@ -522,7 +522,7 @@ namespace System.IO repeat { char8 ch = mCharBuffer[i]; - // Note the following common line feed char8s: + // Note the following common line feed chars: // \n - UNIX \r\n - DOS \r - Mac if (ch == '\r' || ch == '\n') { diff --git a/BeefLibs/corlib/src/Internal.bf b/BeefLibs/corlib/src/Internal.bf index 0ab17ef4..882e73fa 100644 --- a/BeefLibs/corlib/src/Internal.bf +++ b/BeefLibs/corlib/src/Internal.bf @@ -243,6 +243,7 @@ namespace System } } + [AlwaysInclude] public static String[] CreateParamsArray() { char8* cmdLine = GetCommandLineArgs(); @@ -347,6 +348,7 @@ namespace System return strVals; } + [AlwaysInclude] public static void DeleteStringArray(String[] arr) { for (var str in arr) diff --git a/BeefLibs/corlib/src/Math.bf b/BeefLibs/corlib/src/Math.bf index 415c7564..ccd1aab0 100644 --- a/BeefLibs/corlib/src/Math.bf +++ b/BeefLibs/corlib/src/Math.bf @@ -24,7 +24,9 @@ namespace System private static float[7] sRoundPower10Single = .( 1E0f, 1E1f, 1E2f, 1E3f, 1E4f, 1E5f, 1E6f); - + + private static float sMachineEpsilonFloat = GetMachineEpsilonFloat(); + public const double PI_d = 3.14159265358979323846; public const double E_d = 2.7182818284590452354; public const float PI_f = 3.14159265358979323846f; @@ -48,6 +50,33 @@ namespace System public static extern float Floor(float f); public static extern double Floor(double d); + public static bool WithinEpsilon(float a, float b) + { + return Math.Abs(a - b) < sMachineEpsilonFloat; + } + + /// + /// Find the current machine's Epsilon for the float data type. + /// (That is, the largest float, e, where e == 0.0f is true.) + /// + private static float GetMachineEpsilonFloat() + { + float machineEpsilon = 1.0f; + float comparison; + + /* Keep halving the working value of machineEpsilon until we get a number that + * when added to 1.0f will still evaluate as equal to 1.0f. + */ + repeat + { + machineEpsilon *= 0.5f; + comparison = 1.0f + machineEpsilon; + } + while (comparison > 1.0f); + + return machineEpsilon; + } + private static float InternalRound(float value, int32 digits, MidpointRounding mode) { if (Abs(value) < cSingleRoundLimit) @@ -476,5 +505,125 @@ namespace System { return ((val) + (align - 1)) & ~(align - 1); } + + /// Interpolates between two values using a cubic equation. + /// @param name Source value. + /// @param name Source value. + /// @param name Weighting value. + /// @returns Interpolated value. + public static float SmoothStep(float value1, float value2, float amount) + { + /* It is expected that 0 < amount < 1. + * If amount < 0, return value1. + * If amount > 1, return value2. + */ + float result = Clamp(amount, 0f, 1f); + result = Hermite(value1, 0f, value2, 0f, result); + + return result; + } + + /// Performs a Hermite spline interpolation. + /// @param value1 Source position. + /// @param tangent1 Source tangent. + /// @param value2 Source position. + /// @param tangent2 Source tangent. + /// @param amount Weighting factor. + /// @returns The result of the Hermite spline interpolation. + public static float Hermite( + float value1, + float tangent1, + float value2, + float tangent2, + float amount + ) { + /* All transformed to double not to lose precision + * Otherwise, for high numbers of param:amount the result is NaN instead + * of Infinity. + */ + double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount; + double result; + double sCubed = s * s * s; + double sSquared = s * s; + + if (WithinEpsilon(amount, 0f)) + { + result = value1; + } + else if (WithinEpsilon(amount, 1f)) + { + result = value2; + } + else + { + result = ( + ((2 * v1 - 2 * v2 + t2 + t1) * sCubed) + + ((3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared) + + (t1 * s) + + v1 + ); + } + + return (float) result; + } + + /// Returns the Cartesian coordinate for one axis of a point that is defined by a + /// given triangle and two normalized barycentric (areal) coordinates. + /// + /// The coordinate on one axis of vertex 1 of the defining triangle. + /// + /// + /// The coordinate on the same axis of vertex 2 of the defining triangle. + /// + /// + /// The coordinate on the same axis of vertex 3 of the defining triangle. + /// + /// + /// The normalized barycentric (areal) coordinate b2, equal to the weighting factor + /// for vertex 2, the coordinate of which is specified in value2. + /// + /// @param amount2 + /// The normalized barycentric (areal) coordinate b3, equal to the weighting factor + /// for vertex 3, the coordinate of which is specified in value3. + /// + /// @returns Cartesian coordinate of the specified point with respect to the axis being used. + public static float Barycentric( + float value1, + float value2, + float value3, + float amount1, + float amount2 + ) { + return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2; + } + + /// Performs a Catmull-Rom interpolation using the specified positions. + /// @param value1 The first position in the interpolation. + /// @param value2">The second position in the interpolation. + /// @param value3">The third position in the interpolation. + /// @param value4">The fourth position in the interpolation. + /// @param name="amount">Weighting factor. + /// @returns A position that is the result of the Catmull-Rom interpolation. + public static float CatmullRom( + float value1, + float value2, + float value3, + float value4, + float amount + ) { + /* Using formula from http://www.mvps.org/directx/articles/catmull/ + * Internally using doubles not to lose precision. + */ + double amountSquared = amount * amount; + double amountCubed = amountSquared * amount; + return (float) ( + 0.5 * + ( + ((2.0 * value2 + (value3 - value1) * amount) + + ((2.0 * value1 - 5.0 * value2 + 4.0 * value3 - value4) * amountSquared) + + (3.0 * value2 - value1 - 3.0 * value3 + value4) * amountCubed) + ) + ); + } } } diff --git a/BeefLibs/corlib/src/Net/Socket.bf b/BeefLibs/corlib/src/Net/Socket.bf index e4e6b648..d62d9cff 100644 --- a/BeefLibs/corlib/src/Net/Socket.bf +++ b/BeefLibs/corlib/src/Net/Socket.bf @@ -5,6 +5,7 @@ namespace System.Net { class Socket { + const uint16 WINSOCK_VERSION = 0x0202; const int32 WSAENETRESET = 10052; const int32 WSAECONNABORTED = 10053; const int32 WSAECONNRESET = 10054; @@ -169,12 +170,22 @@ namespace System.Net #if BF_PLATFORM_WINDOWS [Import("wsock32.lib"), CLink, CallingConvention(.Stdcall)] static extern int32 WSAStartup(uint16 versionRequired, WSAData* wsaData); + + [Import("wsock32.lib"), CLink, CallingConvention(.Stdcall)] + static extern int32 WSACleanup(); [Import("wsock32.lib"), CLink, CallingConvention(.Stdcall)] static extern int32 WSAGetLastError(); +#elif BF_PLATFORM_LINUX + [LinkName("__errno_location")] + static extern int32* _errno(); +#elif BF_PLATFORM_MACOS + [LinkName("__error")] + static extern int32* _errno(); #else [CLink] static int32 errno; + static int32* _errno() => &errno; #endif [CLink, CallingConvention(.Stdcall)] @@ -230,11 +241,22 @@ namespace System.Net #endif } - public static void Init() + public static int32 Init(uint16 versionRequired = WINSOCK_VERSION) { #if BF_PLATFORM_WINDOWS WSAData wsaData = default; - WSAStartup(0x202, &wsaData); + return WSAStartup(versionRequired, &wsaData); +#else + return 0; +#endif + } + + public static int32 Uninit() + { +#if BF_PLATFORM_WINDOWS + return WSACleanup(); +#else + return 0; #endif } @@ -243,7 +265,7 @@ namespace System.Net #if BF_PLATFORM_WINDOWS return WSAGetLastError(); #else - return errno; + return *_errno(); #endif } diff --git a/BeefLibs/corlib/src/NumberFormatter.bf b/BeefLibs/corlib/src/NumberFormatter.bf index d4680f65..4f14c6db 100644 --- a/BeefLibs/corlib/src/NumberFormatter.bf +++ b/BeefLibs/corlib/src/NumberFormatter.bf @@ -2675,7 +2675,7 @@ namespace System return; int i = sb.Length + (end - start); - sb.Length = i; + sb.PrepareBuffer(i); var end; var start; diff --git a/BeefLibs/corlib/src/Platform.bf b/BeefLibs/corlib/src/Platform.bf index f6749061..f110c709 100644 --- a/BeefLibs/corlib/src/Platform.bf +++ b/BeefLibs/corlib/src/Platform.bf @@ -271,6 +271,7 @@ namespace System CreateAlways, CreateIfNotExists, OpenExisting, + OpenAlways, }; public enum BfpFileCreateFlags : int32 @@ -337,6 +338,8 @@ namespace System [CallingConvention(.Stdcall), CLink] public static extern BfpFile* BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult); [CallingConvention(.Stdcall), CLink] + public static extern int BfpFile_GetSystemHandle(BfpFile* file); + [CallingConvention(.Stdcall), CLink] public static extern void BfpFile_Release(BfpFile* file); [CallingConvention(.Stdcall), CLink] public static extern int BfpFile_Write(BfpFile* file, void* buffer, int size, int timeoutMS, BfpFileResult* outResult); @@ -349,7 +352,7 @@ namespace System [CallingConvention(.Stdcall), CLink] public static extern int64 BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind); [CallingConvention(.Stdcall), CLink] - public static extern void BfpFile_Truncate(BfpFile* file); + public static extern void BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult); [CallingConvention(.Stdcall), CLink] public static extern BfpTimeStamp BfpFile_GetTime_LastWrite(char8* path); [CallingConvention(.Stdcall), CLink] @@ -410,7 +413,8 @@ namespace System AppData_LocalLow, AppData_Roaming, Programs, - Programs_Common + Programs_Common, + Documents } public static Result GetStrHelper(String outStr, delegate void (char8* outPtr, int32* outSize, Result* outResult) func) diff --git a/BeefLibs/corlib/src/Range.bf b/BeefLibs/corlib/src/Range.bf new file mode 100644 index 00000000..dae1f2a1 --- /dev/null +++ b/BeefLibs/corlib/src/Range.bf @@ -0,0 +1,468 @@ +using System.Collections; +using System.Diagnostics; + +namespace System +{ + interface RangeExpression + { + + } + + enum Index + { + case FromFront(int offset); + case FromEnd(int offset); + + public override void ToString(String strBuffer) + { + switch (this) + { + case .FromFront(let offset): + offset.ToString(strBuffer); + case .FromEnd(let offset): + strBuffer.Append('^'); + offset.ToString(strBuffer); + } + } + } + + struct Range : RangeExpression, IEnumerable + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + [Inline] + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + + public int Length + { + [Inline] + get + { + return mEnd - mStart; + } + + [Inline] + set mut + { + mEnd = mStart + value; + } + } + + public int Start + { + [Inline] + get + { + return mStart; + } + + [Inline] + set mut + { + mStart = value; + } + } + + public int End + { + [Inline] + get + { + return mEnd; + } + + set mut + { + mEnd = value; + } + } + + public bool IsEmpty + { + [Inline] + get + { + return mEnd == mStart; + } + } + + public ReverseEnumerator Reversed + { + [Inline] + get + { + return ReverseEnumerator(mEnd - 1, mStart); + } + } + + public bool Contains(int idx) + { + return (idx >= mStart) && (idx < mEnd); + } + + public bool Contains(Range val) + { + return (val.[Friend]mStart >= mStart) && (val.[Friend]mEnd <= mEnd); + } + + public bool Contains(ClosedRange val) + { + return (val.[Friend]mStart >= mStart) && (val.[Friend]mEnd <= mEnd - 1); + } + + public void Clear() mut + { + mStart = 0; + mEnd = 0; + } + + public static operator IndexRange(Range list) + { + return .(.FromFront(list.mStart), .FromFront(list.mEnd), false); + } + + [Inline] + public Enumerator GetEnumerator() + { + return Enumerator(this); + } + + public override void ToString(String strBuffer) + { + strBuffer.AppendF($"{mStart}..<{mEnd}"); + } + + public struct Enumerator : IEnumerator + { + private int mEnd; + private int mIndex; + + [Inline] + public this(Range range) + { + mIndex = range.mStart - 1; + mEnd = range.mEnd; + } + + public void Dispose() + { + } + + public ref int Index + { + get mut + { + return ref mIndex; + } + } + + public int End => mEnd; + + [Inline] + public Result GetNext() mut + { + if (mIndex + 1 >= mEnd) + return .Err; + return ++mIndex; + } + } + + public struct ReverseEnumerator : IEnumerator + { + private int mEnd; + private int mIndex; + + [Inline] + public this(int start, int end) + { + mIndex = start + 1; + mEnd = end; + } + + public void Dispose() + { + } + + public ref int Index + { + get mut + { + return ref mIndex; + } + } + + public int End => mEnd; + + [Inline] + public Result GetNext() mut + { + if (mIndex <= mEnd) + return .Err; + return --mIndex; + } + } + } + + struct IndexRange : RangeExpression + { + protected Index mStart; + protected Index mEnd; + protected bool mIsClosed; + + public this() + { + this = default; + } + + [Inline] + public this(Index start, Index end, bool isClosed=true) + { + mStart = start; + mEnd = end; + mIsClosed = isClosed; + } + + [Inline] + public this(int start, Index end, bool isClosed=true) + { + mStart = .FromFront(start); + mEnd = end; + mIsClosed = isClosed; + } + + [Inline] + public this(Index start, int end, bool isClosed=true) + { + mStart = start; + mEnd = .FromFront(end); + mIsClosed = isClosed; + } + + [Inline] + public this(int start, int end, bool isClosed=true) + { + mStart = .FromFront(start); + mEnd = .FromFront(end); + mIsClosed = isClosed; + } + + public Index Start + { + [Inline] + get + { + return mStart; + } + + [Inline] + set mut + { + mStart = value; + } + } + + public Index End + { + [Inline] + get + { + return mEnd; + } + + set mut + { + mEnd = value; + } + } + + public bool IsClosed + { + [Inline] + get + { + return mIsClosed; + } + + set mut + { + mIsClosed = value; + } + } + + public override void ToString(String strBuffer) + { + mStart.ToString(strBuffer); + if (mIsClosed) + strBuffer.Append("..."); + else + strBuffer.Append("..<"); + mEnd.ToString(strBuffer); + } + } + + struct ClosedRange : RangeExpression, IEnumerable + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + [Inline] + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + + public int Length + { + [Inline] + get + { + return mEnd - mStart; + } + + [Inline] + set mut + { + mEnd = mStart + value; + } + } + + public int Start + { + [Inline] + get + { + return mStart; + } + + [Inline] + set mut + { + mStart = value; + } + } + + public int End + { + [Inline] + get + { + return mEnd; + } + + set mut + { + mEnd = value; + } + } + + public bool IsEmpty + { + [Inline] + get + { + return mEnd == mStart; + } + } + + public Range.ReverseEnumerator Reversed + { + [Inline] + get + { + return Range.ReverseEnumerator(mEnd, mStart); + } + } + + public bool Contains(int idx) + { + return (idx >= mStart) && (idx <= mEnd); + } + + public bool Contains(Range val) + { + return (val.[Friend]mStart >= mStart) && (val.[Friend]mEnd - 1 <= mEnd); + } + + public bool Contains(ClosedRange val) + { + return (val.[Friend]mStart >= mStart) && (val.[Friend]mEnd <= mEnd); + } + + public void Clear() mut + { + mStart = 0; + mEnd = 0; + } + + public static operator IndexRange(ClosedRange list) + { + return .(.FromFront(list.mStart), .FromFront(list.mEnd), true); + } + + [Inline] + public Enumerator GetEnumerator() + { + return Enumerator(this); + } + + public override void ToString(String strBuffer) + { + strBuffer.AppendF($"{mStart}...{mEnd}"); + } + + public struct Enumerator : IEnumerator + { + private int mEnd; + private int mIndex; + + [Inline] + public this(ClosedRange range) + { + mIndex = range.mStart - 1; + mEnd = range.mEnd; + } + + public void Dispose() + { + } + + public ref int Index + { + get mut + { + return ref mIndex; + } + } + + public int End => mEnd; + + [Inline] + public Result GetNext() mut + { + if (mIndex >= mEnd) + return .Err; + return ++mIndex; + } + } + } +} diff --git a/BeefLibs/corlib/src/Reflection/AttributeInfo.bf b/BeefLibs/corlib/src/Reflection/AttributeInfo.bf index c30d130a..1e515ae9 100644 --- a/BeefLibs/corlib/src/Reflection/AttributeInfo.bf +++ b/BeefLibs/corlib/src/Reflection/AttributeInfo.bf @@ -92,6 +92,9 @@ namespace System.Reflection .Double: let attrData = Decode!(data); args[argIdx] = scope:AttrBlock box attrData; + case (TypeCode)51: //BfConstType_TypeOf + let argTypeId = Decode!(data); + args[argIdx] = Type.[Friend]GetType((.)argTypeId); case (TypeCode)255: let stringId = Decode!(data); String str = String.[Friend]sIdStringLiterals[stringId]; diff --git a/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf b/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf index cc13dad1..996d4480 100644 --- a/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf +++ b/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf @@ -29,8 +29,8 @@ namespace System.Reflection get { if (Compiler.IsComptime) - Type.[Friend]Comptime_Method_GetName(mNativeMethodInstance); - return ""; + return Type.[Friend]Comptime_Method_GetName(mNativeMethodInstance); + return "?"; } } public int ParamCount diff --git a/BeefLibs/corlib/src/Reflection/FieldInfo.bf b/BeefLibs/corlib/src/Reflection/FieldInfo.bf index fbef25ad..6b4b67cc 100644 --- a/BeefLibs/corlib/src/Reflection/FieldInfo.bf +++ b/BeefLibs/corlib/src/Reflection/FieldInfo.bf @@ -20,53 +20,13 @@ namespace System.Reflection mFieldData = fieldData; } - public int32 MemberOffset - { - get - { - return (int32)mFieldData.mData; - } - } - - public Type FieldType - { - get - { - return Type.[Friend]GetType(mFieldData.mFieldTypeId); - } - } - - public bool IsConst - { - get - { - return mFieldData.mFlags.HasFlag(.Const); - } - } - - public bool IsStatic - { - get - { - return mFieldData.mFlags.HasFlag(.Static); - } - } - - public bool IsInstanceField - { - get - { - return !mFieldData.mFlags.HasFlag(.Static) && !mFieldData.mFlags.HasFlag(.Const); - } - } - - public StringView Name - { - get - { - return mFieldData.mName; - } - } + public TypeInstance DeclaringType => mTypeInstance; + public int32 MemberOffset => (int32)mFieldData.mData; + public Type FieldType => Type.[Friend]GetType(mFieldData.mFieldTypeId); + public bool IsConst => mFieldData.mFlags.HasFlag(.Const); + public bool IsStatic => mFieldData.mFlags.HasFlag(.Static); + public bool IsInstanceField => !mFieldData.mFlags.HasFlag(.Static) && !mFieldData.mFlags.HasFlag(.Const); + public StringView Name => mFieldData.mName; public Result SetValue(Object obj, Object value) { @@ -525,7 +485,15 @@ namespace System.Reflection { mIdx++; if (mIdx == mTypeInstance.[Friend]mFieldDataCount) - return false; + { + if (mBindingFlags.HasFlag(.DeclaredOnly)) + return false; + if (mTypeInstance.[Friend]mBaseType == 0) + return false; + mTypeInstance = Type.[Friend]GetType(mTypeInstance.[Friend]mBaseType) as TypeInstance; + mIdx = -1; + continue; + } var fieldData = &mTypeInstance.[Friend]mFieldDataPtr[mIdx]; bool matches = (mBindingFlags.HasFlag(BindingFlags.Static) && (fieldData.mFlags.HasFlag(FieldFlags.Static))); matches |= (mBindingFlags.HasFlag(BindingFlags.Instance) && (!fieldData.mFlags.HasFlag(FieldFlags.Static))); diff --git a/BeefLibs/corlib/src/Reflection/MethodInfo.bf b/BeefLibs/corlib/src/Reflection/MethodInfo.bf index 14397d13..f5fbafbd 100644 --- a/BeefLibs/corlib/src/Reflection/MethodInfo.bf +++ b/BeefLibs/corlib/src/Reflection/MethodInfo.bf @@ -18,6 +18,7 @@ namespace System.Reflection mMethodData = methodData; } + public TypeInstance DeclaringType => mTypeInstance; public bool IsInitialized => mMethodData != null; public StringView Name => mMethodData.[Friend]mName; public int ParamCount => mMethodData.[Friend]mParamCount; @@ -37,11 +38,22 @@ namespace System.Reflection return mMethodData.mParamData[paramIdx].mName; } + public Result GetParamCustomAttribute(int paramIdx) where T : Attribute + { + Debug.Assert((uint)paramIdx < (uint)mMethodData.mParamCount); + return mTypeInstance.[Friend]GetCustomAttribute(mMethodData.mParamData[paramIdx].mCustomAttributesIdx); + } + public Result GetCustomAttribute() where T : Attribute { return mTypeInstance.[Friend]GetCustomAttribute(mMethodData.mCustomAttributesIdx); } + public Result GetReturnCustomAttribute() where T : Attribute + { + return mTypeInstance.[Friend]GetCustomAttribute(mMethodData.mReturnCustomAttributesIdx); + } + public enum CallError { case None; @@ -777,7 +789,15 @@ namespace System.Reflection { mIdx++; if (mIdx == mTypeInstance.[Friend]mMethodDataCount) - return false; + { + if (mBindingFlags.HasFlag(.DeclaredOnly)) + return false; + if (mTypeInstance.[Friend]mBaseType == 0) + return false; + mTypeInstance = Type.[Friend]GetType(mTypeInstance.[Friend]mBaseType) as TypeInstance; + mIdx = -1; + continue; + } var methodData = &mTypeInstance.[Friend]mMethodDataPtr[mIdx]; bool matches = (mBindingFlags.HasFlag(BindingFlags.Static) && (methodData.mFlags.HasFlag(.Static))); matches |= (mBindingFlags.HasFlag(BindingFlags.Instance) && (!methodData.mFlags.HasFlag(.Static))); diff --git a/BeefLibs/corlib/src/Reflection/TypeInstance.bf b/BeefLibs/corlib/src/Reflection/TypeInstance.bf index eefc5f0a..5eb42155 100644 --- a/BeefLibs/corlib/src/Reflection/TypeInstance.bf +++ b/BeefLibs/corlib/src/Reflection/TypeInstance.bf @@ -32,7 +32,7 @@ namespace System if (matched.[Friend]mMethodData != null) return .Err(.MultipleResults); else - matched = methodInfo; + matched = methodInfo; } } @@ -41,6 +41,26 @@ namespace System return .Ok(matched); } + [Comptime] + public virtual Result GetMethod(StringView methodName, BindingFlags bindingFlags = cDefaultLookup) + { + ComptimeMethodInfo matched = default; + for (let methodInfo in ComptimeMethodInfo.Enumerator(this as TypeInstance, bindingFlags)) + { + if (methodInfo.Name == methodName) + { + if (matched.mNativeMethodInstance != 0) + return .Err(.MultipleResults); + else + matched = methodInfo; + } + } + + if (matched.mNativeMethodInstance == 0) + return .Err(.NoResults); + return .Ok(matched); + } + public virtual Result GetMethod(int methodIdx) { return .Err(.NoResults); @@ -123,7 +143,10 @@ namespace System.Reflection let objType = typeof(Object) as TypeInstance; #if BF_ENABLE_OBJECT_DEBUG_FLAGS - obj = Internal.Dbg_ObjectAlloc(mTypeClassVData, mInstSize, mInstAlign, 1); + int32 stackCount = Compiler.Options.AllocStackCount; + if (mAllocStackCountOverride != 0) + stackCount = mAllocStackCountOverride; + obj = Internal.Dbg_ObjectAlloc(mTypeClassVData, mInstSize, mInstAlign, stackCount); #else void* mem = new [Align(16)] uint8[mInstSize]* (?); obj = Internal.UnsafeCastToObject(mem); diff --git a/BeefLibs/corlib/src/Result.bf b/BeefLibs/corlib/src/Result.bf index dd7d9140..a4aa719c 100644 --- a/BeefLibs/corlib/src/Result.bf +++ b/BeefLibs/corlib/src/Result.bf @@ -20,22 +20,26 @@ namespace System public T Value { + [Inline] get { return Unwrap(); } } + [Inline] public static implicit operator Result(T value) { return .Ok(value); } + [Inline] public static implicit operator T(Result result) { return result.Unwrap(); } + [Inline] public void IgnoreError() { } diff --git a/BeefLibs/corlib/src/Runtime.bf b/BeefLibs/corlib/src/Runtime.bf index 96ae54fc..eb25c60a 100644 --- a/BeefLibs/corlib/src/Runtime.bf +++ b/BeefLibs/corlib/src/Runtime.bf @@ -5,8 +5,8 @@ using System.Threading; namespace System { - [StaticInitPriority(100)] - class Runtime + [StaticInitPriority(101)] + static class Runtime { const int32 cVersion = 8; @@ -102,7 +102,7 @@ namespace System function void* (int size) mAlloc; function void (void* ptr) mFree; function void (Object obj) mObject_Delete; - function void (Object obj, String str) mObject_ToString; + void* mUnused0; function Type (Object obj) mObject_GetType; function void (Object obj) mObject_GCMarkMembers; function Object (Object obj, int32 typeId) mObject_DynamicCastToTypeId; @@ -141,13 +141,6 @@ namespace System delete obj; } - static void Object_ToString(Object obj, String str) - { -#if BF_DBG_RUNTIME - obj.ToString(str); -#endif - } - static Type Object_GetType(Object obj) { #if BF_DBG_RUNTIME @@ -241,7 +234,6 @@ namespace System mAlloc = => Alloc; mFree = => Free; mObject_Delete = => Object_Delete; - mObject_ToString = => Object_ToString; mObject_GetType = => Object_GetType; mObject_GCMarkMembers = => Object_GCMarkMembers; mObject_DynamicCastToTypeId = => Object_DynamicCastToTypeId; diff --git a/BeefLibs/corlib/src/Security/Cryptography/MD5.bf b/BeefLibs/corlib/src/Security/Cryptography/MD5.bf index efd4a11b..c79a0fd6 100644 --- a/BeefLibs/corlib/src/Security/Cryptography/MD5.bf +++ b/BeefLibs/corlib/src/Security/Cryptography/MD5.bf @@ -1,5 +1,35 @@ namespace System.Security.Cryptography { + struct HashEncode + { + // Only 63 chars - skip zero + const char8[?] cHash64bToChar = .( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' ); + public static void HashEncode64(uint64 val, String outStr) + { + var val; + if ((int64)val < 0) + { + uint64 flippedNum = (uint64)-(int64)val; + // Only flip if the encoded result would actually be shorter + if (flippedNum <= 0x00FFFFFFFFFFFFFFL) + { + val = flippedNum; + outStr.Append('_'); + } + } + + for (int i = 0; i < 10; i++) + { + int charIdx = (int)((val >> (i * 6)) & 0x3F) - 1; + if (charIdx != -1) + outStr.Append(cHash64bToChar[charIdx]); + } + } + } + struct MD5Hash { public uint8[16] mHash; @@ -50,6 +80,14 @@ namespace System.Security.Cryptography val.ToString(strBuffer, "X2", null); } } + + public void Encode(String outStr) + { +#unwarn + HashEncode.HashEncode64(((uint64*)&mHash)[0], outStr); +#unwarn + HashEncode.HashEncode64(((uint64*)&mHash)[1], outStr); + } } class MD5 diff --git a/BeefLibs/corlib/src/Span.bf b/BeefLibs/corlib/src/Span.bf index 2b2f9c36..4931bbaf 100644 --- a/BeefLibs/corlib/src/Span.bf +++ b/BeefLibs/corlib/src/Span.bf @@ -16,18 +16,35 @@ namespace System public this(T[] array) { + if (array == null) + { + this = default; + return; + } mPtr = &array.[Friend]GetRef(0); mLength = array.[Friend]mLength; } public this(T[] array, int index) { + if (array == null) + { + Debug.Assert(index == 0); + this = default; + return; + } mPtr = &array[index]; mLength = array.[Friend]mLength - index; } public this(T[] array, int index, int length) { + if (array == null) + { + Debug.Assert(index == 0 && length == 0); + this = default; + return; + } if (length == 0) mPtr = null; else @@ -110,13 +127,95 @@ namespace System public ref T this[int index] { - [Inline] + [Checked] get + { + Runtime.Assert((uint)index < (uint)mLength); + return ref mPtr[index]; + } + + [Unchecked, Inline] + get { return ref mPtr[index]; } } + public ref T this[Index index] + { + [Checked] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + Runtime.Assert((uint)idx < (uint)mLength); + return ref mPtr[idx]; + } + + [Unchecked, Inline] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + return ref mPtr[idx]; + } + } + + public Span this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + T* start; + switch (range.[Friend]mStart) + { + case .FromFront(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + start = mPtr + offset; + case .FromEnd(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + start = mPtr + mLength - offset; + } + T* end; + if (range.[Friend]mIsClosed) + { + switch (range.[Friend]mEnd) + { + case .FromFront(let offset): + Debug.Assert((uint)offset < (uint)mLength); + end = mPtr + offset + 1; + case .FromEnd(let offset): + Debug.Assert((uint)(offset - 1) <= (uint)mLength); + end = mPtr + mLength - offset + 1; + } + } + else + { + switch (range.[Friend]mEnd) + { + case .FromFront(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + end = mPtr + offset; + case .FromEnd(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + end = mPtr + mLength - offset; + } + } + + return .(start, end - start); + } + } + public Span Slice(int index) { Debug.Assert((uint)index <= (uint)mLength); @@ -200,6 +299,8 @@ namespace System return Enumerator(this); } + public ReverseEnumerator Reversed => ReverseEnumerator(this); + public override void ToString(String strBuffer) { strBuffer.Append("("); @@ -281,6 +382,95 @@ namespace System } + public Result GetNext() mut + { + if (!MoveNext()) + return .Err; + return Current; + } + + public Result GetNextRef() mut + { + if (!MoveNext()) + return .Err; + return &CurrentRef; + } + } + + public struct ReverseEnumerator : IEnumerator, IRefEnumerator + { + private Span mList; + private int mIndex; + private T* mCurrent; + + public this(Span list) + { + mList = list; + mIndex = list.mLength - 1; + mCurrent = null; + } + + public void Dispose() + { + } + + public bool MoveNext() mut + { + if (mIndex >= 0) + { + mCurrent = &mList.mPtr[mIndex]; + mIndex--; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() mut + { + mIndex = mList.mLength + 1; + mCurrent = null; + return false; + } + + public T Current + { + get + { + return *mCurrent; + } + } + + public ref T CurrentRef + { + get + { + return ref *mCurrent; + } + } + + public int Index + { + get + { + return mIndex + 1; + } + } + + public int Length + { + get + { + return mList.mLength; + } + } + + public void Reset() mut + { + mIndex = 0; + mCurrent = null; + } + + public Result GetNext() mut { if (!MoveNext()) diff --git a/BeefLibs/corlib/src/String.bf b/BeefLibs/corlib/src/String.bf index 6a5346b1..70fc7b5b 100644 --- a/BeefLibs/corlib/src/String.bf +++ b/BeefLibs/corlib/src/String.bf @@ -329,7 +329,7 @@ namespace System set { - Debug.Assert((uint)mLength <= (uint)value); + Debug.Assert((uint)value <= (uint)mLength); mLength = (int_strsize)value; } } @@ -1010,17 +1010,95 @@ namespace System public ref char8 this[int index] { + [Checked] get { Debug.Assert((uint)index < (uint)mLength); return ref Ptr[index]; } + [Unchecked, Inline] + get + { + return ref Ptr[index]; + } + + [Checked] set { Debug.Assert((uint)index < (uint)mLength); Ptr[index] = value; } + + [Unchecked, Inline] + set + { + Ptr[index] = value; + } + } + + public ref char8 this[Index index] + { + [Checked] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + Debug.Assert((uint)idx < (uint)mLength); + return ref Ptr[idx]; + } + + [Unchecked, Inline] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + return ref Ptr[idx]; + } + + [Checked] + set + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + Debug.Assert((uint)idx < (uint)mLength); + Ptr[idx] = value; + } + + [Unchecked, Inline] + set + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + Ptr[idx] = value; + } + } + + public StringView this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + return StringView(Ptr, Length)[range]; + } } public void Concat(params Object[] objects) @@ -1351,12 +1429,7 @@ namespace System return -1; } - public bool Contains(String str) - { - return IndexOf(str) != -1; - } - - public bool Contains(String str, bool ignoreCase) + public bool Contains(StringView str, bool ignoreCase = false) { return IndexOf(str, ignoreCase) != -1; } @@ -1542,7 +1615,7 @@ namespace System mLength = newLength; } - public void Insert(int_strsize idx, char8 c) + public void Insert(int idx, char8 c) { Contract.Requires(idx >= 0); @@ -1557,7 +1630,7 @@ namespace System mLength = newLength; } - public void Insert(int_strsize idx, char8 c, int count) + public void Insert(int idx, char8 c, int count) { Contract.Requires(idx >= 0); @@ -1576,7 +1649,7 @@ namespace System mLength = newLength; } - public void Insert(int_strsize idx, char32 c) + public void Insert(int idx, char32 c) { Contract.Requires(idx >= 0); @@ -1625,7 +1698,7 @@ namespace System } } - public void Insert(int_strsize idx, char32 c, int count) + public void Insert(int idx, char32 c, int count) { Contract.Requires(idx >= 0); @@ -1700,7 +1773,7 @@ namespace System //Contract.Assert((char8A | char8B) <= 0x7F, "strings have to be ASCII"); - // uppercase both char8s - notice that we need just one compare per char8 + // uppercase both chars - notice that we need just one compare per char if ((uint32)(charA - 'a') <= (uint32)('z' - 'a')) charA -= 0x20; if ((uint32)(charB - 'a') <= (uint32)('z' - 'a')) charB -= 0x20; @@ -1708,7 +1781,7 @@ namespace System if (charA != charB) return false; - // Next char8 + // Next char curA++;curB++; curLength--; } @@ -1733,7 +1806,7 @@ namespace System //Contract.Assert((char8A | char8B) <= 0x7F, "strings have to be ASCII"); - // uppercase both char8s - notice that we need just one compare per char8 + // uppercase both chars - notice that we need just one compare per char if ((uint32)(charA - 'a') <= (uint32)('z' - 'a')) charA -= 0x20; if ((uint32)(charB - 'a') <= (uint32)('z' - 'a')) charB -= 0x20; @@ -1741,7 +1814,7 @@ namespace System if (charA != charB) return charA - charB; - // Next char8 + // Next char a++;b++; length--; } @@ -1761,7 +1834,7 @@ namespace System int_strsize charB = (int_strsize)*b; //Contract.Assert((char8A | char8B) <= 0x7F, "strings have to be ASCII"); - // uppercase both char8s - notice that we need just one compare per char8 + // uppercase both chars - notice that we need just one compare per char if ((uint32)(charA - 'a') <= (uint32)('z' - 'a')) charA -= 0x20; if ((uint32)(charB - 'a') <= (uint32)('z' - 'a')) charB -= 0x20; @@ -1769,7 +1842,7 @@ namespace System if (charA != charB) return charA - charB; - // Next char8 + // Next char a++;b++; length--; } @@ -2130,6 +2203,82 @@ namespace System TrimEnd(); } + public void TrimEnd(char32 trimChar) + { + let ptr = Ptr; + for (int i = mLength - 1; i >= 0; i--) + { + char8 c = ptr[i]; + if (c >= (char8)0x80) + { + var (c32, idx, len) = GetChar32WithBacktrack(i); + if (c32 != trimChar) + { + if (i < mLength - 1) + RemoveToEnd(i + 1); + return; + } + i = idx; + } + else if ((char32)c != trimChar) + { + if (i < mLength - 1) + RemoveToEnd(i + 1); + return; + } + } + Clear(); + } + + public void TrimEnd(char8 trimChar) + { + TrimEnd((char32)trimChar); + } + + public void TrimStart(char32 trimChar) + { + let ptr = Ptr; + for (int i = 0; i < mLength; i++) + { + char8 c = ptr[i]; + if (c >= (char8)0x80) + { + var (c32, len) = GetChar32(i); + if (c32 != trimChar) + { + if (i > 0) + Remove(0, i); + return; + } + i += len - 1; + } + else if ((char32)c != trimChar) + { + if (i > 0) + Remove(0, i); + return; + } + } + Clear(); + } + + public void TrimStart(char8 trimChar) + { + TrimStart((char32)trimChar); + } + + public void Trim(char32 trimChar) + { + TrimStart(trimChar); + TrimEnd(trimChar); + } + + public void Trim(char8 trimChar) + { + TrimStart((.)trimChar); + TrimEnd((.)trimChar); + } + public void Join(StringView sep, IEnumerator enumerable) { bool isFirst = true; @@ -2271,6 +2420,7 @@ namespace System public RawEnumerator RawChars { + [Inline] get { return RawEnumerator(Ptr, 0, mLength); @@ -2279,6 +2429,7 @@ namespace System public UTF8Enumerator DecodedChars { + [Inline] get { return UTF8Enumerator(Ptr, 0, mLength); @@ -2440,6 +2591,7 @@ namespace System int_strsize mIdx; int_strsize mLength; + [Inline] public this(char8* ptr, int idx, int length) { mPtr = ptr; @@ -2449,11 +2601,13 @@ namespace System public char8 Current { + [Inline] get { return mPtr[mIdx]; } + [Inline] set { mPtr[mIdx] = value; @@ -2462,6 +2616,7 @@ namespace System public ref char8 CurrentRef { + [Inline] get { return ref mPtr[mIdx]; @@ -2470,6 +2625,7 @@ namespace System public int Index { + [Inline] get { return mIdx; @@ -2478,42 +2634,29 @@ namespace System public int Length { + [Inline] get { return mLength; } } - public void Dispose() - { - - } - - public void Reset() - { - - } - - public bool MoveNext() mut + [Inline] + public Result GetNext() mut { ++mIdx; if (mIdx >= mLength) - return false; - return true; - } - - public Result GetNext() mut - { - if (!MoveNext()) return .Err; - return Current; + return mPtr[mIdx]; } + [Inline] public Result GetNextRef() mut { - if (!MoveNext()) + ++mIdx; + if (mIdx >= mLength) return .Err; - return &CurrentRef; + return &mPtr[mIdx]; } } @@ -2669,6 +2812,14 @@ namespace System return mMatchPos; } } + + public int32 MatchIndex + { + get + { + return mCurCount - 1; + } + } public bool HasMore { @@ -2821,6 +2972,97 @@ namespace System mLength = length; } + public ref char8 this[int index] + { + [Checked] + get + { + Runtime.Assert((uint)index < (uint)mLength); + return ref mPtr[index]; + } + + [Unchecked, Inline] + get + { + return ref mPtr[index]; + } + } + + public ref char8 this[Index index] + { + [Checked] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + Runtime.Assert((uint)idx < (uint)mLength); + return ref mPtr[idx]; + } + + [Unchecked, Inline] + get + { + int idx; + switch (index) + { + case .FromFront(let offset): idx = offset; + case .FromEnd(let offset): idx = mLength - offset; + } + return ref mPtr[idx]; + } + } + + public StringView this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + char8* start; + switch (range.[Friend]mStart) + { + case .FromFront(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + start = mPtr + offset; + case .FromEnd(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + start = mPtr + mLength - offset; + } + char8* end; + if (range.[Friend]mIsClosed) + { + switch (range.[Friend]mEnd) + { + case .FromFront(let offset): + Debug.Assert((uint)offset < (uint)mLength); + end = mPtr + offset + 1; + case .FromEnd(let offset): + Debug.Assert((uint)(offset - 1) <= (uint)mLength); + end = mPtr + mLength - offset + 1; + } + } + else + { + switch (range.[Friend]mEnd) + { + case .FromFront(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + end = mPtr + offset; + case .FromEnd(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + end = mPtr + mLength - offset; + } + } + + return .(start, end - start); + } + } + public String.RawEnumerator RawChars { get @@ -2914,6 +3156,13 @@ namespace System return String.[Friend]CompareOrdinalHelper(val1.mPtr, val1.mLength, val2.mPtr, val2.mLength); } + public int CompareTo(StringView strB, bool ignoreCase = false) + { + if (ignoreCase) + return String.[Friend]CompareOrdinalIgnoreCaseHelper(Ptr, Length, strB.Ptr, strB.Length); + return String.[Friend]CompareOrdinalHelper(Ptr, Length, strB.Ptr, strB.Length); + } + public bool Equals(StringView str) { if (mLength != str.[Friend]mLength) @@ -3037,9 +3286,9 @@ namespace System return false; } - public bool Contains(StringView stringView) + public bool Contains(StringView stringView, bool ignoreCase = false) { - return IndexOf(stringView) != -1; + return IndexOf(stringView, ignoreCase) != -1; } public bool StartsWith(StringView b, StringComparison comparisonType = StringComparison.Ordinal) @@ -3130,6 +3379,92 @@ namespace System TrimEnd(); } + public void TrimEnd(char32 trimChar) mut + { + let ptr = Ptr; + for (int i = mLength - 1; i >= 0; i--) + { + char8 c = ptr[i]; + if (c >= (char8)0x80) + { + var (c32, idx, len) = GetChar32WithBacktrack(i); + if (c32 != trimChar) + { + if (i < mLength - 1) + { + mLength = i + 1; + } + return; + } + i = idx; + } + else if (c != (char32)trimChar) + { + if (i < mLength - 1) + { + mLength = i + 1; + } + return; + } + } + Clear(); + } + + public void TrimEnd(char8 trimChar) mut + { + TrimEnd((char32)trimChar); + } + + public void TrimStart(char32 trimChar) mut + { + let ptr = Ptr; + for (int i = 0; i < mLength; i++) + { + char8 c = ptr[i]; + if (c >= (char8)0x80) + { + var (c32, len) = GetChar32(i); + if (c32 != trimChar) + { + if (i > 0) + { + mPtr += i; + mLength -= i; + } + return; + } + i += len - 1; + } + else if (c != (char32)trimChar) + { + if (i > 0) + { + mPtr += i; + mLength -= i; + } + return; + } + } + Clear(); + } + + public void TrimStart(char8 trimChar) mut + { + TrimStart((char32)trimChar); + } + + public void Trim(char32 trimChar) mut + { + TrimStart(trimChar); + TrimEnd(trimChar); + } + + public void Trim(char8 trimChar) mut + { + TrimStart((.)trimChar); + TrimEnd((.)trimChar); + } + public bool StartsWith(char8 c) { if (mLength == 0) @@ -3137,6 +3472,15 @@ namespace System return Ptr[0] == c; } + public bool StartsWith(char32 c) + { + if (c < '\x80') + return StartsWith((char8)c); + if (mLength == 0) + return false; + return UTF8.Decode(Ptr, mLength).c == c; + } + public bool EndsWith(char8 c) { if (mLength == 0) @@ -3144,6 +3488,19 @@ namespace System return Ptr[mLength - 1] == c; } + public bool EndsWith(char32 c) + { + if (c < '\x80') + return EndsWith((char8)c); + if (mLength == 0) + return false; + char8* ptr = Ptr; + int idx = mLength - 1; + while ((idx > 0) && ((uint8)ptr[idx] & 0xC0 == 0x80)) + idx--; + return UTF8.Decode(ptr + idx, mLength - idx).c == c; + } + public void QuoteString(String outString) { String.QuoteString(Ptr, Length, outString); @@ -3226,6 +3583,34 @@ namespace System return StringSplitEnumerator(Ptr, Length, separators, count, options); } + public String Intern() + { + using (String.[Friend]sMonitor.Enter()) + { + bool needsLiteralPass = String.[Friend]sInterns.Count == 0; + String* internalLinkPtr = *((String**)(String.[Friend]sStringLiterals)); + if (internalLinkPtr != String.[Friend]sPrevInternLinkPtr) + { + String.[Friend]sPrevInternLinkPtr = internalLinkPtr; + needsLiteralPass = true; + } + if (needsLiteralPass) + String.[Friend]CheckLiterals(String.[Friend]sStringLiterals); + + String* entryPtr; + if (String.[Friend]sInterns.TryAddAlt(this, out entryPtr)) + { + String result = new String(mLength + 1); + result.Append(this); + result.EnsureNullTerminator(); + *entryPtr = result; + String.[Friend]sOwnedInterns.Add(result); + return result; + } + return *entryPtr; + } + } + public static operator StringView (String str) { StringView sv; diff --git a/BeefLibs/corlib/src/System.bf b/BeefLibs/corlib/src/System.bf index def9c456..9289da0a 100644 --- a/BeefLibs/corlib/src/System.bf +++ b/BeefLibs/corlib/src/System.bf @@ -227,6 +227,26 @@ static } } + public static mixin DeleteContainerAndDisposeItems(var container) + { + if (container != null) + { + for (var value in container) + value.Dispose(); + delete container; + } + } + + public static mixin ClearAndDisposeItems(var container) + { + if (container != null) + { + for (var value in container) + value.Dispose(); + container.Clear(); + } + } + public static mixin DeleteAndNullify(var val) { delete val; diff --git a/BeefLibs/corlib/src/Text/Encoding.bf b/BeefLibs/corlib/src/Text/Encoding.bf index 4421897d..6ba2f20b 100644 --- a/BeefLibs/corlib/src/Text/Encoding.bf +++ b/BeefLibs/corlib/src/Text/Encoding.bf @@ -1,6 +1,7 @@ using System.Diagnostics; namespace System.Text { + [StaticInitPriority(100)] abstract class Encoding { public enum DecodeError diff --git a/BeefLibs/corlib/src/Text/UTF16.bf b/BeefLibs/corlib/src/Text/UTF16.bf index 4a4953f2..4ccecb4d 100644 --- a/BeefLibs/corlib/src/Text/UTF16.bf +++ b/BeefLibs/corlib/src/Text/UTF16.bf @@ -1,7 +1,7 @@ using System.Diagnostics; namespace System.Text { - public class UTF16 + public static class UTF16 { public enum EncodeError { @@ -140,10 +140,10 @@ namespace System.Text public static int GetMaxEncodedLen(int utf8Len) { - // Consider all incoming char8s are < \u80, each incoming char88 equals one outgoing char816 (utfLen * 1) - // For char8s from \u80 to \u7FF, then two incoming char88 equals one outgoing char816 (utfLen * 0.5) - // For char8s from \u800 to \u7FFF, then three incoming char88 equals one or two char816s (utfLen * 0.33) to (utfLen * 0.67) - // For char8s from \u1000 to \u10FFFF, then four incoming char88 equals two outgoing char816s (utfLen * 0.5) + // Consider all incoming chars are < \u80, each incoming char8 equals one outgoing char16 (utfLen * 1) + // For chars from \u80 to \u7FF, then two incoming char8 equals one outgoing char16 (utfLen * 0.5) + // For chars from \u800 to \u7FFF, then three incoming char8 equals one or two char16s (utfLen * 0.33) to (utfLen * 0.67) + // For chars from \u1000 to \u10FFFF, then four incoming char8 equals two outgoing char16s (utfLen * 0.5) return utf8Len; } @@ -211,7 +211,7 @@ namespace System.Text if (c <= '\u{FFFF}') { #if BF_UTF_PEDANTIC - // Illegal UTF16 char8? + // Illegal UTF16 char? Debug.Assert((c <= '\u{D7FF}') || (c >= '\u{E000}')); #endif EncodeChar((char16)c); diff --git a/BeefLibs/corlib/src/Text/UTF8.bf b/BeefLibs/corlib/src/Text/UTF8.bf index 4df4c7f9..0dce24ae 100644 --- a/BeefLibs/corlib/src/Text/UTF8.bf +++ b/BeefLibs/corlib/src/Text/UTF8.bf @@ -1,7 +1,7 @@ using System.Diagnostics; namespace System.Text { - class UTF8 + static class UTF8 { public const int8[256] sTrailingBytesForUTF8 = .( diff --git a/BeefLibs/corlib/src/Threading/Thread.bf b/BeefLibs/corlib/src/Threading/Thread.bf index 8198c23e..39e9fa26 100644 --- a/BeefLibs/corlib/src/Threading/Thread.bf +++ b/BeefLibs/corlib/src/Threading/Thread.bf @@ -8,10 +8,11 @@ namespace System.Threading public delegate void ThreadStart(); public delegate void ParameterizedThreadStart(Object obj); + [StaticInitPriority(100)] public sealed class Thread { private int mInternalThread; - private int32 mPriority; + private ThreadPriority mPriority = .Normal; public int32 mMaxStackSize; private String mName ~ delete _; private Delegate mDelegate; @@ -21,7 +22,7 @@ namespace System.Threading bool mAutoDelete; public static Thread sMainThread = new Thread() ~ delete _; - [StaticInitPriority(101)] + [StaticInitPriority(102)] struct RuntimeThreadInit { static Object Thread_Alloc() @@ -67,6 +68,8 @@ namespace System.Threading if (thread.mName != null) thread.InformThreadNameChange(thread.mName); + if (thread.mPriority != .Normal) + thread.SetPriorityNative((.)thread.mPriority); int32 stackStart = 0; thread.SetStackStart((void*)&stackStart); @@ -219,8 +222,18 @@ namespace System.Threading public ThreadPriority Priority { - get { return (ThreadPriority)GetPriorityNative(); } - set { SetPriorityNative((int32)value); } + get + { + if (mInternalThread != 0) + return (ThreadPriority)GetPriorityNative(); + return mPriority; + } + set + { + mPriority = value; + if (mInternalThread != 0) + SetPriorityNative((int32)value); + } } [CallingConvention(.Cdecl)] private extern int32 GetPriorityNative(); diff --git a/BeefLibs/corlib/src/TimeZoneInfo.bf b/BeefLibs/corlib/src/TimeZoneInfo.bf index e3aa87ac..a14a4127 100644 --- a/BeefLibs/corlib/src/TimeZoneInfo.bf +++ b/BeefLibs/corlib/src/TimeZoneInfo.bf @@ -253,7 +253,10 @@ namespace System { if (rule != null) rule = rule.Clone(); oneYearLocFromUtc = new OffsetAndRule(year, currentYear.BaseUtcOffset, rule); - m_oneYearLocalFromUtc = oneYearLocFromUtc; + if (Interlocked.CompareExchange(ref m_oneYearLocalFromUtc, null, oneYearLocFromUtc) != null) { + delete oneYearLocFromUtc; + oneYearLocFromUtc = m_oneYearLocalFromUtc; + } } return oneYearLocFromUtc; } diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index 73d6acf1..1f79729b 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -20,13 +20,14 @@ namespace System protected const BindingFlags cDefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - protected int32 mSize; - protected TypeId mTypeId; - protected TypeId mBoxedType; - protected TypeFlags mTypeFlags; - protected int32 mMemberDataOffset; - protected TypeCode mTypeCode; - protected uint8 mAlign; + protected int32 mSize; + protected TypeId mTypeId; + protected TypeId mBoxedType; + protected TypeFlags mTypeFlags; + protected int32 mMemberDataOffset; + protected TypeCode mTypeCode; + protected uint8 mAlign; + protected uint8 mAllocStackCountOverride; public static TypeId TypeIdEnd { @@ -683,13 +684,7 @@ namespace System namespace System.Reflection { - public struct TypeId : int32 - { - public Type ToType() - { - return Type.[Friend]sTypes[(int32)this]; - } - } + public struct TypeId : int32 {} [Ordered, AlwaysInclude(AssumeInstantiated=true)] public class TypeInstance : Type @@ -724,6 +719,7 @@ namespace System.Reflection public int32 mMethodIdx; public int32 mVirtualIdx; public int32 mCustomAttributesIdx; + public int32 mReturnCustomAttributesIdx; } public enum ParamFlags : int16 @@ -740,6 +736,7 @@ namespace System.Reflection public TypeId mType; public ParamFlags mParamFlags; public int32 mDefaultIdx; + public int32 mCustomAttributesIdx; } public struct InterfaceData @@ -1137,7 +1134,7 @@ namespace System.Reflection public Type GetGenericArg(int argIdx) { - return mResolvedTypeRefs[argIdx].ToType(); + return Type.GetType(mResolvedTypeRefs[argIdx]); } public override void GetFullName(String strBuffer) @@ -1191,7 +1188,10 @@ namespace System.Reflection let genericType = GetGenericArg(0); let arraySize = [Friend]mInstSize - genericType.Size + genericType.Stride * count; #if BF_ENABLE_OBJECT_DEBUG_FLAGS - obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, 1); + int32 stackCount = Compiler.Options.AllocStackCount; + if (mAllocStackCountOverride != 0) + stackCount = mAllocStackCountOverride; + obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, stackCount); #else void* mem = new [Align(16)] uint8[arraySize]* (?); obj = Internal.UnsafeCastToObject(mem); diff --git a/BeefLibs/corlib/src/Windows.bf b/BeefLibs/corlib/src/Windows.bf index e1d1848c..44f3ba04 100644 --- a/BeefLibs/corlib/src/Windows.bf +++ b/BeefLibs/corlib/src/Windows.bf @@ -48,8 +48,8 @@ namespace System public struct VTable { public function HResult(COM_IUnknown* self, ref Guid riid, void** result) QueryInterface; - public function HResult(COM_IUnknown* self) AddRef; - public function HResult(COM_IUnknown* self) Release; + public function uint32(COM_IUnknown* self) AddRef; + public function uint32(COM_IUnknown* self) Release; } public enum HResult : int32 diff --git a/BeefLibs/curl/src/Curl.bf b/BeefLibs/curl/src/Curl.bf index 50610423..fb42d006 100644 --- a/BeefLibs/curl/src/Curl.bf +++ b/BeefLibs/curl/src/Curl.bf @@ -876,7 +876,7 @@ namespace CURL static extern int curl_easy_perform(void* curl); [CLink, CallingConvention(.Stdcall)] - static extern void* curl_easy_getinfo(void* curl, Option option, void* ptr); + static extern void* curl_easy_getinfo(void* curl, CurlInfo info, void* ptr); [CLink, CallingConvention(.Stdcall)] static extern void* curl_easy_reset(void* curl); @@ -917,6 +917,12 @@ namespace CURL return WrapResult((ReturnCode)curl_easy_setopt(mCURL, (int)option, (int)(void*)val.CStr())); } + public Result SetOpt(Option option, StringView val) + { + Debug.Assert((int)option / 10000 == 1); + return WrapResult((ReturnCode)curl_easy_setopt(mCURL, (int)option, (int)(void*)scope String(4096)..Append(val).CStr())); + } + public Result SetOpt(Option option, int val) { Debug.Assert(((int)option / 10000 == 0) || ((int)option / 10000 == 3)); @@ -935,6 +941,16 @@ namespace CURL return WrapResult((ReturnCode)curl_easy_setopt(mCURL, (int)option, (int)funcPtr)); } + public Result GetInfo(CurlInfo info, String val) + { + char8* ptr = null; + curl_easy_getinfo(mCURL, info, &ptr); + if (ptr == null) + return .Err; + val.Append(ptr); + return .Ok; + } + public Result Perform() { return WrapResult((ReturnCode)curl_easy_perform(mCURL)); diff --git a/BeefLibs/curl/src/Transfer.bf b/BeefLibs/curl/src/Transfer.bf index ba270de8..9460161c 100644 --- a/BeefLibs/curl/src/Transfer.bf +++ b/BeefLibs/curl/src/Transfer.bf @@ -8,7 +8,8 @@ namespace CURL { class Transfer { - CURL.Easy mCurl = new CURL.Easy() ~ delete _; + CURL.Easy mCurl; + bool mOwns; bool mCancelling = false; List mData = new List() ~ delete _; Stopwatch mStatsTimer = new Stopwatch() ~ delete _; @@ -59,7 +60,13 @@ namespace CURL public this() { + mCurl = new CURL.Easy(); + mOwns = true; + } + public this(CURL.Easy curl) + { + mCurl = curl; } public ~this() @@ -67,6 +74,9 @@ namespace CURL mCancelling = true; if (mRunning) mDoneEvent.WaitFor(); + + if (mOwns) + delete mCurl; } int GetCurBytesPerSecond() @@ -119,7 +129,7 @@ namespace CURL return count; } - public void Init(String url) + public void Init(StringView url) { function int(void *p, int dltotal, int dlnow, int ultotal, int ulnow) callback = => Callback; mCurl.SetOptFunc(.XferInfoFunction, (void*)callback); @@ -133,6 +143,13 @@ namespace CURL mCurl.SetOpt(.URL, url); mCurl.SetOpt(.NoProgress, false); mCurl.SetOpt(.IPResolve, (int)CURL.Easy.IPResolve.V4); + mCurl.SetOpt(.HTTPGet, true); + } + + public void InitPost(String url, String param) + { + Init(url); + mCurl.SetOpt(.Postfields, param); } public Result> Perform() @@ -178,6 +195,11 @@ namespace CURL return mResult; } + public void GetContentType(String outContentType) + { + mCurl.GetInfo(.ContentType, outContentType); + } + public void Cancel(bool wait = false) { mCancelling = true; diff --git a/BeefRT/dbg/gc_raw.cpp b/BeefRT/dbg/gc_raw.cpp index e02a54bf..605564b4 100644 --- a/BeefRT/dbg/gc_raw.cpp +++ b/BeefRT/dbg/gc_raw.cpp @@ -421,8 +421,13 @@ void BFGC::RawShutdown() gDbgErrorString = errorStr; gDbgErrorString += "\n"; + int passLeakCount = 0; + for (auto& rawLeak : mSweepInfo.mRawLeaks) { + if (passLeakCount == 20000) // Only display so many... + break; + Beefy::String typeName; if (rawLeak.mRawAllocData->mType != NULL) typeName = rawLeak.mRawAllocData->mType->GetFullName() + "*"; @@ -451,6 +456,8 @@ void BFGC::RawShutdown() if (gDbgErrorString.length() < 256) gDbgErrorString += StrFormat(" %s\n", leakStr.c_str()); + + passLeakCount++; } BF_ASSERT(mSweepInfo.mLeakCount > 0); diff --git a/BeefRT/rt/BfObjects.h b/BeefRT/rt/BfObjects.h index c9234ed1..c7e79574 100644 --- a/BeefRT/rt/BfObjects.h +++ b/BeefRT/rt/BfObjects.h @@ -84,7 +84,7 @@ namespace bf void*(*Alloc)(intptr size); void(*Free)(void* ptr); void(*Object_Delete)(bf::System::Object* obj); - void(*Object_ToString)(bf::System::Object* obj, bf::System::String* str); + void* mUnused0; bf::System::Type* (*Object_GetType)(bf::System::Object* obj); void(*Object_GCMarkMembers)(bf::System::Object* obj); bf::System::Object* (*Object_DynamicCastToTypeId)(bf::System::Object* obj, int typeId); diff --git a/BeefRT/rt/Internal.cpp b/BeefRT/rt/Internal.cpp index d391e642..5bb1d054 100644 --- a/BeefRT/rt/Internal.cpp +++ b/BeefRT/rt/Internal.cpp @@ -84,8 +84,7 @@ namespace bf BFRT_EXPORT static Object* UnsafeCastToObject(void* inPtr); BFRT_EXPORT static void* UnsafeCastToPtr(Object* obj); BFRT_EXPORT static void ObjectDynCheck(Object* object, int typeId, bool allowNull); - BFRT_EXPORT static void ObjectDynCheckFailed(Object* object, int typeId); - BFRT_EXPORT static void Throw(Exception* ex); + BFRT_EXPORT static void ObjectDynCheckFailed(Object* object, int typeId); BFRT_EXPORT static void ThrowIndexOutOfRange(intptr stackOffset); BFRT_EXPORT static void FatalError(String* error, intptr stackOffset = 0); BFRT_EXPORT static void MemCpy(void* dest, void* src, intptr length); @@ -215,18 +214,20 @@ static void TestReadCmd(Beefy::String& str); static void Internal_FatalError(const char* error) { - if (gClientPipe != NULL) + if ((gClientPipe != NULL) && (!gTestBreakOnFailure)) { Beefy::String str = ":TestFatal\t"; str += error; + str.Replace('\n', '\r'); str += "\n"; TestString(str); Beefy::String result; - TestReadCmd(result); + TestReadCmd(result); + exit(1); } - - BfpSystem_FatalError(error, "BEEF FATAL ERROR"); + else + BfpSystem_FatalError(error, "BEEF FATAL ERROR"); } extern "C" BFRT_EXPORT int BF_CALLTYPE ftoa(float val, char* str) @@ -395,26 +396,21 @@ void* Internal::UnsafeCastToPtr(Object* obj) return (void*)obj; } -void Internal::Throw(Exception* ex) -{ - bf::System::String* exStr = gBfRtCallbacks.String_Alloc(); - gBfRtCallbacks.Object_ToString(ex, exStr); - - Beefy::String errorStr = StrFormat("FATAL: %s", exStr->CStr()); - SETUP_ERROR(errorStr.c_str(), 1); - BF_DEBUG_BREAK(); - gBfRtCallbacks.DebugMessageData_Fatal(); - - printf("Thrown: %s", errorStr.c_str()); - //TODO: What about capturing callstack? - - exit(3); - - //throw ex; -} - void Internal::ThrowIndexOutOfRange(intptr stackOffset) { + if (gClientPipe != NULL) + { + if (gTestBreakOnFailure) + { + SETUP_ERROR("Index out of range", (int)(2 + stackOffset)); + BF_DEBUG_BREAK(); + } + + Beefy::String str = ":TestFail\tIndex out of range\n"; + TestString(str); + exit(1); + } + if ((stackOffset != -1) && (::IsDebuggerPresent())) { SETUP_ERROR("Index out of range", (int)(2 + stackOffset)); @@ -426,6 +422,22 @@ void Internal::ThrowIndexOutOfRange(intptr stackOffset) void Internal::FatalError(bf::System::String* error, intptr stackOffset) { + if (gClientPipe != NULL) + { + if (gTestBreakOnFailure) + { + SETUP_ERROR(error->CStr(), (int)(2 + stackOffset)); + BF_DEBUG_BREAK(); + } + + Beefy::String str = ":TestFail\t"; + str += error->CStr(); + str.Replace('\n', '\r'); + str += "\n"; + TestString(str); + exit(1); + } + if ((stackOffset != -1) && (::IsDebuggerPresent())) { SETUP_ERROR(error->CStr(), (int)(2 + stackOffset)); @@ -659,6 +671,7 @@ void Internal::Test_Error(char* error) { Beefy::String str = ":TestFail\t"; str += error; + str.Replace('\n', '\r'); str += "\n"; TestString(str); } @@ -670,12 +683,7 @@ void Internal::Test_Write(char* strPtr) { Beefy::String str = ":TestWrite\t"; str += strPtr; - for (char& c : str) - { - if (c == '\n') - c = '\r'; - } - + str.Replace('\n', '\r'); str += "\n"; TestString(str); } diff --git a/BeefRT/rt/Thread.cpp b/BeefRT/rt/Thread.cpp index 5cf5dce8..655e71f4 100644 --- a/BeefRT/rt/Thread.cpp +++ b/BeefRT/rt/Thread.cpp @@ -53,12 +53,12 @@ void Thread::SetJoinOnDelete(bool joinOnDelete) int Thread::GetPriorityNative() { - return (int)BfpThread_GetPriority(GetInternalThread()->mThreadHandle, NULL); + return (int)BfpThread_GetPriority(GetInternalThread()->mThreadHandle, NULL) + 2; } void Thread::SetPriorityNative(int priority) { - return BfpThread_SetPriority(GetInternalThread()->mThreadHandle, (BfpThreadPriority)priority, NULL); + return BfpThread_SetPriority(GetInternalThread()->mThreadHandle, (BfpThreadPriority)(priority - 2), NULL); } bool Thread::GetIsAlive() diff --git a/BeefTools/BeefPerf/src/BPApp.bf b/BeefTools/BeefPerf/src/BPApp.bf index fba0d9c4..730ec048 100644 --- a/BeefTools/BeefPerf/src/BPApp.bf +++ b/BeefTools/BeefPerf/src/BPApp.bf @@ -234,9 +234,8 @@ namespace BeefPerf mMainFrame = new MainFrame(); mDockingFrame = mMainFrame.mDockingFrame; - BFWindow.Flags windowFlags = BFWindow.Flags.Border | BFWindow.Flags.SysMenu | //| BFWindow.Flags.CaptureMediaKeys | - BFWindow.Flags.Caption | BFWindow.Flags.Minimize | BFWindow.Flags.QuitOnClose | BFWindowBase.Flags.Resizable | - BFWindow.Flags.Menu | BFWindow.Flags.SysMenu; + BFWindow.Flags windowFlags = .Border | .SysMenu | .Caption | .Minimize | .Maximize | + .QuitOnClose | .Resizable | .Menu; if (mWantsFullscreen) windowFlags |= BFWindowBase.Flags.Fullscreen; @@ -336,7 +335,7 @@ namespace BeefPerf void SetupTab(TabbedView tabbedView, String label, Widget widget) { - var tabButton = tabbedView.AddTab(label, 0, widget, false); + var tabButton = tabbedView.AddTab(label, 0, widget, false, 0); tabButton.mCloseClickedEvent.Add(new () => { var tabbedView = tabButton.mTabbedView; @@ -921,7 +920,7 @@ namespace BeefPerf TabbedView tabbedView = FindTabbedView(mDockingFrame, -1, 1); if (tabbedView != null) { - tabButton = tabbedView.AddTab(name, width, tabContent, ownsContent); + tabButton = tabbedView.AddTab(name, width, tabContent, ownsContent, 0); result = ShowTabResult.OpenedNew; } } diff --git a/BeefTools/BeefPerf/src/ScriptManager.bf b/BeefTools/BeefPerf/src/ScriptManager.bf index 45c12433..f3200f1f 100644 --- a/BeefTools/BeefPerf/src/ScriptManager.bf +++ b/BeefTools/BeefPerf/src/ScriptManager.bf @@ -311,7 +311,7 @@ namespace BeefPerf { var str = scope::String(); if (argView.UnQuoteString(str) case .Err) - Fail("Failed to unquote string"); + Fail($"Failed to unquote string: {argView}\nCmd:{cmdLineView}"); args.Add(str); isLiteralString = false; } @@ -322,7 +322,7 @@ namespace BeefPerf case .Ok(let val): args.Add(scope:: box val); case .Err: - Fail("Failed to parse float"); + Fail($"Failed to parse float: {argView}\nCmd:{cmdLineView}"); return; } } @@ -333,7 +333,7 @@ namespace BeefPerf case .Ok(let val): args.Add(scope:: box val); case .Err: - Fail("Failed to parse double"); + Fail($"Failed to parse double: {argView}\nCmd:{cmdLineView}"); return; } } @@ -344,7 +344,7 @@ namespace BeefPerf case .Ok(let val): args.Add(scope:: box val); case .Err: - Fail("Failed to parse int"); + Fail($"Failed to parse int: {argView}\nCmd:{cmdLineView}"); return; } } diff --git a/BeefySysLib/BeefySysLib.cpp b/BeefySysLib/BeefySysLib.cpp index 4d55d00d..d75ef692 100644 --- a/BeefySysLib/BeefySysLib.cpp +++ b/BeefySysLib/BeefySysLib.cpp @@ -610,6 +610,23 @@ BF_EXPORT void BF_CALLTYPE Gfx_DrawQuads(TextureSegment* textureSegment, Default } } +BF_EXPORT void BF_CALLTYPE Gfx_DrawIndexedVertices(int vertexSize, void* vtxData, int vtxCount, uint16* idxData, int idxCount) +{ + DrawLayer* drawLayer = gBFApp->mRenderDevice->mCurDrawLayer; + + uint16 idxOfs; + void* drawBatchVtxPtr; + uint16* drawBatchIdxPtr; + gBFApp->mRenderDevice->mCurDrawLayer->AllocIndexed(vtxCount, idxCount, (void**)&drawBatchVtxPtr, &drawBatchIdxPtr, &idxOfs); + BF_ASSERT(gBFApp->mRenderDevice->mCurDrawLayer->mCurDrawBatch->mVtxSize == vertexSize); + + uint16* idxPtr = idxData; + for (int idxIdx = 0; idxIdx < idxCount; idxIdx++) + *(drawBatchIdxPtr++) = *(idxPtr++) + idxOfs; + + memcpy(drawBatchVtxPtr, vtxData, vertexSize * vtxCount); +} + BF_EXPORT void BF_CALLTYPE Gfx_DrawIndexedVertices2D(int vertexSize, void* vtxData, int vtxCount, uint16* idxData, int idxCount, float a, float b, float c, float d, float tx, float ty, float z) { DrawLayer* drawLayer = gBFApp->mRenderDevice->mCurDrawLayer; @@ -643,14 +660,14 @@ BF_EXPORT void BF_CALLTYPE Gfx_DrawIndexedVertices2D(int vertexSize, void* vtxDa } } -BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantData(int slotIdx, void* constData, int size) +BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size) { - gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantData(slotIdx, constData, size); + gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantData(usageIdx, slotIdx, constData, size); } -BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount) +BF_EXPORT void BF_CALLTYPE Gfx_SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount) { - gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantDataTyped(slotIdx, constData, size, typeData, typeCount); + gBFApp->mRenderDevice->mCurDrawLayer->SetShaderConstantDataTyped(usageIdx, slotIdx, constData, size, typeData, typeCount); } BF_EXPORT void BF_CALLTYPE Gfx_QueueRenderCmd(RenderCmd* renderCmd) @@ -678,6 +695,16 @@ BF_EXPORT void BF_CALLTYPE RenderState_Delete(RenderState* renderState) delete renderState; } +BF_EXPORT void BF_CALLTYPE RenderState_SetTexWrap(RenderState* renderState, bool texWrap) +{ + renderState->SetTexWrap(texWrap); +} + +BF_EXPORT void BF_CALLTYPE RenderState_SetWireframe(RenderState* renderState, bool wireframe) +{ + renderState->SetWireframe(wireframe); +} + BF_EXPORT void BF_CALLTYPE RenderState_SetClip(RenderState* renderState, float x, float y, float width, float height) { BF_ASSERT((width >= 0) && (height >= 0)); @@ -715,6 +742,11 @@ BF_EXPORT void BF_CALLTYPE RenderState_SetDepthWrite(RenderState* renderState, i renderState->SetWriteDepthBuffer(depthWrite != 0); } +BF_EXPORT void BF_CALLTYPE RenderState_SetTopology(RenderState* renderState, int topology) +{ + renderState->SetTopology((Topology3D)topology); +} + BF_EXPORT Shader* BF_CALLTYPE Gfx_LoadShader(const char* fileName, VertexDefinition* vertexDefinition) { return gBFApp->mRenderDevice->LoadShader(fileName, vertexDefinition); diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj index 46f8d0c6..b84cb83f 100644 --- a/BeefySysLib/BeefySysLib.vcxproj +++ b/BeefySysLib/BeefySysLib.vcxproj @@ -445,6 +445,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -1929,16 +1930,20 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + + + + @@ -1958,6 +1963,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -2152,6 +2158,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -2161,7 +2168,9 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + + @@ -2172,6 +2181,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters index a4f7ebce..016806a0 100644 --- a/BeefySysLib/BeefySysLib.vcxproj.filters +++ b/BeefySysLib/BeefySysLib.vcxproj.filters @@ -707,6 +707,21 @@ src\util + + src\gfx + + + src\util + + + src\util + + + src\util + + + src\util + @@ -1075,6 +1090,21 @@ src\util + + src\gfx + + + src\util + + + src\util + + + src\util + + + src\util + diff --git a/BeefySysLib/CMakeLists.txt b/BeefySysLib/CMakeLists.txt index ef8f5391..2d6993f2 100644 --- a/BeefySysLib/CMakeLists.txt +++ b/BeefySysLib/CMakeLists.txt @@ -278,6 +278,7 @@ file(GLOB SRC_FILES util/BSpline.cpp util/CatmullRom.cpp util/ChunkedDataBuffer.cpp + util/Compress.cpp util/CubicFuncSpline.cpp util/CubicSpline.cpp util/FileEnumerator.cpp @@ -285,11 +286,13 @@ file(GLOB SRC_FILES util/Heap.cpp util/Json.cpp util/MappedFile.cpp + util/MathUtils.cpp util/Matrix4.cpp util/PerfTimer.cpp util/Point.cpp util/PolySpline.cpp util/Quaternion.cpp + util/Sphere.cpp util/String.cpp util/StackHelper.cpp util/ThreadPool.cpp diff --git a/BeefySysLib/Common.cpp b/BeefySysLib/Common.cpp index 8bd5a04d..a5edbebf 100644 --- a/BeefySysLib/Common.cpp +++ b/BeefySysLib/Common.cpp @@ -7,6 +7,7 @@ #include "platform/PlatformHelper.h" #ifndef BF_SMALL +#define STB_SPRINTF_DECORATE(name) BF_stbsp_##name #define STB_SPRINTF_IMPLEMENTATION #include "third_party/stb/stb_sprintf.h" #endif @@ -811,7 +812,7 @@ String Beefy::vformat(const char* fmt, va_list argPtr) { String str; char buf[STB_SPRINTF_MIN]; - stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr); + BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr); return str; } #endif @@ -943,6 +944,21 @@ char* Beefy::LoadTextData(const StringImpl& path, int* size) return data; } +bool Beefy::LoadTextData(const StringImpl& path, StringImpl& str) +{ + int size = 0; + char* data = LoadTextData(path, &size); + if (data == NULL) + return false; + if ((str.mAllocSizeAndFlags & StringImpl::DynAllocFlag) != 0) + str.Release(); + str.mPtr = data; + str.mAllocSizeAndFlags = size | StringImpl::DynAllocFlag | StringImpl::StrPtrFlag; + str.mLength = size; + return true; +} + + #ifdef BF_MINGW unsigned long long __cdecl _byteswap_uint64(unsigned long long _Int64) { @@ -1262,4 +1278,5 @@ bool Beefy::RecursiveDeleteDirectory(const StringImpl& dirPath) void Beefy::BFFatalError(const char* message, const char* file, int line) { BFFatalError(String(message), String(file), line); -} \ No newline at end of file +} + diff --git a/BeefySysLib/Common.h b/BeefySysLib/Common.h index fa309baa..781eb435 100644 --- a/BeefySysLib/Common.h +++ b/BeefySysLib/Common.h @@ -220,6 +220,7 @@ int32 GetHighestBitSet(int32 n); uint8* LoadBinaryData(const StringImpl& path, int* size); char* LoadTextData(const StringImpl& path, int* size); +bool LoadTextData(const StringImpl& path, StringImpl& str); int64 GetFileTimeWrite(const StringImpl& path); String GetFileDir(const StringImpl& path); String GetFileName(const StringImpl& path); diff --git a/BeefySysLib/DataStream.h b/BeefySysLib/DataStream.h index 813be3d1..26a46a84 100644 --- a/BeefySysLib/DataStream.h +++ b/BeefySysLib/DataStream.h @@ -49,6 +49,14 @@ public: Read((void*) &val, sizeof(T)); } + template + T ReadT() + { + T val; + Read((void*)&val, sizeof(T)); + return val; + } + virtual void Write(float val); virtual void Write(uint8 val); virtual void Write(int8 val); diff --git a/BeefySysLib/FileStream.cpp b/BeefySysLib/FileStream.cpp index 1e850d76..08ee6f3d 100644 --- a/BeefySysLib/FileStream.cpp +++ b/BeefySysLib/FileStream.cpp @@ -219,7 +219,8 @@ void SysFileStream::SetSizeFast(int size) int curPos = GetPos(); SetPos(size); - BfpFile_Truncate(mFile); + BfpFileResult result = BfpFileResult_Ok; + BfpFile_Truncate(mFile, &result); SetPos(curPos); return; } diff --git a/BeefySysLib/Span.h b/BeefySysLib/Span.h index a09388aa..a57e3ba6 100644 --- a/BeefySysLib/Span.h +++ b/BeefySysLib/Span.h @@ -9,7 +9,7 @@ class Span { public: T* mVals; - int mSize; + intptr mSize; public: struct Iterator @@ -47,16 +47,17 @@ public: Span() { - + mVals = NULL; + mSize = 0; } - Span(T* mPtr, int size) + Span(T* mPtr, intptr size) { - mSize = (int)refVec.size(); - mVals = NULL; + mVals = mPtr; + mSize = size; } - T& operator[](int idx) const + T& operator[](intptr idx) const { return mVals[idx]; } @@ -76,7 +77,7 @@ public: return mVals[mSize - 1]; } - int size() const + intptr size() const { return mSize; } @@ -91,7 +92,7 @@ public: return mSize == 0; } - T Get(int idx) + T Get(intptr idx) { if ((idx < 0) || (idx >= mSize)) return (T)0; @@ -99,7 +100,7 @@ public: } template - T2 GetAs(int idx) + T2 GetAs(intptr idx) { if ((idx < 0) || (idx >= mSize)) return (T2)0; @@ -120,7 +121,7 @@ public: return mVals[0]; } - void SetSize(int size) + void SetSize(intptr size) { BF_ASSERT(size <= mSize); mSize = size; diff --git a/BeefySysLib/gfx/DrawLayer.cpp b/BeefySysLib/gfx/DrawLayer.cpp index 24cefac8..098e9250 100644 --- a/BeefySysLib/gfx/DrawLayer.cpp +++ b/BeefySysLib/gfx/DrawLayer.cpp @@ -339,9 +339,9 @@ void DrawLayer::SetTexture(int texIdx, Texture* texture) } } -void DrawLayer::SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount) +void DrawLayer::SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount) { - SetShaderConstantData(slotIdx, constData, size); + SetShaderConstantData(usageIdx, slotIdx, constData, size); } /// @@ -390,5 +390,6 @@ BF_EXPORT void BF_CALLTYPE DrawLayer_DrawToRenderTarget(DrawLayer* drawLayer, Te renderDevice->PhysSetRenderState(renderDevice->mDefaultRenderState); renderDevice->PhysSetRenderTarget(textureSegment->mTexture); drawLayer->Draw(); + drawLayer->Clear(); renderDevice->mCurRenderTarget = prevTarget; } diff --git a/BeefySysLib/gfx/DrawLayer.h b/BeefySysLib/gfx/DrawLayer.h index 3c30582b..65d06dd1 100644 --- a/BeefySysLib/gfx/DrawLayer.h +++ b/BeefySysLib/gfx/DrawLayer.h @@ -99,8 +99,8 @@ public: virtual DrawBatch* AllocateBatch(int minVtxCount, int minIdxCount); void QueueRenderCmd(RenderCmd* renderCmd); virtual RenderCmd* CreateSetTextureCmd(int textureIdx, Texture* texture) = 0; - virtual void SetShaderConstantData(int slotIdx, void* constData, int size) = 0; - virtual void SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount); + virtual void SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size) = 0; + virtual void SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount); public: DrawLayer(); diff --git a/BeefySysLib/gfx/FTFont.cpp b/BeefySysLib/gfx/FTFont.cpp index 36460849..9f65052d 100644 --- a/BeefySysLib/gfx/FTFont.cpp +++ b/BeefySysLib/gfx/FTFont.cpp @@ -144,7 +144,16 @@ bool FTFont::Load(const StringImpl& fileName, float pointSize) face->mFileName = fileName; FT_Face ftFace = NULL; - auto error = FT_New_Face(gFTLibrary, fileName.c_str(), 0, &ftFace); + + String useFileName = fileName; + int faceIdx = 0; + int atPos = (int)useFileName.IndexOf('@'); + if (atPos != -1) + { + faceIdx = atoi(useFileName.c_str() + atPos + 1); + useFileName.RemoveToEnd(atPos); + } + auto error = FT_New_Face(gFTLibrary, useFileName.c_str(), faceIdx, &ftFace); face->mFTFace = ftFace; } else diff --git a/BeefySysLib/gfx/ModelDef.cpp b/BeefySysLib/gfx/ModelDef.cpp index 7e37fc52..95fbaa86 100644 --- a/BeefySysLib/gfx/ModelDef.cpp +++ b/BeefySysLib/gfx/ModelDef.cpp @@ -2,9 +2,28 @@ #include "BFApp.h" #include "gfx/RenderDevice.h" #include "gfx/ModelInstance.h" +#include "util/Dictionary.h" +#include "util/TLSingleton.h" +#include "FileStream.h" +#include "MemStream.h" +#include "util/MathUtils.h" +#include "util/Sphere.h" +#include "util/HashSet.h" +#include "util/BeefPerf.h" + +#pragma warning(disable:4190) USING_NS_BF; +struct ModelManager +{ +public: + Dictionary mMaterialMap; +}; + +ModelManager sModelManager; +static TLSingleton gModelDef_TLStrReturn; + void Beefy::ModelAnimation::GetJointTranslation(int jointIdx, float frameNum, ModelJointTranslation* outJointTranslation) { // Frame 35 @@ -35,9 +54,47 @@ void Beefy::ModelAnimation::GetJointTranslation(int jointIdx, float frameNum, Mo // -BF_EXPORT ModelInstance* BF_CALLTYPE ModelDef_CreateModelInstance(ModelDef* modelDef) +BF_EXPORT ModelInstance* BF_CALLTYPE ModelDef_CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags) { - return gBFApp->mRenderDevice->CreateModelInstance(modelDef); + return gBFApp->mRenderDevice->CreateModelInstance(modelDef, flags); +} + +BF_EXPORT void ModelDef_Compact(ModelDef* modelDef) +{ + modelDef->Compact(); +} + +BF_EXPORT void ModelDef_GetBounds(ModelDef* modelDef, Vector3& min, Vector3& max) +{ + modelDef->GetBounds(min, max); +} + +BF_EXPORT void ModelDef_SetBaseDir(ModelDef* modelDef, char* baseDIr) +{ + modelDef->mLoadDir = baseDIr; +} + +BF_EXPORT const char* BF_CALLTYPE ModelDef_GetInfo(ModelDef* modelDef) +{ + String& outString = *gModelDef_TLStrReturn.Get(); + outString.Clear(); + for (int meshIdx = 0; meshIdx < (int)modelDef->mMeshes.mSize; meshIdx++) + { + auto mesh = modelDef->mMeshes[meshIdx]; + for (int primIdx = 0; primIdx < (int)mesh.mPrimitives.mSize; primIdx++) + { + auto prims = mesh.mPrimitives[primIdx]; + outString += StrFormat("%d\t%d\t%d\t%d", meshIdx, primIdx, prims.mIndices.size(), prims.mVertices.size()); + for (auto& texPath : prims.mTexPaths) + { + outString += "\t"; + outString += texPath; + } + + outString += "\n"; + } + } + return outString.c_str(); } BF_EXPORT float BF_CALLTYPE ModelDef_GetFrameRate(ModelDef* modelDef) @@ -60,6 +117,19 @@ BF_EXPORT ModelAnimation* BF_CALLTYPE ModelDef_GetAnimation(ModelDef* modelDef, return &modelDef->mAnims[animIdx]; } +BF_EXPORT void BF_CALLTYPE ModelDef_SetTextures(ModelDef* modelDef, int32 meshIdx, int32 primitivesIdx, char** paths, int32 pathCount) +{ + auto& prims = modelDef->mMeshes[meshIdx].mPrimitives[primitivesIdx]; + prims.mTexPaths.Clear(); + for (int i = 0; i < pathCount; i++) + prims.mTexPaths.Add(paths[i]); +} + +BF_EXPORT bool BF_CALLTYPE ModelDef_RayIntersect(ModelDef* modelDef, const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance) +{ + return modelDef->RayIntersect(worldMtx, origin, vec, outIntersect, outDistance); +} + BF_EXPORT void BF_CALLTYPE ModelDefAnimation_GetJointTranslation(ModelAnimation* modelAnimation, int jointIdx, float frame, ModelJointTranslation* outJointTranslation) { modelAnimation->GetJointTranslation(jointIdx, frame, outJointTranslation); @@ -77,9 +147,629 @@ BF_EXPORT const char* BF_CALLTYPE ModelDefAnimation_GetName(ModelAnimation* mode BF_EXPORT void BF_CALLTYPE ModelDefAnimation_Clip(ModelAnimation* modelAnimation, int startFrame, int numFrames) { - modelAnimation->mFrames.erase(modelAnimation->mFrames.begin(), modelAnimation->mFrames.begin() + startFrame); - modelAnimation->mFrames.erase(modelAnimation->mFrames.begin() + numFrames, modelAnimation->mFrames.end()); + modelAnimation->mFrames.RemoveRange(0, startFrame); + modelAnimation->mFrames.RemoveRange(numFrames, modelAnimation->mFrames.Count() - numFrames); +} + +ModelDef::ModelDef() +{ + mFlags = Flags_None; +} + +ModelDef::~ModelDef() +{ + for (auto& materialInstance : mMaterials) + { + materialInstance.mDef->mRefCount--; + } +} + +void ModelDef::Compact() +{ + for (auto& mesh : mMeshes) + { + for (auto& prims : mesh.mPrimitives) + { + Array vtxMap; + vtxMap.Insert(0, -1, prims.mVertices.mSize); + int newVtxIdx = 0; + + for (uint16 vtxIdx : prims.mIndices) + { + if (vtxMap[vtxIdx] == -1) + vtxMap[vtxIdx] = newVtxIdx++; + } + + if (newVtxIdx >= prims.mVertices.mSize) + continue; + + for (uint16& vtxIdx : prims.mIndices) + vtxIdx = vtxMap[vtxIdx]; + + Array oldVertices = prims.mVertices; + prims.mVertices.Resize(newVtxIdx); + for (int oldVtxIdx = 0; oldVtxIdx < oldVertices.mSize; oldVtxIdx++) + { + int newVtxIdx = vtxMap[oldVtxIdx]; + if (newVtxIdx != -1) + prims.mVertices[newVtxIdx] = oldVertices[oldVtxIdx]; + } + } + } +} + +void ModelDef::CalcBounds() +{ + int vtxCount = 0; + + for (auto& mesh : mMeshes) + { + for (auto& prims : mesh.mPrimitives) + { + for (auto& vtx : prims.mVertices) + { + if (vtxCount == 0) + { + mBounds.mMin = vtx.mPosition; + mBounds.mMax = vtx.mPosition; + } + else + { + mBounds.mMin.mX = BF_MIN(mBounds.mMin.mX, vtx.mPosition.mX); + mBounds.mMin.mY = BF_MIN(mBounds.mMin.mY, vtx.mPosition.mY); + mBounds.mMin.mZ = BF_MIN(mBounds.mMin.mZ, vtx.mPosition.mZ); + + mBounds.mMax.mX = BF_MAX(mBounds.mMax.mX, vtx.mPosition.mX); + mBounds.mMax.mY = BF_MAX(mBounds.mMax.mY, vtx.mPosition.mY); + mBounds.mMax.mZ = BF_MAX(mBounds.mMax.mZ, vtx.mPosition.mZ); + } + + vtxCount++; + } + } + } + + mFlags = (Flags)(mFlags | Flags_HasBounds); +} + +void ModelDef::GetBounds(Vector3& min, Vector3& max) +{ + if ((mFlags & Flags_HasBounds) == 0) + CalcBounds(); + + min = mBounds.mMin; + max = mBounds.mMax; +} + +#define SWAP(x, y) { auto temp = x; x = y; y = temp; } +#define N (sizeof(A)/sizeof(A[0])) + +// Partition using Lomuto partition scheme +static int partition(float a[], int left, int right, int pIndex) +{ + // pick `pIndex` as a pivot from the array + float pivot = a[pIndex]; + + // Move pivot to end + SWAP(a[pIndex], a[right]); + + // elements less than the pivot will be pushed to the left of `pIndex`; + // elements more than the pivot will be pushed to the right of `pIndex`; + // equal elements can go either way + pIndex = left; + + // each time we find an element less than or equal to the pivot, `pIndex` + // is incremented, and that element would be placed before the pivot. + for (int i = left; i < right; i++) + { + if (a[i] <= pivot) + { + SWAP(a[i], a[pIndex]); + pIndex++; + } + } + + // move pivot to its final place + SWAP(a[pIndex], a[right]); + + // return `pIndex` (index of the pivot element) + return pIndex; +} + +// Returns the k'th smallest element in the list within `left…right` +// (i.e., `left <= k <= right`). The search space within the array is +// changing for each round – but the list is still the same size. +// Thus, `k` does not need to be updated with each round. +static float quickselect(float A[], int left, int right, int k) +{ + // If the array contains only one element, return that element + if (left == right) { + return A[left]; + } + + // select `pIndex` between left and right + int pIndex = left + rand() % (right - left + 1); + + pIndex = partition(A, left, right, pIndex); + + // The pivot is in its final sorted position + if (k == pIndex) { + return A[k]; + } + + // if `k` is less than the pivot index + else if (k < pIndex) { + return quickselect(A, left, pIndex - 1, k); + } + + // if `k` is more than the pivot index + else { + return quickselect(A, pIndex + 1, right, k); + } +} + +void ModelDef::GenerateCollisionData() +{ + BP_ZONE("ModelDef::GenerateCollisionData"); + + mFlags = (Flags)(mFlags | Flags_HasBVH); + + int statsWorkItrs = 0; + int statsWorkTris = 0; + + BF_ASSERT(mBVNodes.IsEmpty()); + + struct _WorkEntry + { + int mParentNodeIdx; + int mTriWorkIdx; + int mTriWorkCount; + }; + + Array triWorkList; + + int triCount = 0; + for (auto& mesh : mMeshes) + { + for (auto& prims : mesh.mPrimitives) + { + int startIdx = mBVIndices.mSize; + triCount += prims.mIndices.mSize / 3; + triWorkList.Reserve(triWorkList.mSize + triCount); + mBVIndices.Reserve(mBVIndices.mSize + prims.mIndices.mSize); + mBVVertices.Reserve(mBVVertices.mSize + prims.mVertices.mSize); + + for (int triIdx = 0; triIdx < triCount; triIdx++) + triWorkList.Add(startIdx / 3 + triIdx); + + for (auto idx : prims.mIndices) + { + mBVIndices.Add(idx + startIdx); + } + + for (auto& vtx : prims.mVertices) + mBVVertices.Add(vtx.mPosition); + } + } + + Array<_WorkEntry> workList; + + _WorkEntry workEntry; + workEntry.mParentNodeIdx = -1; + workEntry.mTriWorkIdx = 0; + workEntry.mTriWorkCount = triWorkList.mSize; + workList.Add(workEntry); + + Array points; + points.Reserve(triWorkList.mSize); + Array centers; + centers.Reserve(triWorkList.mSize); + Array left; + left.Reserve(triWorkList.mSize); + Array right; + right.Reserve(triWorkList.mSize); + + mBVTris.Reserve(triWorkList.mSize * 2); + + while (!workList.IsEmpty()) + { + auto workEntry = workList.back(); + workList.pop_back(); + + statsWorkItrs++; + statsWorkTris += workEntry.mTriWorkCount; + + centers.Clear(); + left.Clear(); + right.Clear(); + + int nodeIdx = mBVNodes.mSize; + mBVNodes.Add(ModelBVNode()); + + if (workEntry.mParentNodeIdx != -1) + { + auto& bvParent = mBVNodes[workEntry.mParentNodeIdx]; + if (bvParent.mLeft == -1) + bvParent.mLeft = nodeIdx; + else + { + BF_ASSERT(bvParent.mRight == -1); + bvParent.mRight = nodeIdx; + } + } + + for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++) + { + bool inLeft = false; + bool inRight = false; + + int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx]; + for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++) + { + int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx]; + bool isNewVtx = false; + + int32* valuePtr = NULL; + auto& vtx = mBVVertices[vtxIdx]; + + auto& bvNode = mBVNodes[nodeIdx]; + if ((triIdxIdx == 0) && (triVtxIdx == 0)) + { + bvNode.mBoundAABB.mMin = vtx; + bvNode.mBoundAABB.mMax = vtx; + } + else + { + bvNode.mBoundAABB.mMin.mX = BF_MIN(bvNode.mBoundAABB.mMin.mX, vtx.mX); + bvNode.mBoundAABB.mMin.mY = BF_MIN(bvNode.mBoundAABB.mMin.mY, vtx.mY); + bvNode.mBoundAABB.mMin.mZ = BF_MIN(bvNode.mBoundAABB.mMin.mZ, vtx.mZ); + + bvNode.mBoundAABB.mMax.mX = BF_MAX(bvNode.mBoundAABB.mMax.mX, vtx.mX); + bvNode.mBoundAABB.mMax.mY = BF_MAX(bvNode.mBoundAABB.mMax.mY, vtx.mY); + bvNode.mBoundAABB.mMax.mZ = BF_MAX(bvNode.mBoundAABB.mMax.mZ, vtx.mZ); + } + } + } + + //mBVNodes[nodeIdx].mBoundSphere = Sphere::MiniBall(points.mVals, points.mSize); + + bool didSplit = false; + + if (workEntry.mTriWorkCount > 4) + { + int splitPlane = 0; + float splitWidth = 0; + + // Split along widest AABB dimension + for (int dimIdx = 0; dimIdx < 3; dimIdx++) + { + float minVal = 0; + float maxVal = 0; + + for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++) + { + int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx]; + for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++) + { + int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx]; + const Vector3& vtx = mBVVertices.mVals[vtxIdx]; + + float coord = ((float*)&vtx)[dimIdx]; + if ((triIdxIdx == 0) && (triVtxIdx == 0)) + { + minVal = coord; + maxVal = coord; + } + else + { + minVal = BF_MIN(minVal, coord); + maxVal = BF_MAX(maxVal, coord); + } + } + } + + float width = maxVal - minVal; + if (width > splitWidth) + { + splitPlane = dimIdx; + splitWidth = width; + } + } + + centers.SetSize(workEntry.mTriWorkCount); + for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++) + { + int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx]; + float coordAcc = 0; + for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++) + { + int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx]; + const Vector3& vtx = mBVVertices.mVals[vtxIdx]; + float coord = ((float*)&vtx)[splitPlane]; + coordAcc += coord; + } + + float coordAvg = coordAcc / 3; + centers.mVals[triIdxIdx] = coordAvg; + } + + float centerCoord = quickselect(centers.mVals, 0, centers.mSize - 1, centers.mSize / 2); + +// centers.Sort([](float lhs, float rhs) { return lhs < rhs; }); +// centerCoord = centers[centers.mSize / 2]; + + for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++) + { + bool inLeft = false; + bool inRight = false; + + int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx]; + for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++) + { + int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx]; + const Vector3& vtx = mBVVertices.mVals[vtxIdx]; + float coord = ((float*)&vtx)[splitPlane]; + + if (coord < centerCoord) + inLeft = true; + else + inRight = true; + } + + if (inLeft) + left.Add(triIdx); + if (inRight) + right.Add(triIdx); + } + + // Don't split if the split didn't significantly separate the triangles + bool doSplit = + (left.mSize <= workEntry.mTriWorkCount * 0.85f) && + (right.mSize <= workEntry.mTriWorkCount * 0.85f); + + if (doSplit) + { + mBVNodes[nodeIdx].mKind = ModelBVNode::Kind_Branch; + mBVNodes[nodeIdx].mLeft = -1; + mBVNodes[nodeIdx].mRight = -1; + + _WorkEntry childWorkEntry; + + childWorkEntry.mParentNodeIdx = nodeIdx; + childWorkEntry.mTriWorkIdx = triWorkList.mSize; + childWorkEntry.mTriWorkCount = right.mSize; + workList.Add(childWorkEntry); + triWorkList.Insert(triWorkList.mSize, right.mVals, right.mSize); + + childWorkEntry.mParentNodeIdx = nodeIdx; + childWorkEntry.mTriWorkIdx = triWorkList.mSize; + childWorkEntry.mTriWorkCount = left.mSize; + workList.Add(childWorkEntry); + triWorkList.Insert(triWorkList.mSize, left.mVals, left.mSize); + + continue; + } + } + + // Did not split + int triStartIdx = mBVTris.mSize; + //mBVTris.Reserve(mBVTris.mSize + workEntry.mTriWorkCount); + for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++) + mBVTris.Add(triWorkList[workEntry.mTriWorkIdx + triIdxIdx]); + + auto& bvNode = mBVNodes[nodeIdx]; + bvNode.mKind = ModelBVNode::Kind_Leaf; + bvNode.mTriStartIdx = triStartIdx; + bvNode.mTriCount = workEntry.mTriWorkCount; + } + + NOP; +} + +void ModelDef::RayIntersect(ModelBVNode* bvNode, const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance) +{ +// if (!RayIntersectsCircle(origin, vec, bvNode->mBoundSphere, NULL, NULL, NULL)) +// return false; + if (!RayIntersectsAABB(origin, vec, bvNode->mBoundAABB, NULL, NULL, NULL)) + return; + + if (bvNode->mKind == ModelBVNode::Kind_Branch) + { + bool hadIntersect = false; + for (int branchIdx = 0; branchIdx < 2; branchIdx++) + RayIntersect(&mBVNodes[(branchIdx == 0) ? bvNode->mLeft : bvNode->mRight], worldMtx, origin, vec, outIntersect, outDistance); + return; + } + + for (int triIdxIdx = 0; triIdxIdx < bvNode->mTriCount; triIdxIdx++) + { + int triIdx = mBVTris[bvNode->mTriStartIdx + triIdxIdx]; + + Vector3 curIntersect; + float curDistance; + if (RayIntersectsTriangle(origin, vec, + mBVVertices[mBVIndices[triIdx*3+0]], mBVVertices[mBVIndices[triIdx*3+1]], mBVVertices[mBVIndices[triIdx*3+2]], + &curIntersect, &curDistance)) + { + if (curDistance < outDistance) + { + outIntersect = curIntersect; + outDistance = curDistance; + } + } + } +} + +bool ModelDef::RayIntersect(const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance) +{ + if ((mFlags & Flags_HasBounds) == 0) + CalcBounds(); + + if (!RayIntersectsAABB(origin, vec, mBounds, NULL, NULL, NULL)) + return false; + + if (mBVNodes.IsEmpty()) + GenerateCollisionData(); + + const float maxDist = 3.40282e+038f; + outDistance = maxDist; + RayIntersect(&mBVNodes[0], worldMtx, origin, vec, outIntersect, outDistance); + return outDistance != maxDist; +} + +ModelMaterialDef* ModelMaterialDef::CreateOrGet(const StringImpl& prefix, const StringImpl& path) +{ + StringT<128> key = prefix; + key += ":"; + key += path; + ModelMaterialDef** modelMaterialDefPtr = NULL; + if (sModelManager.mMaterialMap.TryAdd(key, NULL, &modelMaterialDefPtr)) + *modelMaterialDefPtr = new ModelMaterialDef(); + return *modelMaterialDefPtr; } +/// +class ModelReader +{ +public: + ModelDef* mModelDef; + DataStream* mFS; + +public: + ModelReader(ModelDef* modelDef) + { + mFS = NULL; + mModelDef = modelDef; + } + + ~ModelReader() + { + delete mFS; + } + + bool ReadFile() + { + int sig = mFS->ReadInt32(); + if (sig != 0xBEEF0001) + return false; + + int flags = mFS->ReadInt32(); + Array texNames; + int texNameSize = mFS->ReadInt32(); + for (int i = 0; i < texNameSize; i++) + { + String path = mFS->ReadAscii32SizedString(); + texNames.Add(path); + } + + int idxCount = mFS->ReadInt32(); + + mModelDef->mMeshes.Add(ModelMesh()); + auto& modelMesh = mModelDef->mMeshes[0]; + modelMesh.mPrimitives.Add(ModelPrimitives()); + auto& modelPrimitives = modelMesh.mPrimitives[0]; + + modelPrimitives.mFlags = (ModelPrimitives::Flags)flags; + modelPrimitives.mTexPaths = texNames; + + modelPrimitives.mIndices.Resize(idxCount); + mFS->Read(modelPrimitives.mIndices.mVals, idxCount * 2); + + int vtxCount = mFS->ReadInt32(); + modelPrimitives.mVertices.Resize(vtxCount); + for (int i = 0; i < vtxCount; i++) + { + if ((flags & ModelPrimitives::Flags_Vertex_Position) != 0) + mFS->ReadT(modelPrimitives.mVertices[i].mPosition); + if ((flags & ModelPrimitives::Flags_Vertex_Tex0) != 0) + mFS->ReadT(modelPrimitives.mVertices[i].mTexCoords); + if ((flags & ModelPrimitives::Flags_Vertex_Tex1) != 0) + mFS->ReadT(modelPrimitives.mVertices[i].mBumpTexCoords); + } + + return true; + } + + bool ReadFile(const StringImpl& fileName, const StringImpl& baseDir) + { + if (fileName.StartsWith('@')) + { + int colon = (int)fileName.IndexOf(':'); + String addrStr = fileName.Substring(1, colon - 1); + String lenStr = fileName.Substring(colon + 1); + void* addr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16); + int len = (int)strtol(lenStr.c_str(), NULL, 10); + MemStream* memStream = new MemStream(addr, len, false); + mFS = memStream; + } + else + { + FileStream* fs = new FileStream(); + mFS = fs; + if (!fs->Open(fileName, "rb")) + return false; + } + + return ReadFile(); + } +}; + +BF_EXPORT void* BF_CALLTYPE Res_OpenModel(const char* fileName, const char* baseDir, void* vertexDefinition) +{ + ModelDef* modelDef = new ModelDef(); + modelDef->mLoadDir = baseDir; + ModelReader reader(modelDef); + if (!reader.ReadFile(fileName, baseDir)) + { + delete modelDef; + return NULL; + } + + return modelDef; +} + +BF_EXPORT StringView BF_CALLTYPE Res_SerializeModel(ModelDef* modelDef) +{ + DynMemStream ms; + ms.Write((int)0xBEEF0001); + + for (auto& mesh : modelDef->mMeshes) + { + for (auto& prims : mesh.mPrimitives) + { + ms.Write((int)prims.mFlags); + ms.Write(prims.mTexPaths.mSize); + for (auto& path : prims.mTexPaths) + ms.Write(path); + + ms.Write((int)prims.mIndices.mSize); + ms.Write(prims.mIndices.mVals, prims.mIndices.mSize * 2); + + ms.Write((int)prims.mVertices.mSize); + + for (int i = 0; i < prims.mVertices.mSize; i++) + { + auto& vtx = prims.mVertices[i]; + if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Position) != 0) + ms.WriteT(vtx.mPosition); + if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Tex0) != 0) + ms.WriteT(vtx.mTexCoords); + if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Tex1) != 0) + ms.WriteT(vtx.mBumpTexCoords); + if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Color) != 0) + ms.WriteT(vtx.mColor); + if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Normal) != 0) + ms.WriteT(vtx.mNormal); + if ((prims.mFlags & ModelPrimitives::Flags_Vertex_Tangent) != 0) + ms.WriteT(vtx.mTangent); + } + } + } + + String& outString = *gModelDef_TLStrReturn.Get(); + outString.Clear(); + outString.Append((char*)ms.GetPtr(), ms.GetSize()); + return outString; +} diff --git a/BeefySysLib/gfx/ModelDef.h b/BeefySysLib/gfx/ModelDef.h index e5184af4..8e040142 100644 --- a/BeefySysLib/gfx/ModelDef.h +++ b/BeefySysLib/gfx/ModelDef.h @@ -3,6 +3,10 @@ #include "Common.h" #include "util/Quaternion.h" #include "util/Vector.h" +#include "util/Array.h" +#include "gfx/Texture.h" +#include "util/Sphere.h" +#include "util/MathUtils.h" #include NS_BF_BEGIN; @@ -18,14 +22,14 @@ public: class ModelAnimationFrame { public: - std::vector mJointTranslations; + Array mJointTranslations; }; class ModelAnimation { public: String mName; - std::vector mFrames; + Array mFrames; public: void GetJointTranslation(int jointIdx, float frameNum, ModelJointTranslation* outJointTranslation); @@ -55,24 +59,190 @@ public: Matrix4 mPoseInvMatrix; }; +class ModelMetalicRoughness +{ +public: + Vector3 mBaseColorFactor; + float mMetallicFactor; + float mRoughnessFactor; + +public: + ModelMetalicRoughness() + { + mMetallicFactor = 0; + mRoughnessFactor = 0; + } +}; + +class ModelMaterialDef +{ +public: + class TextureParameterValue + { + public: + String mName; + String mTexturePath; + + public: + TextureParameterValue() + { + + } + + ~TextureParameterValue() + { + + } + }; + +public: + String mName; + int mRefCount; + bool mInitialized; + OwnedArray mTextureParameterValues; + +public: + ModelMaterialDef() + { + mRefCount = 0; + mInitialized = false; + } + + static ModelMaterialDef* CreateOrGet(const StringImpl& prefix, const StringImpl& path); +}; + +class ModelMaterialInstance +{ +public: + ModelMaterialDef* mDef; + String mName; + ModelMetalicRoughness mModelMetalicRoughness; +}; + +class ModelPrimitives +{ +public: + enum Flags + { + Flags_None = 0, + Flags_Vertex_Position = 1, + Flags_Vertex_Tex0 = 2, + Flags_Vertex_Tex1 = 4, + Flags_Vertex_Tex2 = 8, + Flags_Vertex_Color = 0x10, + Flags_Vertex_Normal = 0x20, + Flags_Vertex_Tangent = 0x40, + }; + +public: + Array mVertices; + Array mIndices; + ModelMaterialInstance* mMaterial; + Array mTexPaths; + Flags mFlags; + +public: + ModelPrimitives() + { + mMaterial = NULL; + mFlags = Flags_None; + } +}; + class ModelMesh { +public: + String mName; + Array mPrimitives; +}; + +class ModelNode +{ public: String mName; - std::vector mVertices; - std::vector mIndices; - String mTexFileName; - String mBumpFileName; + Vector3 mTranslation; + Vector4 mRotation; + ModelMesh* mMesh; + Array mChildren; +}; + +class ModelBVNode +{ +public: + enum Kind + { + Kind_None, + Kind_Branch, + Kind_Leaf + }; + +public: + Sphere mBoundSphere; + AABB mBoundAABB; + + union + { + struct + { + int mLeft; + int mRight; + }; + + struct + { + int mTriStartIdx; + int mTriCount; + }; + }; + + Kind mKind; + +public: + ModelBVNode() + { + mKind = Kind_None; + } }; class ModelDef { +public: + enum Flags + { + Flags_None, + Flags_HasBounds, + Flags_HasBVH, + }; + public: String mLoadDir; - float mFrameRate; - std::vector mMeshes; - std::vector mJoints; - std::vector mAnims; + float mFrameRate; + Array mMeshes; + Array mJoints; + Array mAnims; + Array mNodes; + Array mMaterials; + + Flags mFlags; + AABB mBounds; + Array mBVNodes; + Array mBVIndices; + Array mBVVertices; + Array mBVTris; + +protected: + void CalcBounds(); + void RayIntersect(ModelBVNode* bvNode, const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance); + +public: + ModelDef(); + ~ModelDef(); + + void Compact(); + void GetBounds(Vector3& min, Vector3& max); + + void GenerateCollisionData(); + bool RayIntersect(const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance); }; NS_BF_END; diff --git a/BeefySysLib/gfx/ModelInstance.cpp b/BeefySysLib/gfx/ModelInstance.cpp index 48923231..2e99d71b 100644 --- a/BeefySysLib/gfx/ModelInstance.cpp +++ b/BeefySysLib/gfx/ModelInstance.cpp @@ -6,8 +6,8 @@ ModelInstance::ModelInstance(ModelDef* modelDef) { mNext = NULL; mModelDef = modelDef; - mJointTranslations.resize(mModelDef->mJoints.size()); - mMeshesVisible.insert(mMeshesVisible.begin(), mModelDef->mMeshes.size(), true); + mJointTranslations.Resize(mModelDef->mJoints.size()); + mMeshesVisible.Insert(0, mModelDef->mMeshes.size(), true); } void Beefy::ModelInstance::SetJointPosition(int jointIdx, const ModelJointTranslation& jointTranslation) diff --git a/BeefySysLib/gfx/ModelInstance.h b/BeefySysLib/gfx/ModelInstance.h index 871bb1a2..a838f2d8 100644 --- a/BeefySysLib/gfx/ModelInstance.h +++ b/BeefySysLib/gfx/ModelInstance.h @@ -11,8 +11,8 @@ class ModelInstance : public RenderCmd { public: ModelDef* mModelDef; - std::vector mJointTranslations; - std::vector mMeshesVisible; + Array mJointTranslations; + Array mMeshesVisible; public: ModelInstance(ModelDef* modelDef); diff --git a/BeefySysLib/gfx/RenderDevice.cpp b/BeefySysLib/gfx/RenderDevice.cpp index cd21c94d..61af4300 100644 --- a/BeefySysLib/gfx/RenderDevice.cpp +++ b/BeefySysLib/gfx/RenderDevice.cpp @@ -19,8 +19,11 @@ RenderState::RenderState() mWriteDepthBuffer = false; mCullMode = CullMode_None; mDepthFunc = DepthFunc_Always; + mTopology = Topology3D_TriangleList; mShader = NULL; mClipped = false; + mTexWrap = false; + mWireframe = false; } RenderTarget::RenderTarget() @@ -103,8 +106,8 @@ Texture* RenderDevice::LoadTexture(const StringImpl& fileName, int flags) int dotPos = (int)fileName.LastIndexOf('.'); String ext; if (dotPos != -1) - ext = fileName.Substring(dotPos); - + ext = fileName.Substring(dotPos); + ImageData* imageData = NULL; bool handled = false; bool failed = false; diff --git a/BeefySysLib/gfx/RenderDevice.h b/BeefySysLib/gfx/RenderDevice.h index 7c0b1f0c..1333aaef 100644 --- a/BeefySysLib/gfx/RenderDevice.h +++ b/BeefySysLib/gfx/RenderDevice.h @@ -143,6 +143,12 @@ enum CullMode : int8 CullMode_Back }; +enum Topology3D : int8 +{ + Topology3D_TriangleList, + Topology3D_LineLine +}; + enum TextureFlag : int8 { TextureFlag_Additive = 1, @@ -182,18 +188,24 @@ public: bool mWriteDepthBuffer; DepthFunc mDepthFunc; bool mClipped; + bool mTexWrap; + bool mWireframe; Rect mClipRect; CullMode mCullMode; + Topology3D mTopology; public: RenderState(); virtual ~RenderState() {} virtual void SetShader(Shader* shader) { mShader = shader; } + virtual void SetTexWrap(bool wrap) { mTexWrap = wrap; } + virtual void SetWireframe(bool wireframe) { mWireframe = wireframe; } virtual void SetClipped(bool clipped) { mClipped = clipped; } virtual void SetClipRect(const Rect& rect) { mClipRect = rect; } virtual void SetWriteDepthBuffer(bool writeDepthBuffer) { mWriteDepthBuffer = writeDepthBuffer; } virtual void SetDepthFunc(DepthFunc depthFunc) { mDepthFunc = depthFunc; } + virtual void SetTopology(Topology3D topology) { mTopology = topology; } }; class PoolData @@ -239,6 +251,12 @@ public: } }; +enum ModelCreateFlags +{ + ModelCreateFlags_None = 0, + ModelCreateFlags_NoSetRenderState = 1 +}; + class RenderDevice { public: @@ -270,7 +288,7 @@ public: virtual void RemoveRenderWindow(RenderWindow* renderWindow); virtual RenderState* CreateRenderState(RenderState* srcRenderState); - virtual ModelInstance* CreateModelInstance(ModelDef* modelDef) { return NULL; } + virtual ModelInstance* CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags) { return NULL; } virtual VertexDefinition* CreateVertexDefinition(VertexDefData* elementData, int numElements); virtual void FrameStart() = 0; diff --git a/BeefySysLib/gfx/glTF.cpp b/BeefySysLib/gfx/glTF.cpp new file mode 100644 index 00000000..63886574 --- /dev/null +++ b/BeefySysLib/gfx/glTF.cpp @@ -0,0 +1,875 @@ +#include "glTF.h" +#include "gfx/ModelInstance.h" +#include "util/Json.h" +#include "gfx/RenderDevice.h" +#include "BFApp.h" + +USING_NS_BF; + +static bool IsWhitespace(char c) +{ + return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); +} + +class GLTFPropsParser +{ +public: + enum NodeKind + { + NodeKind_None, + NodeKind_End, + NodeKind_LBrace, + NodeKind_RBrace, + NodeKind_LBracket, + NodeKind_RBracket, + NodeKind_Equals, + NodeKind_Index, + NodeKind_Integer, + NodeKind_Float, + NodeKind_String + }; + + struct Node + { + public: + NodeKind mKind; + int mStart; + int mEnd; + union + { + int mValueInt; + float mValueFloat; + }; + + public: + Node() + { + mKind = NodeKind_None; + mStart = -1; + mEnd = -1; + mValueInt = 0; + } + }; + +public: + char* mStart; + char* mPtr; + char* mEnd; + Node mNext; + +public: + GLTFPropsParser(const StringImpl& str) + { + mStart = str.mPtr; + mPtr = mStart; + mEnd = mStart + str.mLength; + } + +// double ParseLiteralDouble() +// { +// char buf[256]; +// int len = BF_MAX(mTokenEnd - mTokenStart, 255); +// +// memcpy(buf, &mSrc[mTokenStart], len); +// char c = buf[len - 1]; +// if ((c == 'd') || (c == 'D') || (c == 'f') || (c == 'F')) +// buf[len - 1] = '\0'; +// else +// buf[len] = '\0'; +// +// return strtod(buf, NULL); +// } + + Node PeekNext() + { + if (mNext.mKind != NodeKind_None) + return mNext; + + while (true) + { + if (mPtr >= mEnd) + { + mNext.mKind = NodeKind_End; + return mNext; + } + + char* start = mPtr; + char c = *(mPtr++); + + if (c == '{') + mNext.mKind = NodeKind_LBrace; + else if (c == '}') + mNext.mKind = NodeKind_RBrace; + else if (c == '[') + mNext.mKind = NodeKind_LBracket; + else if (c == ']') + mNext.mKind = NodeKind_RBracket; + else if (c == '=') + mNext.mKind = NodeKind_Equals; + + if (mNext.mKind != NodeKind_None) + { + mNext.mStart = (int)(mPtr - mStart - 1); + mNext.mEnd = (int)(mPtr - mStart); + return mNext; + } + + if ((c >= '0') && (c <= '9')) + { + bool hadDot = false; + while (mPtr < mEnd) + { + char c = *mPtr; + if (c == '.') + { + mPtr++; + hadDot = true; + } + else if ((c >= '0') && (c <= '9')) + { + mPtr++; + } + else + break; + } + + mNext.mStart = (int)(start - mStart); + mNext.mEnd = (int)(mPtr - mStart); + + char buf[256]; + int len = BF_MIN((int)(mPtr - start), 255); + + memcpy(buf, start, len); + char c = buf[len - 1]; + if ((c == 'd') || (c == 'D') || (c == 'f') || (c == 'F')) + buf[len - 1] = '\0'; + else + buf[len] = '\0'; + + if (hadDot) + { + mNext.mKind = NodeKind_Float; + mNext.mValueFloat = (float)strtod(buf, NULL); + } + else + { + mNext.mKind = NodeKind_Integer; + mNext.mValueInt = atoi(buf); + } + return mNext; + } + + if (!IsWhitespace(c)) + { + char* lastCPtr = start; + while (mPtr < mEnd) + { + char c = *mPtr; + if ((c == '}') || (c == '=') || (c == '[') || (c == '\r') || (c == '\n')) + break; + if (c != ' ') + lastCPtr = mPtr; + mPtr++; + } + mPtr = lastCPtr + 1; + + mNext.mStart = (int)(start - mStart); + mNext.mEnd = (int)(mPtr - mStart); + mNext.mKind = NodeKind_String; + return mNext; + } + } + } + + Node GetNext() + { + auto node = PeekNext(); + mNext = Node(); + return node; + } + + StringView GetStringView(const Node& node) + { + return StringView(mStart + node.mStart, node.mEnd - node.mStart); + } + + StringView GetStringView(const Node& node, StringView& prefix) + { + auto stringView = StringView(mStart + node.mStart, node.mEnd - node.mStart); + if (!stringView.EndsWith('\'')) + { + prefix = ""; + return stringView; + } + + int strStartIdx = (int)stringView.IndexOf('\''); + prefix = StringView(stringView, 0, strStartIdx); + return StringView(stringView, strStartIdx + 1, (int)stringView.mLength - strStartIdx - 2); + } + + bool GetNextStringView(StringView& prefix, StringView& value) + { + auto node = GetNext(); + if (node.mKind != NodeKind_String) + return false; + auto stringView = StringView(mStart + node.mStart, node.mEnd - node.mStart); + if (!stringView.EndsWith('\'')) + { + prefix = ""; + value = stringView; + return true; + } + int strStartIdx = (int)stringView.IndexOf('\''); + prefix = StringView(stringView, 0, strStartIdx); + value = StringView(stringView, strStartIdx + 1, (int)stringView.mLength - strStartIdx - 2); + return true; + } +}; + +enum ComponentType +{ + Int8 = 5120, + UInt8 = 5121, + Int16 = 5122, + UInt16 = 5123, + UInt32 = 5125, + Float = 5126, +}; + +BF_EXPORT void* BF_CALLTYPE Res_OpenGLTF(const char* fileName, const char* baseDir, void* vertexDefinition) +{ + ModelDef* modelDef = new ModelDef(); + GLTFReader reader(modelDef); + if (!reader.ReadFile(fileName, baseDir)) + { + delete modelDef; + return NULL; + } + + return modelDef; +} + +GLTFReader::GLTFReader(ModelDef* modelDef) +{ + mModelDef = modelDef; +} + +GLTFReader::~GLTFReader() +{ + +} + +struct DataSpan +{ + uint8* mPtr; + int mSize; +}; + +struct DataAccessor +{ + uint8* mPtr; + int mSize; + int mCount; + ComponentType mComponentType; +}; + +template +static void ReadBuffer(DataAccessor& dataAccessor, T* outPtr, int outStride) +{ + for (int i = 0; i < dataAccessor.mCount; i++) + *(T*)((uint8*)outPtr + i * outStride) = ((T*)dataAccessor.mPtr)[i]; +} + +template +static void ReadBuffer(DataAccessor& dataAccessor, T* outPtr, int outStride, int inStride) +{ + for (int i = 0; i < dataAccessor.mCount; i++) + *(T*)((uint8*)outPtr + i * outStride) = *(T*)(dataAccessor.mPtr + i * inStride); +} + +static void TrySkipValue(GLTFPropsParser& propsParser) +{ + auto nextNode = propsParser.PeekNext(); + + if (nextNode.mKind == GLTFPropsParser::NodeKind_LBracket) + { + propsParser.GetNext(); + propsParser.GetNext(); + propsParser.GetNext(); + nextNode = propsParser.PeekNext(); + } + + if (nextNode.mKind == GLTFPropsParser::NodeKind_Equals) + { + propsParser.GetNext(); + + int depth = 0; + do + { + auto node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_End) + return; + if (node.mKind == GLTFPropsParser::NodeKind_LBrace) + depth++; + else if (node.mKind == GLTFPropsParser::NodeKind_RBrace) + depth--; + if (node.mKind == GLTFPropsParser::NodeKind_LBracket) + depth++; + if (node.mKind == GLTFPropsParser::NodeKind_LBracket) + depth--; + } while (depth > 0); + } +} + +static bool ExpectIndex(GLTFPropsParser& propsParser, int& idx) +{ + auto node = propsParser.GetNext(); + if (node.mKind != GLTFPropsParser::NodeKind_LBracket) + return false; + node = propsParser.GetNext(); + if (node.mKind != GLTFPropsParser::NodeKind_Integer) + return false; + idx = node.mValueInt; + node = propsParser.GetNext(); + if (node.mKind != GLTFPropsParser::NodeKind_RBracket) + return false; + return true; +}; + +static bool ExpectOpen(GLTFPropsParser& propsParser) +{ + if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_LBrace) + return false; + return true; +}; + +static bool ExpectClose(GLTFPropsParser& propsParser) +{ + if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_RBrace) + return false; + return true; +}; + +static bool ExpectEquals(GLTFPropsParser& propsParser) +{ + if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_Equals) + return false; + return true; +}; + +bool GLTFReader::ParseMaterialDef(ModelMaterialDef* materialDef, const StringImpl& matText) +{ + GLTFPropsParser propsParser(matText); + + while (true) + { + auto node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_End) + break; + + if (node.mKind == GLTFPropsParser::NodeKind_String) + { + auto key = propsParser.GetStringView(node); + if (key == "Parent") + { + if (propsParser.GetNext().mKind != GLTFPropsParser::NodeKind_Equals) + return false; + + auto valueNode = propsParser.GetNext(); + if (valueNode.mKind != GLTFPropsParser::NodeKind_String) + return false; + + StringView prefix; + StringView str = propsParser.GetStringView(valueNode, prefix); + auto parentMaterialDef = LoadMaterial(str); + } + else if (key == "TextureParameterValues") + { + int count = 0; + if (!ExpectIndex(propsParser, count)) + return false; + if (!ExpectEquals(propsParser)) + return false; + if (!ExpectOpen(propsParser)) + return false; + + while (true) + { + node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_RBrace) + break; + if (node.mKind != GLTFPropsParser::NodeKind_String) + return false; + + StringView prefix; + StringView str = propsParser.GetStringView(node, prefix); + if (str == "TextureParameterValues") + { + auto textureParamValue = materialDef->mTextureParameterValues.Alloc(); + + int idx = 0; + if (!ExpectIndex(propsParser, idx)) + return false; + if (!ExpectEquals(propsParser)) + return false; + if (!ExpectOpen(propsParser)) + return false; + + while (true) + { + node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_RBrace) + break; + if (node.mKind != GLTFPropsParser::NodeKind_String) + return false; + + str = propsParser.GetStringView(node, prefix); + if (str == "ParameterInfo") + { + if (!ExpectEquals(propsParser)) + return false; + if (!ExpectOpen(propsParser)) + return false; + if (!propsParser.GetNextStringView(prefix, str)) + return false; + if (!ExpectEquals(propsParser)) + return false; + if (!propsParser.GetNextStringView(prefix, str)) + return false; + textureParamValue->mName = str; + + if (!ExpectClose(propsParser)) + return false; + } + else if (str == "ParameterValue") + { + if (!ExpectEquals(propsParser)) + return false; + if (!propsParser.GetNextStringView(prefix, str)) + return false; + + String path = mRootDir; + path += str; + int dotPos = (int)path.IndexOf('.'); + if (dotPos != -1) + path.RemoveToEnd(dotPos); + path += ".tga"; + + textureParamValue->mTexturePath = path; + +// Texture* texture = gBFApp->mRenderDevice->LoadTexture(path, 0); +// textureParamValue->mTexture = texture; + } + else + TrySkipValue(propsParser); + } + } + else + { + TrySkipValue(propsParser); + } + } + } + else + { + TrySkipValue(propsParser); + } + } + } + + return true; +} + +ModelMaterialDef* GLTFReader::LoadMaterial(const StringImpl& relPath) +{ + String propsPath; + if (relPath.StartsWith('/')) + { + propsPath = mRootDir + relPath; + int dotPos = (int)propsPath.LastIndexOf('.'); + if (dotPos > 0) + propsPath.RemoveToEnd(dotPos); + propsPath += ".props.txt"; + } + else if (mBasePathName.Contains("staticmesh")) + propsPath = GetFileDir(mBasePathName) + "/" + relPath + ".props.txt"; + else + propsPath = GetFileDir(mBasePathName) + "/materials/" + relPath + ".props.txt"; + + ModelMaterialDef* materialDef = ModelMaterialDef::CreateOrGet("GLTF", propsPath); + + if (materialDef->mInitialized) + return materialDef; + + materialDef->mInitialized = true; + + String propText; + if (LoadTextData(propsPath, propText)) + { + if (!ParseMaterialDef(materialDef, propText)) + { + // Had error + } + + } + return materialDef; +} + +bool GLTFReader::LoadModelProps(const StringImpl& propsPath) +{ + String propText; + if (!LoadTextData(propsPath, propText)) + return false; + + GLTFPropsParser propsParser(propText); + while (true) + { + auto node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_End) + break; + + if (node.mKind == GLTFPropsParser::NodeKind_String) + { + auto key = propsParser.GetStringView(node); + if (key == "StaticMaterials") + { + int count = 0; + if (!ExpectIndex(propsParser, count)) + return false; + if (!ExpectEquals(propsParser)) + return false; + if (!ExpectOpen(propsParser)) + return false; + + while (true) + { + node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_RBrace) + break; + if (node.mKind != GLTFPropsParser::NodeKind_String) + return false; + + StringView prefix; + StringView str = propsParser.GetStringView(node, prefix); + if (str == "StaticMaterials") + { + StaticMaterial staticMaterial; + + int idx = 0; + if (!ExpectIndex(propsParser, idx)) + return false; + if (!ExpectEquals(propsParser)) + return false; + if (!ExpectOpen(propsParser)) + return false; + + while (true) + { + node = propsParser.GetNext(); + if (node.mKind == GLTFPropsParser::NodeKind_RBrace) + break; + if (node.mKind != GLTFPropsParser::NodeKind_String) + return false; + + str = propsParser.GetStringView(node, prefix); + if (str == "MaterialSlotName") + { + if (!ExpectEquals(propsParser)) + return false; + if (!propsParser.GetNextStringView(prefix, str)) + return false; + staticMaterial.mMaterialSlotName = str; + } + else if (str == "MaterialInterface") + { + if (!ExpectEquals(propsParser)) + return false; + if (!propsParser.GetNextStringView(prefix, str)) + return false; + staticMaterial.mMaterialDef = LoadMaterial(str); + } + else + TrySkipValue(propsParser); + } + + mStaticMaterials.Add(staticMaterial); + } + else + { + TrySkipValue(propsParser); + } + } + } + else + { + TrySkipValue(propsParser); + } + } + } + + return true; +} + +bool GLTFReader::ReadFile(const StringImpl& filePath, const StringImpl& rootDir) +{ + String basePathName; + int dotPos = (int)filePath.LastIndexOf('.'); + if (dotPos > 0) + basePathName = filePath.Substring(0, dotPos); + else + basePathName = basePathName; + mBasePathName = basePathName; + mRootDir = rootDir; + + String jsonPath = basePathName + ".gltf"; + + char* textData = LoadTextData(jsonPath, NULL); + if (textData == NULL) + return false; + defer({ delete textData; }); + + Json* jRoot = Json::Parse(textData); + if (jRoot == NULL) + return false; + defer({ delete jRoot; }); + + LoadModelProps(basePathName + ".props.txt"); + + Array> buffers; + Array bufferViews; + Array dataAccessors; + + if (auto jBuffers = jRoot->GetObjectItem("buffers")) + { + for (auto jBuffer = jBuffers->mChild; jBuffer != NULL; jBuffer = jBuffer->mNext) + { + Array data; + if (auto jName = jBuffer->GetObjectItem("uri")) + { + if (jName->mValueString != NULL) + { + String dataPath = GetFileDir(basePathName) + "/" + jName->mValueString; + + int size = 0; + uint8* rawData = LoadBinaryData(dataPath, &size); + if (rawData != NULL) + data.Insert(0, rawData, size); + } + } + + buffers.Add(data); + } + } + + if (auto jBufferViews = jRoot->GetObjectItem("bufferViews")) + { + for (auto jBufferView = jBufferViews->mChild; jBufferView != NULL; jBufferView = jBufferView->mNext) + { + int bufferIdx = 0; + int byteOffset = 0; + int byteLength = 0; + + if (auto jBufferIdx = jBufferView->GetObjectItem("buffer")) + bufferIdx = jBufferIdx->mValueInt; + if (auto jByteOffset = jBufferView->GetObjectItem("byteOffset")) + byteOffset = jByteOffset->mValueInt; + if (auto jByteLength = jBufferView->GetObjectItem("byteLength")) + byteLength = jByteLength->mValueInt; + bufferViews.Add(DataSpan{ buffers[bufferIdx].mVals + byteOffset, byteLength }); + } + } + + if (auto jAccessors = jRoot->GetObjectItem("accessors")) + { + for (auto jAccessor = jAccessors->mChild; jAccessor != NULL; jAccessor = jAccessor->mNext) + { + DataAccessor dataAccessor = { 0 }; + if (auto jBufferIdx = jAccessor->GetObjectItem("bufferView")) + { + DataSpan& dataSpan = bufferViews[jBufferIdx->mValueInt]; + dataAccessor.mPtr = dataSpan.mPtr; + dataAccessor.mSize = dataSpan.mSize; + } + if (auto jCount = jAccessor->GetObjectItem("count")) + dataAccessor.mCount = jCount->mValueInt; + if (auto jCount = jAccessor->GetObjectItem("componentType")) + dataAccessor.mComponentType = (ComponentType)jCount->mValueInt; + + dataAccessors.Add(dataAccessor); + } + } + + auto _GetFloat3 = [&](Json* json, Vector3& vec) + { + int i = 0; + for (auto jItem = json->mChild; jItem != NULL; jItem = jItem->mNext) + { + if (i == 0) + vec.mX = (float)jItem->mValueDouble; + if (i == 1) + vec.mY = (float)jItem->mValueDouble; + if (i == 2) + vec.mZ = (float)jItem->mValueDouble; + i++; + } + }; + + auto _GetFloat4 = [&](Json* json, Vector4& vec) + { + int i = 0; + for (auto jItem = json->mChild; jItem != NULL; jItem = jItem->mNext) + { + if (i == 0) + vec.mX = (float)jItem->mValueDouble; + if (i == 1) + vec.mY = (float)jItem->mValueDouble; + if (i == 2) + vec.mZ = (float)jItem->mValueDouble; + if (i == 3) + vec.mW = (float)jItem->mValueDouble; + i++; + } + }; + + if (auto jMaterials = jRoot->GetObjectItem("materials")) + { + int materialIdx = 0; + for (auto jMaterial = jMaterials->mChild; jMaterial != NULL; jMaterial = jMaterial->mNext) + { + ModelMaterialInstance modelMaterialInstance; + if (auto jName = jMaterial->GetObjectItem("name")) + { + if (jName->mValueString != NULL) + { + modelMaterialInstance.mName = jName->mValueString; + String matPath = jName->mValueString; + + if (materialIdx < mStaticMaterials.mSize) + matPath = mStaticMaterials[materialIdx].mMaterialSlotName; + + ModelMaterialDef* materialDef = LoadMaterial(matPath); + modelMaterialInstance.mDef = materialDef; + } + } + if (auto jPBRMetallicRoughness = jMaterial->GetObjectItem("pbrMetallicRoughness")) + { + + } + + mModelDef->mMaterials.Add(modelMaterialInstance); + materialIdx++; + } + } + + if (auto jMeshes = jRoot->GetObjectItem("meshes")) + { + for (auto jMesh = jMeshes->mChild; jMesh != NULL; jMesh = jMesh->mNext) + { + ModelMesh modelMesh; + + if (auto jName = jMesh->GetObjectItem("name")) + { + if (jName->mValueString != NULL) + modelMesh.mName = jName->mValueString; + } + + if (auto jPrimitives = jMesh->GetObjectItem("primitives")) + { + modelMesh.mPrimitives.Resize(jPrimitives->GetArraySize()); + + int primCount = 0; + for (auto jPrimitive = jPrimitives->mChild; jPrimitive != NULL; jPrimitive = jPrimitive->mNext) + { + ModelPrimitives& modelPrimitives = modelMesh.mPrimitives[primCount]; + + if (auto jIndices = jPrimitive->GetObjectItem("indices")) + { + auto& dataAccessor = dataAccessors[jIndices->mValueInt]; + modelPrimitives.mIndices.ResizeRaw(dataAccessor.mCount); + for (int i = 0; i < dataAccessor.mCount; i++) + modelPrimitives.mIndices[i] = *(uint16*)(dataAccessor.mPtr + i * 2); + } + + if (auto jIndices = jPrimitive->GetObjectItem("material")) + modelPrimitives.mMaterial = &mModelDef->mMaterials[jIndices->mValueInt]; + + if (auto jAttributes = jPrimitive->GetObjectItem("attributes")) + { + if (auto jPosition = jAttributes->GetObjectItem("POSITION")) + { + auto& dataAccessor = dataAccessors[jPosition->mValueInt]; + modelPrimitives.mVertices.Resize(dataAccessor.mCount); + ReadBuffer(dataAccessor, &modelPrimitives.mVertices[0].mPosition, sizeof(ModelVertex)); + } + + if (auto jNormal = jAttributes->GetObjectItem("NORMAL")) + ReadBuffer(dataAccessors[jNormal->mValueInt], &modelPrimitives.mVertices[0].mNormal, sizeof(ModelVertex)); + if (auto jTangent = jAttributes->GetObjectItem("TANGENT")) + ReadBuffer(dataAccessors[jTangent->mValueInt], &modelPrimitives.mVertices[0].mTangent, sizeof(ModelVertex), sizeof(Vector4)); + if (auto jColor = jAttributes->GetObjectItem("COLOR_0")) + ReadBuffer(dataAccessors[jColor->mValueInt], &modelPrimitives.mVertices[0].mColor, sizeof(ModelVertex)); + if (auto jTexCoords = jAttributes->GetObjectItem("TEXCOORD_0")) + { + ReadBuffer(dataAccessors[jTexCoords->mValueInt], &modelPrimitives.mVertices[0].mTexCoords, sizeof(ModelVertex)); + for (auto& vertex : modelPrimitives.mVertices) + { + vertex.mTexCoords.mV = 1.0f - vertex.mTexCoords.mV; + } + } + if (auto jTexCoords = jAttributes->GetObjectItem("TEXCOORD_1")) + { + ReadBuffer(dataAccessors[jTexCoords->mValueInt], &modelPrimitives.mVertices[0].mTexCoords, sizeof(ModelVertex)); + for (auto& vertex : modelPrimitives.mVertices) + { + //vertex.mTexCoords.mU = 1.0f - vertex.mTexCoords.mU; + vertex.mTexCoords.mV = 1.0f - vertex.mTexCoords.mV; + } + } + else + { + for (auto& vertex : modelPrimitives.mVertices) + vertex.mBumpTexCoords = vertex.mTexCoords; + } + } + + primCount++; + } + } + + mModelDef->mMeshes.Add(modelMesh); + } + } + + if (auto jNodes = jRoot->GetObjectItem("nodes")) + { + mModelDef->mNodes.Reserve(jNodes->GetArraySize()); + for (auto jNode = jNodes->mChild; jNode != NULL; jNode = jNode->mNext) + { + ModelNode modelNode; + if (auto jName = jNode->GetObjectItem("name")) + { + if (jName->mValueString != NULL) + modelNode.mName = jName->mValueString; + } + if (auto jChildren = jNode->GetObjectItem("children")) + { + for (auto jChild = jChildren->mChild; jChild != NULL; jChild = jChild->mNext) + { + int childIdx = jChild->mValueInt; + modelNode.mChildren.Add(mModelDef->mNodes.mVals + childIdx); + } + } + + if (auto jTranslation = jNode->GetObjectItem("translation")) + _GetFloat3(jTranslation, modelNode.mTranslation); + if (auto jTranslation = jNode->GetObjectItem("rotation")) + _GetFloat4(jTranslation, modelNode.mRotation); + if (auto jMesh = jNode->GetObjectItem("mesh")) + modelNode.mMesh = mModelDef->mMeshes.mVals + jMesh->mValueInt; + + mModelDef->mNodes.Add(modelNode); + } + } + + return true; +} diff --git a/BeefySysLib/gfx/glTF.h b/BeefySysLib/gfx/glTF.h new file mode 100644 index 00000000..231f8d87 --- /dev/null +++ b/BeefySysLib/gfx/glTF.h @@ -0,0 +1,43 @@ +#pragma once + +#pragma once + +#include "Common.h" +#include "util/Quaternion.h" +#include "util/Vector.h" +#include "util/Array.h" +#include "FileStream.h" +#include + +NS_BF_BEGIN; + +class ModelDef; +class ModelMaterialDef; + +class GLTFReader +{ +public: + class StaticMaterial + { + public: + ModelMaterialDef* mMaterialDef; + String mMaterialSlotName; + }; + +public: + String mBasePathName; + String mRootDir; + ModelDef* mModelDef; + Array mStaticMaterials; + +public: + GLTFReader(ModelDef* modelDef); + ~GLTFReader(); + + bool ParseMaterialDef(ModelMaterialDef* materialDef, const StringImpl& matText); + ModelMaterialDef* LoadMaterial(const StringImpl& path); + bool LoadModelProps(const StringImpl& relPath); + bool ReadFile(const StringImpl& filePath, const StringImpl& rootDir); +}; + +NS_BF_END; \ No newline at end of file diff --git a/BeefySysLib/img/TGAData.cpp b/BeefySysLib/img/TGAData.cpp index 8864d20f..41c85647 100644 --- a/BeefySysLib/img/TGAData.cpp +++ b/BeefySysLib/img/TGAData.cpp @@ -49,7 +49,7 @@ bool TGAData::ReadData() bool flipped = (hdr->mImageDescriptor & 0x20) != 0; mWidth = hdr->mWidth; - mHeight = hdr->mWidth; + mHeight = hdr->mHeight; mBits = new uint32[mWidth * mHeight]; if (hdr->mDataTypeCode == 10) // RLE @@ -148,6 +148,67 @@ readSpanHeader: } } + NOP; + } + else if (aMode == 3) + { + int y = 0; + int x = 0; + + readSpanHeader3: + int spanLen = 0; + uint32 spanColor = 0; + + uint8 spanHeader = *(srcPtr++); + spanLen = (spanHeader & 0x7F) + 1; + if ((spanHeader & 0x80) != 0) + { + // Repeat color + int b = *(srcPtr++); + int g = *(srcPtr++); + int r = *(srcPtr++); + int a = 255; + + spanColor = (a << 24) | (b << 16) | (g << 8) | r; + + for (; y < mHeight; y++) + { + for (; x < mWidth; x++) + { + if (spanLen == 0) + goto readSpanHeader3; + *(destPtr++) = spanColor; + spanLen--; + } + + x = 0; + destPtr += destAdd; + } + } + else + { + for (; y < mHeight; y++) + { + for (; x < mWidth; x++) + { + if (spanLen == 0) + goto readSpanHeader3; + + int b = *(srcPtr++); + int g = *(srcPtr++); + int r = *(srcPtr++); + int a = 255; + + *(destPtr++) = (a << 24) | (b << 16) | (g << 8) | r; + + spanLen--; + } + + x = 0; + destPtr += destAdd; + } + } + NOP; } } diff --git a/BeefySysLib/platform/PlatformInterface.h b/BeefySysLib/platform/PlatformInterface.h index 6a36a1f8..79b08843 100644 --- a/BeefySysLib/platform/PlatformInterface.h +++ b/BeefySysLib/platform/PlatformInterface.h @@ -326,7 +326,8 @@ enum BfpSysDirectoryKind BfpSysDirectoryKind_AppData_LocalLow, BfpSysDirectoryKind_AppData_Roaming, BfpSysDirectoryKind_Programs, - BfpSysDirectoryKind_Programs_Common + BfpSysDirectoryKind_Programs_Common, + BfpSysDirectoryKind_Documents }; struct BfpFindFileData; @@ -343,6 +344,7 @@ enum BfpFileCreateKind BfpFileCreateKind_CreateAlways, BfpFileCreateKind_CreateIfNotExists, BfpFileCreateKind_OpenExisting, + BfpFileCreateKind_OpenAlways }; enum BfpFileCreateFlags @@ -412,6 +414,7 @@ enum BfpFileStdKind BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* name, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttr, BfpFileResult* outResult); BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult); +BFP_EXPORT intptr BFP_CALLTYPE BfpFile_GetSystemHandle(BfpFile* file); BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file); BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult); BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult); @@ -419,7 +422,7 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr BFP_EXPORT void BFP_CALLTYPE BfpFile_Flush(BfpFile* file); BFP_EXPORT int64 BFP_CALLTYPE BfpFile_GetFileSize(BfpFile* file); BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind); -BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file); +BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult); BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path); BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFile_GetAttributes(const char* path, BfpFileResult* outResult); BFP_EXPORT void BFP_CALLTYPE BfpFile_SetAttributes(const char* path, BfpFileAttributes attribs, BfpFileResult* outResult); diff --git a/BeefySysLib/platform/posix/PosixCommon.cpp b/BeefySysLib/platform/posix/PosixCommon.cpp index 0d3efb03..9e55deda 100644 --- a/BeefySysLib/platform/posix/PosixCommon.cpp +++ b/BeefySysLib/platform/posix/PosixCommon.cpp @@ -36,6 +36,7 @@ #endif #endif +#define STB_SPRINTF_DECORATE(name) BF_stbsp_##name #include "../../third_party/stb/stb_sprintf.h" #include #include @@ -338,9 +339,9 @@ static void syminfo_callback (void *data, uintptr_t pc, const char *symname, uin { char str[4096]; if (symname) - stbsp_snprintf(str, 4096, "%@ %s\n", pc, symname); + BF_stbsp_snprintf(str, 4096, "%@ %s\n", pc, symname); else - stbsp_snprintf(str, 4096, "%@\n", pc); + BF_stbsp_snprintf(str, 4096, "%@\n", pc); BFP_ERRPRINTF("%s", str); } @@ -354,7 +355,7 @@ static int full_callback(void *data, uintptr_t pc, const char* filename, int lin const char* showName = (demangledName != NULL) ? demangledName : function; char str[4096]; - stbsp_snprintf(str, 4096, "%@ %s %s:%d\n", pc, showName, filename?filename:"??", lineno); + BF_stbsp_snprintf(str, 4096, "%@ %s %s:%d\n", pc, showName, filename?filename:"??", lineno); BFP_ERRPRINTF("%s", str); if (demangledName != NULL) @@ -843,6 +844,8 @@ BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, cons //printf("BfpSpawn_Create: %s %s %x\n", inTargetPath, args, flags); + char* prevWorkingDir = NULL; + if ((workingDir != NULL) && (workingDir[0] != 0)) { if (chdir(workingDir) != 0) @@ -851,8 +854,19 @@ BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, cons OUTRESULT(BfpSpawnResult_UnknownError); return NULL; } + + prevWorkingDir = getcwd(NULL, 0); } + defer( + { + if (prevWorkingDir != NULL) + { + chdir(prevWorkingDir); + free(prevWorkingDir); + } + }); + String newArgs; String tempFileName; @@ -928,7 +942,7 @@ BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, cons { if (firstCharIdx != -1) { - stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx)); + stringViews.Add(Beefy::StringView(args + firstCharIdx, i - firstCharIdx)); firstCharIdx = -1; } } @@ -947,7 +961,7 @@ BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, cons } } if (firstCharIdx != -1) - stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx)); + stringViews.Add(Beefy::StringView(args + firstCharIdx, i - firstCharIdx)); Beefy::Array argvArr; @@ -1844,6 +1858,10 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* inName, BfpFileCreat } return result; }; + + // POSIX doesn't need the OpenAlways kind. + if (createKind == BfpFileCreateKind_OpenAlways) + createKind = BfpFileCreateKind_CreateAlways; BfpFile* bfpFile = NULL; @@ -1940,6 +1958,11 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResu return bfpFile; } +BFP_EXPORT intptr BFP_CALLTYPE BfpFile_GetSystemHandle(BfpFile* file) +{ + return (intptr)file->mHandle; +} + BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file) { if ((file->mHandle != -1) && (!file->mIsStd)) @@ -2060,13 +2083,15 @@ BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileS return lseek64(file->mHandle, offset, whence); } -BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file) +BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult) { int64 curPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR); if (ftruncate64(file->mHandle, curPos) != 0) - { - //TODO: Report error? - } + { + OUTRESULT(BfpFileResult_UnknownError); + return; + } + OUTRESULT(BfpFileResult_Ok); } BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path) diff --git a/BeefySysLib/platform/win/DDS.h b/BeefySysLib/platform/win/DDS.h new file mode 100644 index 00000000..66bd734d --- /dev/null +++ b/BeefySysLib/platform/win/DDS.h @@ -0,0 +1,243 @@ +//-------------------------------------------------------------------------------------- +// dds.h +// +// This header defines constants and structures that are useful when parsing +// DDS files. DDS files were originally designed to use several structures +// and constants that are native to DirectDraw and are defined in ddraw.h, +// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar +// (compatible) constants and structures so that one can use DDS files +// without needing to include ddraw.h. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//-------------------------------------------------------------------------------------- + +#pragma once + +#if defined(_XBOX_ONE) && defined(_TITLE) +#include +#else +#include +#endif + +#include + +namespace DirectX +{ + +#pragma pack(push,1) + +const uint32_t DDS_MAGIC = 0x20534444; // "DDS " + +struct DDS_PIXELFORMAT +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwFourCC; + uint32_t dwRGBBitCount; + uint32_t dwRBitMask; + uint32_t dwGBitMask; + uint32_t dwBBitMask; + uint32_t dwABitMask; +}; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + +#ifndef MAKEFOURCC + #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ + ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) +#endif /* defined(MAKEFOURCC) */ + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT1 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','2'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT3 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT4 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT5 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC4_UNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC4_SNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC5_UNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC5_SNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_YUY2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_X8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_X8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_G16R16 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R5G6B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A1R5G5B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A4R4G4B4 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L16 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8 = + { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0x0000, 0x0000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_Q8W8V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_V16U16 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; + +// D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue + +// This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat) +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DX10 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; + +#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT +#define DDS_WIDTH 0x00000004 // DDSD_WIDTH + +#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE +#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ + DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ + DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME + +// Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION +enum DDS_RESOURCE_DIMENSION +{ + DDS_DIMENSION_TEXTURE1D = 2, + DDS_DIMENSION_TEXTURE2D = 3, + DDS_DIMENSION_TEXTURE3D = 4, +}; + +// Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG +enum DDS_RESOURCE_MISC_FLAG +{ + DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L, +}; + +enum DDS_MISC_FLAGS2 +{ + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, +}; + +enum DDS_ALPHA_MODE +{ + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, +}; + +struct DDS_HEADER +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwHeight; + uint32_t dwWidth; + uint32_t dwPitchOrLinearSize; + uint32_t dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags + uint32_t dwMipMapCount; + uint32_t dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t dwCaps; + uint32_t dwCaps2; + uint32_t dwCaps3; + uint32_t dwCaps4; + uint32_t dwReserved2; +}; + +struct DDS_HEADER_DXT10 +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see DDS_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 +}; + +#pragma pack(pop) + +static_assert( sizeof(DDS_HEADER) == 124, "DDS Header size mismatch" ); +static_assert( sizeof(DDS_HEADER_DXT10) == 20, "DDS DX10 Extended Header size mismatch"); + +}; // namespace diff --git a/BeefySysLib/platform/win/DXRenderDevice.cpp b/BeefySysLib/platform/win/DXRenderDevice.cpp index 8601b6e5..6770afcd 100644 --- a/BeefySysLib/platform/win/DXRenderDevice.cpp +++ b/BeefySysLib/platform/win/DXRenderDevice.cpp @@ -5,6 +5,10 @@ #include "img/ImageData.h" #include "util/PerfTimer.h" #include "util/BeefPerf.h" +#include "FileStream.h" +#include "DDS.h" + +using namespace DirectX; #include @@ -37,6 +41,131 @@ USING_NS_BF; #define DXFAILED(check) ((hr = (check)) != 0) #define DXCHECK(check) if ((check) != 0) BF_FATAL(StrFormat("DirectX call failed with result 0x%X", check).c_str()); +static int GetBytesPerPixel(DXGI_FORMAT fmt, int& blockSize) +{ + blockSize = 1; + switch (fmt) + { + case DXGI_FORMAT_UNKNOWN: return 0; + case DXGI_FORMAT_R32G32B32A32_TYPELESS: return 4 + 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32A32_FLOAT: return 4 + 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32A32_UINT: return 4 + 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32A32_SINT: return 4 + 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32_TYPELESS: return 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32_FLOAT: return 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32_UINT: return 4 + 4 + 4; + case DXGI_FORMAT_R32G32B32_SINT: return 4 + 4 + 4; + case DXGI_FORMAT_R16G16B16A16_TYPELESS: return 2 + 2 + 2 + 2; + case DXGI_FORMAT_R16G16B16A16_FLOAT: return 2 + 2 + 2 + 2; + case DXGI_FORMAT_R16G16B16A16_UNORM: return 2 + 2 + 2 + 2; + case DXGI_FORMAT_R16G16B16A16_UINT: return 2 + 2 + 2 + 2; + case DXGI_FORMAT_R16G16B16A16_SNORM: return 2 + 2 + 2 + 2; + case DXGI_FORMAT_R16G16B16A16_SINT: return 2 + 2 + 2 + 2; + case DXGI_FORMAT_R32G32_TYPELESS: return 4 + 4; + case DXGI_FORMAT_R32G32_FLOAT: return 4 + 4; + case DXGI_FORMAT_R32G32_UINT: return 4 + 4; + case DXGI_FORMAT_R32G32_SINT: return 4 + 4; + case DXGI_FORMAT_R32G8X24_TYPELESS: return 4 + 3; + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: return 4 + 1 + 3; + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: return 4 + 1 + 3; + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: return 4 + 1 + 1 + 3; + case DXGI_FORMAT_R10G10B10A2_TYPELESS: return 4; + case DXGI_FORMAT_R10G10B10A2_UNORM: return 4; + case DXGI_FORMAT_R10G10B10A2_UINT: return 4; + case DXGI_FORMAT_R11G11B10_FLOAT: return 4; + case DXGI_FORMAT_R8G8B8A8_TYPELESS: return 4; + case DXGI_FORMAT_R8G8B8A8_UNORM: return 4; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return 4; + case DXGI_FORMAT_R8G8B8A8_UINT: return 4; + case DXGI_FORMAT_R8G8B8A8_SNORM: return 4; + case DXGI_FORMAT_R8G8B8A8_SINT: return 4; + case DXGI_FORMAT_R16G16_TYPELESS: return 4; + case DXGI_FORMAT_R16G16_FLOAT: return 4; + case DXGI_FORMAT_R16G16_UNORM: return 4; + case DXGI_FORMAT_R16G16_UINT: return 4; + case DXGI_FORMAT_R16G16_SNORM: return 4; + case DXGI_FORMAT_R16G16_SINT: return 4; + case DXGI_FORMAT_R32_TYPELESS: return 4; + case DXGI_FORMAT_D32_FLOAT: return 4; + case DXGI_FORMAT_R32_FLOAT: return 4; + case DXGI_FORMAT_R32_UINT: return 4; + case DXGI_FORMAT_R32_SINT: return 4; + case DXGI_FORMAT_R24G8_TYPELESS: return 4; + case DXGI_FORMAT_D24_UNORM_S8_UINT: return 4; + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return 4; + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: return 4; + case DXGI_FORMAT_R8G8_TYPELESS: return 2; + case DXGI_FORMAT_R8G8_UNORM: return 2; + case DXGI_FORMAT_R8G8_UINT: return 2; + case DXGI_FORMAT_R8G8_SNORM: return 2; + case DXGI_FORMAT_R8G8_SINT: return 2; + case DXGI_FORMAT_R16_TYPELESS: return 2; + case DXGI_FORMAT_R16_FLOAT: return 2; + case DXGI_FORMAT_D16_UNORM: return 2; + case DXGI_FORMAT_R16_UNORM: return 2; + case DXGI_FORMAT_R16_UINT: return 2; + case DXGI_FORMAT_R16_SNORM: return 2; + case DXGI_FORMAT_R16_SINT: return 2; + case DXGI_FORMAT_R8_TYPELESS: return 1; + case DXGI_FORMAT_R8_UNORM: return 1; + case DXGI_FORMAT_R8_UINT: return 1; + case DXGI_FORMAT_R8_SNORM: return 1; + case DXGI_FORMAT_R8_SINT: return 1; + case DXGI_FORMAT_A8_UNORM: return 1; + case DXGI_FORMAT_R1_UNORM: return 1; + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: return 3; + case DXGI_FORMAT_R8G8_B8G8_UNORM: return 4; + case DXGI_FORMAT_G8R8_G8B8_UNORM: return 4; + case DXGI_FORMAT_BC1_TYPELESS: blockSize = 4; return 8; + case DXGI_FORMAT_BC1_UNORM: blockSize = 4; return 8; + case DXGI_FORMAT_BC1_UNORM_SRGB: blockSize = 4; return 8; + case DXGI_FORMAT_BC2_TYPELESS: blockSize = 4; return 16; + case DXGI_FORMAT_BC2_UNORM: blockSize = 4; return 16; + case DXGI_FORMAT_BC2_UNORM_SRGB: blockSize = 4; return 16; + case DXGI_FORMAT_BC3_TYPELESS: blockSize = 4; return 16; + case DXGI_FORMAT_BC3_UNORM: blockSize = 4; return 16; + case DXGI_FORMAT_BC3_UNORM_SRGB: blockSize = 4; return 16; + case DXGI_FORMAT_BC4_TYPELESS: blockSize = 4; return 8; + case DXGI_FORMAT_BC4_UNORM: blockSize = 4; return 8; + case DXGI_FORMAT_BC4_SNORM: blockSize = 4; return 8; + case DXGI_FORMAT_BC5_TYPELESS: blockSize = 4; return 16; + case DXGI_FORMAT_BC5_UNORM: blockSize = 4; return 16; + case DXGI_FORMAT_BC5_SNORM: blockSize = 4; return 16; + case DXGI_FORMAT_B5G6R5_UNORM: return 1; + case DXGI_FORMAT_B5G5R5A1_UNORM: return 2; + case DXGI_FORMAT_B8G8R8A8_UNORM: return 4; + case DXGI_FORMAT_B8G8R8X8_UNORM: return 4; + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: return 4; + case DXGI_FORMAT_B8G8R8A8_TYPELESS: return 4; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return 4; + case DXGI_FORMAT_B8G8R8X8_TYPELESS: return 4; + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return 4; + case DXGI_FORMAT_BC6H_TYPELESS: return 1; + case DXGI_FORMAT_BC6H_UF16: return 1; + case DXGI_FORMAT_BC6H_SF16: return 1; + case DXGI_FORMAT_BC7_TYPELESS: blockSize = 4; return 16; + case DXGI_FORMAT_BC7_UNORM: blockSize = 4; return 16; + case DXGI_FORMAT_BC7_UNORM_SRGB: blockSize = 4; return 16; + case DXGI_FORMAT_AYUV: return 1; + case DXGI_FORMAT_Y410: return 1; + case DXGI_FORMAT_Y416: return 1; + case DXGI_FORMAT_NV12: return 1; + case DXGI_FORMAT_P010: return 1; + case DXGI_FORMAT_P016: return 1; + case DXGI_FORMAT_420_OPAQUE: return 1; + case DXGI_FORMAT_YUY2: return 1; + case DXGI_FORMAT_Y210: return 1; + case DXGI_FORMAT_Y216: return 1; + case DXGI_FORMAT_NV11: return 1; + case DXGI_FORMAT_AI44: return 1; + case DXGI_FORMAT_IA44: return 1; + case DXGI_FORMAT_P8: return 1; + case DXGI_FORMAT_A8P8: return 1; + case DXGI_FORMAT_B4G4R4A4_UNORM: return 1; + default: return 1; + } +} + DXShaderParam::DXShaderParam() { mD3DVariable = NULL; @@ -125,6 +254,9 @@ DXTexture::DXTexture() DXTexture::~DXTexture() { + if ((!mPath.IsEmpty()) && (mRenderDevice != NULL)) + ((DXRenderDevice*)mRenderDevice)->mTextureMap.Remove(mPath); + //OutputDebugStrF("DXTexture::~DXTexture %@\n", this); delete mImageData; if (mD3DResourceView != NULL) @@ -381,9 +513,17 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) DXRenderState* dxRenderState = (DXRenderState*)renderState; DXShader* dxShader = (DXShader*)renderState->mShader; - if ((renderState->mShader != mPhysRenderState->mShader) && (renderState->mShader != NULL)) + if (renderState->mTopology != mPhysRenderState->mTopology) { - mD3DDeviceContext->PSSetSamplers(0, 1, &mD3DDefaultSamplerState); + D3D_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + if (dxRenderState->mTopology == Topology3D_LineLine) + topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; + mD3DDeviceContext->IASetPrimitiveTopology(topology); + } + + if ((renderState->mShader != mPhysRenderState->mShader) && (renderState->mShader != NULL)) + { + mD3DDeviceContext->PSSetSamplers(0, 1, renderState->mTexWrap ? &mD3DWrapSamplerState : &mD3DDefaultSamplerState); mD3DDeviceContext->IASetInputLayout(dxShader->mD3DLayout); mD3DDeviceContext->VSSetShader(dxShader->mD3DVertexShader, NULL, 0); mD3DDeviceContext->PSSetShader(dxShader->mD3DPixelShader, NULL, 0); @@ -462,6 +602,9 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) setRasterizerState = true; } + if (renderState->mWireframe != mPhysRenderState->mWireframe) + setRasterizerState = true; + if (setRasterizerState) { if (dxRenderState->mD3DRasterizerState == NULL) @@ -470,13 +613,13 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) { D3D11_CULL_NONE, D3D11_CULL_FRONT, - D3D11_CULL_BACK + D3D11_CULL_BACK }; D3D11_RASTERIZER_DESC rasterizerState; rasterizerState.CullMode = cullModes[dxRenderState->mCullMode]; //rasterizerState.CullMode = D3D11_CULL_BACK; - rasterizerState.FillMode = D3D11_FILL_SOLID; + rasterizerState.FillMode = renderState->mWireframe ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID; rasterizerState.FrontCounterClockwise = false; rasterizerState.DepthBias = 0; rasterizerState.DepthBiasClamp = 0; @@ -572,7 +715,7 @@ struct DXModelVertex Vector3 mTangent; }; -ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef) +ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags) { DXModelInstance* dxModelInstance = new DXModelInstance(modelDef); @@ -589,104 +732,150 @@ ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef) }; auto vertexDefinition = CreateVertexDefinition(vertexDefData, sizeof(vertexDefData) / sizeof(vertexDefData[0])); - auto renderState = CreateRenderState(mDefaultRenderState); - renderState->mShader = LoadShader(gBFApp->mInstallDir + "/shaders/ModelStd", vertexDefinition); + RenderState* renderState = NULL; + if ((flags & ModelCreateFlags_NoSetRenderState) == 0) + { + renderState = CreateRenderState(mDefaultRenderState); + renderState->mShader = LoadShader(gBFApp->mInstallDir + "/shaders/ModelStd", vertexDefinition); + renderState->mTexWrap = true; + renderState->mDepthFunc = DepthFunc_LessEqual; + renderState->mWriteDepthBuffer = true; + } delete vertexDefinition; - - //renderState->mCullMode = CullMode_Front; - - renderState->mDepthFunc = DepthFunc_LessEqual; - renderState->mWriteDepthBuffer = true; - + dxModelInstance->mRenderState = renderState; //// - dxModelInstance->mD3DRenderDevice = this; - - dxModelInstance->mDXModelMeshs.resize(modelDef->mMeshes.size()); + dxModelInstance->mD3DRenderDevice = this; + dxModelInstance->mDXModelMeshs.Resize(modelDef->mMeshes.size()); + int dxMeshIdx = 0; for (int meshIdx = 0; meshIdx < (int)modelDef->mMeshes.size(); meshIdx++) { ModelMesh* mesh = &modelDef->mMeshes[meshIdx]; - - DXModelMesh* dxMesh = &dxModelInstance->mDXModelMeshs[meshIdx]; - - String texPath = mesh->mTexFileName; - if ((int)texPath.IndexOf(':') == -1) - texPath = modelDef->mLoadDir + "Textures/" + texPath; - //texPath = gBFApp->mInstallDir + L"models/Textures/" + texPath; - - dxMesh->mTexture = (DXTexture*)((RenderDevice*)this)->LoadTexture(texPath, TextureFlag_NoPremult); - - dxMesh->mNumIndices = (int)mesh->mIndices.size(); - dxMesh->mNumVertices = (int)mesh->mVertices.size(); - - D3D11_BUFFER_DESC bd; - bd.Usage = D3D11_USAGE_DYNAMIC; - bd.ByteWidth = (int)mesh->mIndices.size() * sizeof(uint16); - bd.BindFlags = D3D11_BIND_INDEX_BUFFER; - bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - bd.MiscFlags = 0; - bd.StructureByteStride = 0; - - mD3DDevice->CreateBuffer(&bd, NULL, &dxMesh->mD3DIndexBuffer); - - D3D11_MAPPED_SUBRESOURCE mappedSubResource; - - DXCHECK(mD3DDeviceContext->Map(dxMesh->mD3DIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource)); - uint16* dxIdxData = (uint16*)mappedSubResource.pData; - for (int idxIdx = 0; idxIdx < dxMesh->mNumIndices; idxIdx++) - dxIdxData[idxIdx] = (uint16)mesh->mIndices[idxIdx]; - mD3DDeviceContext->Unmap(dxMesh->mD3DIndexBuffer, 0); - - // - - bd.Usage = D3D11_USAGE_DYNAMIC; - bd.ByteWidth = (int)mesh->mVertices.size() * sizeof(DXModelVertex); - bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; - bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - bd.MiscFlags = 0; - bd.StructureByteStride = 0; - - mD3DDevice->CreateBuffer(&bd, NULL, &dxMesh->mD3DVertexBuffer); - - /*DXCHECK(mD3DDeviceContext->Map(dxMesh->mD3DVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource)); - DXModelVertex* dxVtxData = (DXModelVertex*)mappedSubResource.pData; - for (int vtxIdx = 0; vtxIdx < (int)mesh->mVertexData.size(); vtxIdx++) - { - VertexData* srcVtxData = &mesh->mVertexData[vtxIdx]; - DXModelVertex* destVtx = dxVtxData + vtxIdx; - - destVtx->mPosition = srcVtxData->mCoords; - destVtx->mTexCoords = srcVtxData->mTexCoords[0]; - destVtx->mTexCoords.mV = 1.0f - destVtx->mTexCoords.mV; - destVtx->mBumpTexCoords = srcVtxData->mTexCoords[0]; - destVtx->mColor = 0xFFFFFFFF; - destVtx->mTangent = srcVtxData->mTangent; - } + DXModelMesh* dxMesh = &dxModelInstance->mDXModelMeshs[dxMeshIdx]; - mD3DDeviceContext->Unmap(dxMesh->mD3DVertexBuffer, 0);*/ + dxMesh->mPrimitives.Resize(mesh->mPrimitives.size()); + + for (int primitivesIdx = 0 ; primitivesIdx < (int)mesh->mPrimitives.size(); primitivesIdx++) + { + auto primitives = &mesh->mPrimitives[primitivesIdx]; + auto dxPrimitives = &dxMesh->mPrimitives[primitivesIdx]; + +// String texPath = mesh->mTexFileName; +// if (!texPath.IsEmpty()) +// { +// if ((int)texPath.IndexOf(':') == -1) +// texPath = modelDef->mLoadDir + "Textures/" + texPath; +// //texPath = gBFApp->mInstallDir + L"models/Textures/" + texPath; +// +// dxPrimitives->mTexture = (DXTexture*)((RenderDevice*)this)->LoadTexture(texPath, TextureFlag_NoPremult); +// } + + Array texPaths = primitives->mTexPaths; + + + if (primitives->mMaterial != NULL) + { + dxPrimitives->mMaterialName = primitives->mMaterial->mName; + if (primitives->mMaterial->mDef != NULL) + { + for (auto& texParamVal : primitives->mMaterial->mDef->mTextureParameterValues) + { + if (texPaths.IsEmpty()) + texPaths.Add(texParamVal->mTexturePath); + +// if (texPath.IsEmpty()) +// texPath = texParamVal->mTexturePath; +// if ((texParamVal->mName == "Albedo_texture") || (texParamVal->mName.EndsWith("_Color"))) +// texPath = texParamVal->mTexturePath; +// else if ((texParamVal->mName == "NM_texture") || (texParamVal->mName.EndsWith("_NM"))) +// bumpTexPath = texParamVal->mTexturePath; + } + } + } + + for (auto& texPath : texPaths) + { + if (!modelDef->mLoadDir.IsEmpty()) + texPath = GetAbsPath(texPath, modelDef->mLoadDir); + dxPrimitives->mTextures.Add((DXTexture*)((RenderDevice*)this)->LoadTexture(texPath, TextureFlag_NoPremult)); + } + + dxPrimitives->mNumIndices = (int)primitives->mIndices.size(); + dxPrimitives->mNumVertices = (int)primitives->mVertices.size(); + + D3D11_BUFFER_DESC bd; + bd.Usage = D3D11_USAGE_DYNAMIC; + bd.ByteWidth = (int)primitives->mIndices.size() * sizeof(uint16); + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + bd.StructureByteStride = 0; + + mD3DDevice->CreateBuffer(&bd, NULL, &dxPrimitives->mD3DIndexBuffer); + + D3D11_MAPPED_SUBRESOURCE mappedSubResource; + + DXCHECK(mD3DDeviceContext->Map(dxPrimitives->mD3DIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource)); + uint16* dxIdxData = (uint16*)mappedSubResource.pData; + for (int idxIdx = 0; idxIdx < dxPrimitives->mNumIndices; idxIdx++) + dxIdxData[idxIdx] = (uint16)primitives->mIndices[idxIdx]; + mD3DDeviceContext->Unmap(dxPrimitives->mD3DIndexBuffer, 0); + + // + + bd.Usage = D3D11_USAGE_DYNAMIC; + bd.ByteWidth = (int)primitives->mVertices.size() * sizeof(DXModelVertex); + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + bd.StructureByteStride = 0; + + mD3DDevice->CreateBuffer(&bd, NULL, &dxPrimitives->mD3DVertexBuffer); + + DXCHECK(mD3DDeviceContext->Map(dxPrimitives->mD3DVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubResource)); + DXModelVertex* dxVtxData = (DXModelVertex*)mappedSubResource.pData; + for (int vtxIdx = 0; vtxIdx < (int)primitives->mVertices.size(); vtxIdx++) + { + ModelVertex* srcVtxData = &primitives->mVertices[vtxIdx]; + DXModelVertex* destVtx = dxVtxData + vtxIdx; + + destVtx->mPosition = srcVtxData->mPosition; + destVtx->mTexCoords = srcVtxData->mTexCoords; + //destVtx->mTexCoords.mV = 1.0f - destVtx->mTexCoords.mV; + destVtx->mTexCoords.mV = destVtx->mTexCoords.mV; + destVtx->mBumpTexCoords = srcVtxData->mBumpTexCoords; + destVtx->mColor = srcVtxData->mColor; + destVtx->mTangent = srcVtxData->mTangent; + } + + mD3DDeviceContext->Unmap(dxPrimitives->mD3DVertexBuffer, 0); + + dxMeshIdx++; + } } return dxModelInstance; } -void DXDrawLayer::SetShaderConstantData(int slotIdx, void* constData, int size) +void DXDrawLayer::SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size) { DXSetConstantData* dxSetConstantData = AllocRenderCmd(size); dxSetConstantData->mRenderState = mRenderDevice->mCurRenderState; + dxSetConstantData->mUsageIdx = usageIdx; dxSetConstantData->mSlotIdx = slotIdx; dxSetConstantData->mSize = size; - if (size == 64) // Transpose for shader - *((Matrix4*)dxSetConstantData->mData) = Matrix4::Transpose(*((Matrix4*)constData)); - else +// if (size == 64) // Transpose for shader +// *((Matrix4*)dxSetConstantData->mData) = Matrix4::Transpose(*((Matrix4*)constData)); +// else memcpy(dxSetConstantData->mData, constData, size); QueueRenderCmd(dxSetConstantData); } -void DXDrawLayer::SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount) +void DXDrawLayer::SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount) { for (int usageIdx = 0; usageIdx < 2; usageIdx++) { @@ -770,18 +959,20 @@ void DXDrawLayer::SetShaderConstantDataTyped(int slotIdx, void* constData, int s /// -DXModelMesh::DXModelMesh() +DXModelPrimitives::DXModelPrimitives() { mD3DIndexBuffer = NULL; - mD3DVertexBuffer = NULL; + mD3DVertexBuffer = NULL; } -DXModelMesh::~DXModelMesh() +DXModelPrimitives::~DXModelPrimitives() { if (mD3DIndexBuffer != NULL) mD3DIndexBuffer->Release(); if (mD3DVertexBuffer != NULL) mD3DVertexBuffer->Release(); + for (auto tex : mTextures) + tex->Release(); } ////////////////////////////////////////////////////////////////////////// @@ -839,6 +1030,12 @@ void DXRenderState::SetClipped(bool clipped) InvalidateRasterizerState(); } +void DXRenderState::SetTexWrap(bool wrap) +{ + mTexWrap = wrap; + InvalidateRasterizerState(); +} + void DXRenderState::SetClipRect(const Rect& rect) { BF_ASSERT((rect.mWidth >= 0) && (rect.mHeight >= 0)); @@ -870,7 +1067,8 @@ DXModelInstance::~DXModelInstance() void DXModelInstance::Render(RenderDevice* renderDevice, RenderWindow* renderWindow) { - SetRenderState(); + if (mRenderState != NULL) + SetRenderState(); for (int meshIdx = 0; meshIdx < (int)mDXModelMeshs.size(); meshIdx++) { @@ -879,26 +1077,33 @@ void DXModelInstance::Render(RenderDevice* renderDevice, RenderWindow* renderWin DXModelMesh* dxMesh = &mDXModelMeshs[meshIdx]; - mD3DRenderDevice->mD3DDeviceContext->PSSetShaderResources(0, 1, &dxMesh->mTexture->mD3DResourceView); - - // Set vertex buffer - UINT stride = sizeof(DXModelVertex); - UINT offset = 0; - mD3DRenderDevice->mD3DDeviceContext->IASetVertexBuffers(0, 1, &dxMesh->mD3DVertexBuffer, &stride, &offset); - mD3DRenderDevice->mD3DDeviceContext->IASetIndexBuffer(dxMesh->mD3DIndexBuffer, DXGI_FORMAT_R16_UINT, 0); - mD3DRenderDevice->mD3DDeviceContext->DrawIndexed(dxMesh->mNumIndices, 0, 0); + for (auto primIdx = 0; primIdx < (int)dxMesh->mPrimitives.size(); primIdx++) + { + auto dxPrimitives = &dxMesh->mPrimitives[primIdx]; + + if (dxPrimitives->mTextures.IsEmpty()) + continue; + + for (int i = 0; i < (int)dxPrimitives->mTextures.mSize; i++) + mD3DRenderDevice->mD3DDeviceContext->PSSetShaderResources(i, 1, &dxPrimitives->mTextures[i]->mD3DResourceView); + + // Set vertex buffer + UINT stride = sizeof(DXModelVertex); + UINT offset = 0; + mD3DRenderDevice->mD3DDeviceContext->IASetVertexBuffers(0, 1, &dxPrimitives->mD3DVertexBuffer, &stride, &offset); + mD3DRenderDevice->mD3DDeviceContext->IASetIndexBuffer(dxPrimitives->mD3DIndexBuffer, DXGI_FORMAT_R16_UINT, 0); + mD3DRenderDevice->mD3DDeviceContext->DrawIndexed(dxPrimitives->mNumIndices, 0, 0); + } } } void Beefy::DXModelInstance::CommandQueued(DrawLayer* drawLayer) -{ -#ifndef BF_NO_FBX +{ mRenderState = drawLayer->mRenderDevice->mCurRenderState; - BF_ASSERT(mRenderState->mShader->mVertexSize == sizeof(DXModelVertex)); - drawLayer->mCurTextures[0] = NULL; +#ifndef BF_NO_FBX ModelAnimation* fbxAnim = &mModelDef->mAnims[0]; Matrix4 jointsMatrices[BF_MAX_NUM_BONES]; @@ -992,46 +1197,59 @@ void DXSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin void DXSetConstantData::Render(RenderDevice* renderDevice, RenderWindow* renderWindow) { - SetRenderState(); + //SetRenderState(); DXShader* dxShader = (DXShader*)renderDevice->mCurRenderState->mShader; DXRenderDevice* dxRenderDevice = (DXRenderDevice*)renderDevice; HRESULT result = 0; - int numBlocks = (mSize + 16 - 1) / 16; - int mtxBufferNum = mSlotIdx * 32 + (numBlocks - 1) * 2 + mUsageIdx; - static ID3D11Buffer* matrixBuffers[32 * 32 * 2] = {NULL}; - - if (matrixBuffers[mtxBufferNum] == NULL) - { + int bufferSize = BF_ALIGN(mSize, 16); + + int id = (mSlotIdx << 24) | (bufferSize << 1) | (mUsageIdx); + ID3D11Buffer* buffer = NULL; + ID3D11Buffer** bufferPtr = NULL; + if (dxRenderDevice->mBufferMap.TryAdd(id, NULL, &bufferPtr)) + { D3D11_BUFFER_DESC matrixBufferDesc; matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; - matrixBufferDesc.ByteWidth = sizeof(float[4]) * numBlocks; + matrixBufferDesc.ByteWidth = bufferSize; matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; matrixBufferDesc.MiscFlags = 0; matrixBufferDesc.StructureByteStride = 0; - result = dxRenderDevice->mD3DDevice->CreateBuffer(&matrixBufferDesc, NULL, &matrixBuffers[mtxBufferNum]); + result = dxRenderDevice->mD3DDevice->CreateBuffer(&matrixBufferDesc, NULL, &buffer); if (FAILED(result)) return; + + //OutputDebugStrF("Created Buffer %d %p\n", bufferSize, buffer); + + *bufferPtr = buffer; } + else + buffer = *bufferPtr; D3D11_MAPPED_SUBRESOURCE mappedResource; - result = dxRenderDevice->mD3DDeviceContext->Map(matrixBuffers[mtxBufferNum], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); - if (FAILED(result)) + result = dxRenderDevice->mD3DDeviceContext->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + if (FAILED(result)) return; - - float* dataPtr = (float*) mappedResource.pData; - memset(dataPtr, 0, numBlocks * 16); + + float* dataPtr = (float*)mappedResource.pData; + memset(dataPtr, 0, bufferSize); memcpy(mappedResource.pData, mData, mSize); - - dxRenderDevice->mD3DDeviceContext->Unmap(matrixBuffers[mtxBufferNum], 0); + + dxRenderDevice->mD3DDeviceContext->Unmap(buffer, 0); if (mUsageIdx == 0) - dxRenderDevice->mD3DDeviceContext->VSSetConstantBuffers(mSlotIdx, 1, &matrixBuffers[mtxBufferNum]); + { + //OutputDebugStrF("VSSetConstantBuffers %d %p\n", mSlotIdx, buffer); + dxRenderDevice->mD3DDeviceContext->VSSetConstantBuffers(mSlotIdx, 1, &buffer); + } else - dxRenderDevice->mD3DDeviceContext->PSSetConstantBuffers(mSlotIdx, 1, &matrixBuffers[mtxBufferNum]); + { + //OutputDebugStrF("PSSetConstantBuffers %d %p\n", mSlotIdx, buffer); + dxRenderDevice->mD3DDeviceContext->PSSetConstantBuffers(mSlotIdx, 1, &buffer); + } } /// @@ -1268,6 +1486,9 @@ DXRenderDevice::DXRenderDevice() DXRenderDevice::~DXRenderDevice() { + for (auto& kv : mTextureMap) + kv.mValue->mRenderDevice = NULL; + mD3DVertexBuffer->Release(); mD3DIndexBuffer->Release(); delete mDefaultRenderState; @@ -1291,6 +1512,7 @@ bool DXRenderDevice::Init(BFApp* app) D3D_FEATURE_LEVEL d3dFeatureLevel = (D3D_FEATURE_LEVEL)0; int flags = 0; + //TODO: //flags = D3D11_CREATE_DEVICE_DEBUG; DXCHECK(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevelArr, 6, D3D11_SDK_VERSION, &mD3DDevice, &d3dFeatureLevel, &mD3DDeviceContext)); OutputDebugStrF("D3D Feature Level: %X\n", d3dFeatureLevel); @@ -1334,8 +1556,7 @@ bool DXRenderDevice::Init(BFApp* app) rasterizerState.AntialiasedLineEnable = false; mD3DDevice->CreateRasterizerState(&rasterizerState, &dxRenderState->mD3DRasterizerState); - mD3DDeviceContext->RSSetState(dxRenderState->mD3DRasterizerState); - + mD3DDeviceContext->RSSetState(dxRenderState->mD3DRasterizerState); mD3DDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ID3D11BlendState* g_pBlendState = NULL; @@ -1363,9 +1584,18 @@ bool DXRenderDevice::Init(BFApp* app) sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampDesc.MinLOD = 0; - sampDesc.MaxLOD = D3D11_FLOAT32_MAX; - + sampDesc.MaxLOD = D3D11_FLOAT32_MAX; DXCHECK(mD3DDevice->CreateSamplerState(&sampDesc, &mD3DDefaultSamplerState)); + + ZeroMemory(&sampDesc, sizeof(sampDesc)); + sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampDesc.MinLOD = 0; + sampDesc.MaxLOD = D3D11_FLOAT32_MAX; + DXCHECK(mD3DDevice->CreateSamplerState(&sampDesc, &mD3DWrapSamplerState)); D3D11_BUFFER_DESC bd; bd.Usage = D3D11_USAGE_DYNAMIC; @@ -1402,6 +1632,8 @@ void DXRenderDevice::ReleaseNative() mD3DNormalBlendState = NULL; mD3DDefaultSamplerState->Release(); mD3DDefaultSamplerState = NULL; + mD3DWrapSamplerState->Release(); + mD3DWrapSamplerState = NULL; mD3DDeviceContext->Release(); mD3DDeviceContext = NULL; mD3DDevice->Release(); @@ -1468,6 +1700,178 @@ void DXRenderDevice::FrameEnd() } } +Texture* DXRenderDevice::LoadTexture(const StringImpl& fileName, int flags) +{ + if (fileName.StartsWith("!backbuffer:")) + { + int colon = (int)fileName.IndexOf(':'); + String addrStr = fileName.Substring(colon + 1); + void* addr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16); + BFWindow* window = (BFWindow*)addr; + DXRenderWindow* renderWindow = (DXRenderWindow*)window->mRenderWindow; + + DXTexture* aTexture = NULL; + aTexture->mD3DRenderTargetView = renderWindow->mD3DRenderTargetView; + aTexture->mD3DTexture = renderWindow->mD3DBackBuffer; + + aTexture->mD3DRenderTargetView->AddRef(); + aTexture->mD3DTexture->AddRef(); + aTexture->AddRef(); + return aTexture; + } + + DXTexture* aTexture = NULL; + if (mTextureMap.TryGetValue(fileName, &aTexture)) + { + aTexture->AddRef(); + return aTexture; + } + + int dotPos = (int)fileName.LastIndexOf('.'); + String ext; + if (dotPos != -1) + ext = fileName.Substring(dotPos); + + if (ext.Equals(".dds", StringImpl::CompareKind_OrdinalIgnoreCase)) + { + FileStream fs; + if (!fs.Open(fileName, "rb")) + return NULL; + + int header = fs.ReadInt32(); + if (header != 0x20534444) + return NULL; + + auto hdr = fs.ReadT(); + + DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; + + if (hdr.ddspf.dwFlags == DDS_RGBA) + { + if (hdr.ddspf.dwRGBBitCount == 32) + { + if (hdr.ddspf.dwRBitMask == 0xff) + format = DXGI_FORMAT_R8G8B8A8_UNORM; + else if (hdr.ddspf.dwRBitMask = 0xff0000) + format = DXGI_FORMAT_B8G8R8A8_UNORM; + else if (hdr.ddspf.dwRBitMask == 0xffff) + format = DXGI_FORMAT_R16G16_UNORM; + else if (hdr.ddspf.dwRBitMask == 0x3ff) + format = DXGI_FORMAT_R10G10B10A2_UNORM; + } + else if (hdr.ddspf.dwRGBBitCount == 16) + { + if (hdr.ddspf.dwRBitMask == 0x7c00) + format = DXGI_FORMAT_B5G5R5A1_UNORM; + else if (hdr.ddspf.dwRBitMask == 0xf800) + format = DXGI_FORMAT_B5G6R5_UNORM; + } + else if (hdr.ddspf.dwRGBBitCount == 8) + { + if (hdr.ddspf.dwRBitMask == 0xff) + format = DXGI_FORMAT_R8_UNORM; + else if (hdr.ddspf.dwABitMask == 0xff) + format = DXGI_FORMAT_A8_UNORM; + } + } + + if (hdr.ddspf.dwFourCC == '1TXD') + format = DXGI_FORMAT_BC1_UNORM; + if (hdr.ddspf.dwFourCC == '3TXD') + format = DXGI_FORMAT_BC2_UNORM; + if (hdr.ddspf.dwFourCC == '5TXD') + format = DXGI_FORMAT_BC3_UNORM; + if (hdr.ddspf.dwFourCC == 'U4CB') + format = DXGI_FORMAT_BC4_UNORM; + if (hdr.ddspf.dwFourCC == 'S4CB') + format = DXGI_FORMAT_BC4_SNORM; + if (hdr.ddspf.dwFourCC == '2ITA') + format = DXGI_FORMAT_BC5_UNORM; + if (hdr.ddspf.dwFourCC == 'S5CB') + format = DXGI_FORMAT_BC5_SNORM; + + if (hdr.ddspf.dwFourCC == '01XD') + { + auto hdr10 = fs.ReadT(); + format = hdr10.dxgiFormat; + } + + int blockSize = 0; + int bytesPerPixel = GetBytesPerPixel(format, blockSize); + + int mipSize = ((hdr.dwWidth + blockSize - 1) / blockSize) * ((hdr.dwHeight + blockSize - 1) / blockSize) * bytesPerPixel; + Array data; + data.Resize(mipSize); + fs.Read(data.mVals, data.mSize); + + D3D11_SUBRESOURCE_DATA resData; + resData.pSysMem = data.mVals; + resData.SysMemPitch = ((hdr.dwWidth + blockSize - 1) / blockSize) * bytesPerPixel; + resData.SysMemSlicePitch = mipSize; + + // Create the target texture + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = hdr.dwWidth; + desc.Height = hdr.dwHeight; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + DXGI_FORMAT viewFormat = format; + switch (viewFormat) + { + case DXGI_FORMAT_B8G8R8A8_TYPELESS: viewFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break; + case DXGI_FORMAT_R8G8B8A8_TYPELESS: viewFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break; + case DXGI_FORMAT_BC1_TYPELESS: viewFormat = DXGI_FORMAT_BC1_UNORM; break; + case DXGI_FORMAT_BC2_TYPELESS: viewFormat = DXGI_FORMAT_BC2_UNORM; break; + case DXGI_FORMAT_BC3_TYPELESS: viewFormat = DXGI_FORMAT_BC3_UNORM; break; + case DXGI_FORMAT_BC4_TYPELESS: viewFormat = DXGI_FORMAT_BC4_UNORM; break; + case DXGI_FORMAT_BC5_TYPELESS: viewFormat = DXGI_FORMAT_BC5_UNORM; break; + } + + //OutputDebugStrF("Creating texture\n"); + + ID3D11Texture2D* d3DTexture = NULL; + DXCHECK(mD3DDevice->CreateTexture2D(&desc, &resData, &d3DTexture)); + + D3D11_SHADER_RESOURCE_VIEW_DESC srDesc; + srDesc.Format = viewFormat; + srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srDesc.Texture2D.MostDetailedMip = 0; + srDesc.Texture2D.MipLevels = 1; + + ID3D11ShaderResourceView* d3DShaderResourceView = NULL; + DXCHECK(mD3DDevice->CreateShaderResourceView(d3DTexture, &srDesc, &d3DShaderResourceView)); + + DXTexture* aTexture = new DXTexture(); + aTexture->mPath = fileName; + aTexture->mRenderDevice = this; + aTexture->mWidth = hdr.dwWidth; + aTexture->mHeight = hdr.dwHeight; + aTexture->mD3DTexture = d3DTexture; + aTexture->mD3DResourceView = d3DShaderResourceView; + aTexture->AddRef(); + + mTextureMap[aTexture->mPath] = aTexture; + mTextures.Add(aTexture); + return aTexture; + } + + aTexture = (DXTexture*)RenderDevice::LoadTexture(fileName, flags); + if (aTexture != NULL) + { + aTexture->mPath = fileName; + mTextureMap[aTexture->mPath] = aTexture; + } + + return aTexture; +} + Texture* DXRenderDevice::LoadTexture(ImageData* imageData, int flags) { ID3D11ShaderResourceView* d3DShaderResourceView = NULL; @@ -1809,7 +2213,7 @@ Texture* DXRenderDevice::CreateRenderTarget(int width, int height, bool destAlph ID3D11Texture2D* d3DTexture = NULL; DXCHECK(mD3DDevice->CreateTexture2D(&desc, NULL, &d3DTexture)); - + aWidth = width; aHeight = height; @@ -1833,6 +2237,7 @@ Texture* DXRenderDevice::CreateRenderTarget(int width, int height, bool destAlph aRenderTarget->mWidth = width; aRenderTarget->mHeight = height; aRenderTarget->mRenderDevice = this; + aRenderTarget->mD3DTexture = d3DTexture; aRenderTarget->mD3DResourceView = d3DShaderResourceView; aRenderTarget->mD3DRenderTargetView = d3DRenderTargetView; aRenderTarget->AddRef(); diff --git a/BeefySysLib/platform/win/DXRenderDevice.h b/BeefySysLib/platform/win/DXRenderDevice.h index dcd83d93..b9bce08d 100644 --- a/BeefySysLib/platform/win/DXRenderDevice.h +++ b/BeefySysLib/platform/win/DXRenderDevice.h @@ -44,6 +44,7 @@ #include "gfx/DrawLayer.h" #include "gfx/ModelInstance.h" #include "util/HashSet.h" +#include "util/Dictionary.h" #include NS_BF_BEGIN; @@ -55,6 +56,7 @@ class DXRenderDevice; class DXTexture : public Texture { public: + String mPath; DXRenderDevice* mRenderDevice; ID3D11Texture2D* mD3DTexture; ID3D11ShaderResourceView* mD3DResourceView; @@ -93,7 +95,7 @@ typedef std::map DXShaderParamMap; class DXShader : public Shader { -public: +public: ID3D11InputLayout* mD3DLayout; ID3D11VertexShader* mD3DVertexShader; ID3D11PixelShader* mD3DPixelShader; @@ -124,8 +126,8 @@ class DXDrawLayer : public DrawLayer public: virtual DrawBatch* CreateDrawBatch(); virtual RenderCmd* CreateSetTextureCmd(int textureIdx, Texture* texture) override; - virtual void SetShaderConstantData(int slotIdx, void* constData, int size) override; - virtual void SetShaderConstantDataTyped(int slotIdx, void* constData, int size, int* typeData, int typeCount) override; + virtual void SetShaderConstantData(int usageIdx, int slotIdx, void* constData, int size) override; + virtual void SetShaderConstantDataTyped(int usageIdx, int slotIdx, void* constData, int size, int* typeData, int typeCount) override; public: DXDrawLayer(); @@ -203,32 +205,40 @@ public: void IndalidateDepthStencilState(); virtual void SetClipped(bool clipped); + virtual void SetTexWrap(bool clipped); virtual void SetClipRect(const Rect& rect); virtual void SetWriteDepthBuffer(bool writeDepthBuffer); virtual void SetDepthFunc(DepthFunc depthFunc); }; -class DXModelMesh +class DXModelPrimitives { public: + String mMaterialName; int mNumIndices; int mNumVertices; - DXTexture* mTexture; + Array mTextures; ID3D11Buffer* mD3DIndexBuffer; //TODO: Split the vertex buffer up into static and dynamic buffers ID3D11Buffer* mD3DVertexBuffer; public: - DXModelMesh(); - ~DXModelMesh(); + DXModelPrimitives(); + ~DXModelPrimitives(); +}; + +class DXModelMesh +{ +public: + Array mPrimitives; }; class DXModelInstance : public ModelInstance { public: DXRenderDevice* mD3DRenderDevice; - std::vector mDXModelMeshs; + Array mDXModelMeshs; public: DXModelInstance(ModelDef* modelDef); @@ -274,6 +284,7 @@ public: ID3D11DeviceContext* mD3DDeviceContext; ID3D11BlendState* mD3DNormalBlendState; ID3D11SamplerState* mD3DDefaultSamplerState; + ID3D11SamplerState* mD3DWrapSamplerState; bool mHasVSync; ID3D11Buffer* mD3DVertexBuffer; @@ -282,14 +293,16 @@ public: int mIdxByteIdx; HashSet mRenderStates; - HashSet mTextures; - + HashSet mTextures; + Dictionary mTextureMap; + Dictionary mBufferMap; + public: virtual void PhysSetRenderState(RenderState* renderState) override; virtual void PhysSetRenderWindow(RenderWindow* renderWindow); virtual void PhysSetRenderTarget(Texture* renderTarget) override; virtual RenderState* CreateRenderState(RenderState* srcRenderState) override; - virtual ModelInstance* CreateModelInstance(ModelDef* modelDef) override; + virtual ModelInstance* CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags) override; public: DXRenderDevice(); @@ -302,6 +315,7 @@ public: void FrameStart() override; void FrameEnd() override; + Texture* LoadTexture(const StringImpl& fileName, int flags) override; Texture* LoadTexture(ImageData* imageData, int flags) override; Texture* CreateDynTexture(int width, int height) override; Shader* LoadShader(const StringImpl& fileName, VertexDefinition* vertexDefinition) override; diff --git a/BeefySysLib/platform/win/Platform.cpp b/BeefySysLib/platform/win/Platform.cpp index 0d82acc0..e8a91d1e 100644 --- a/BeefySysLib/platform/win/Platform.cpp +++ b/BeefySysLib/platform/win/Platform.cpp @@ -2637,6 +2637,9 @@ BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sy case BfpSysDirectoryKind_Programs_Common: _GetKnownFolder(FOLDERID_CommonPrograms); return; + case BfpSysDirectoryKind_Documents: + _GetKnownFolder(FOLDERID_Documents); + return; } TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult); @@ -2738,9 +2741,13 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK creationDisposition = CREATE_ALWAYS; } else if (createKind == BfpFileCreateKind_CreateIfNotExists) - { + { creationDisposition = CREATE_NEW; } + else if (createKind == BfpFileCreateKind_OpenAlways) + { + creationDisposition = OPEN_ALWAYS; + } else { creationDisposition = OPEN_EXISTING; @@ -2821,6 +2828,11 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResu return bfpFile; } +BFP_EXPORT intptr BFP_CALLTYPE BfpFile_GetSystemHandle(BfpFile* file) +{ + return (intptr)file->mHandle; +} + BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file) { if ((file->mHandle != INVALID_HANDLE_VALUE) && (!file->mIsStd)) @@ -3011,9 +3023,14 @@ BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileS return newPos.QuadPart; } -BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file) +BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult) { - SetEndOfFile(file->mHandle); + if (!SetEndOfFile(file->mHandle)) + { + OUTRESULT(BfpFileResult_UnknownError); + return; + } + OUTRESULT(BfpFileResult_Ok); } BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path) diff --git a/BeefySysLib/platform/win/WinBFApp.cpp b/BeefySysLib/platform/win/WinBFApp.cpp index acb7159b..401912b6 100644 --- a/BeefySysLib/platform/win/WinBFApp.cpp +++ b/BeefySysLib/platform/win/WinBFApp.cpp @@ -76,6 +76,22 @@ static BOOL ClipToMonitor(HMONITOR mon, HDC hdc, LPRECT monRect, LPARAM userArg) return TRUE; } +static BOOL KeyboardLayoutHasAltGr(HKL layout) +{ + BOOL hasAltGr = FALSE; + int scancode; + for (WORD i = 32; i < 256; ++i) + { + scancode = VkKeyScanEx((TCHAR)i, layout); + if ((scancode != -1) && ((scancode & 0x600) == 0x600)) + { + hasAltGr = TRUE; + break; + } + } + return hasAltGr; +} + WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { HINSTANCE hInstance = GetModuleHandle(NULL); @@ -110,9 +126,7 @@ WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y wc.lpfnWndProc = WindowProcStub; wc.lpszClassName = L"BFWindow"; wc.lpszMenuName = NULL; - RegisterClassW(&wc); - - + RegisterClassW(&wc); int requestedX = x; int requestedY = y; @@ -262,7 +276,10 @@ WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y mAlphaMaskPixels = NULL; mAlphaMaskWidth = 0; mAlphaMaskHeight = 0; - mNeedsStateReset = false; + mNeedsStateReset = false; + mAwaitKeyReleases = false; + mAwaitKeyEventTick = 0; + mFocusLostTick = ::GetTickCount(); if (windowFlags & BFWINDOW_DEST_ALPHA) { @@ -281,6 +298,9 @@ WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y BF_ASSERT(winParent->mHWnd); parent->mChildren.push_back(this); } + + HKL layout = GetKeyboardLayout(0); + mKeyLayoutHasAltGr = (KeyboardLayoutHasAltGr(layout) == TRUE); } WinBFWindow::~WinBFWindow() @@ -332,6 +352,8 @@ void WinBFWindow::SetTitle(const char* title) void WinBFWindow::LostFocus(BFWindow* newFocus) { + ///OutputDebugStrF("Lost focus\n"); + mFocusLostTick = ::GetTickCount(); WinBFWindow* bfNewFocus = (WinBFWindow*)newFocus; mSoftHasFocus = false; for (int i = 0; i < KEYCODE_MAX; i++) @@ -354,6 +376,9 @@ void WinBFWindow::LostFocus(BFWindow* newFocus) void WinBFWindow::SetForeground() { + bool hadFocus = mHasFocus; + DWORD prevFocusLostTick = mFocusLostTick; + if (mFlags & BFWINDOW_FAKEFOCUS) { mHasFocus = true; @@ -363,8 +388,13 @@ void WinBFWindow::SetForeground() ::SetFocus(mHWnd); ::SetForegroundWindow(mHWnd); + if ((!hadFocus) && (::GetTickCount() - prevFocusLostTick >= 1000)) + { + mAwaitKeyReleases = true; + mAwaitKeyEventTick = ::GetTickCount(); + } - //OutputDebugStrF("SetForeground %p\n", mHWnd); + //OutputDebugStrF("SetForeground %p %d %d %d\n", mHWnd, hadFocus, ::GetTickCount() - prevFocusLostTick, mAwaitKeyReleases); } static POINT gLastScreenMouseCoords = { -1, -1 }; @@ -388,6 +418,30 @@ void WinBFWindow::RehupMouseOver(bool isMouseOver) } } +bool WinBFWindow::CheckKeyReleases(bool isKeyDown) +{ + if (!mAwaitKeyReleases) + return true; + + // Time expired with no key presses + if ((mAwaitKeyEventTick != 0) && (::GetTickCount() - mAwaitKeyEventTick > 120)) + { + mAwaitKeyReleases = false; + return true; + } + mAwaitKeyEventTick = 0; + + bool hasKeyDown = false; + uint8 keysDown[256] = { 0 }; + ::GetKeyboardState((PBYTE)&keysDown); + for (int i = 0; i < 256; i++) + if (keysDown[i] & 0x80) + hasKeyDown = true; + if (!hasKeyDown) + mAwaitKeyReleases = false; + return !mAwaitKeyReleases; +} + LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WinBFApp* app = (WinBFApp*) gBFApp; @@ -857,6 +911,7 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar //NOTE: This line broke Alt+Gr for braces and such. Determine why this was needed. //if ((!mIsKeyDown[VK_MENU]) && (!mIsKeyDown[VK_CONTROL])) + if (CheckKeyReleases(true)) { for (int i = 0; i < (lParam & 0x7FFF); i++) mKeyCharFunc(this, (WCHAR)wParam); @@ -880,6 +935,9 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (keyCode == VK_APPS) break; // This is handled in WM_CONTEXTMENU + if ((mKeyLayoutHasAltGr) && (keyCode == VK_MENU) && ((lParam & 0x01000000) != 0)) + keyCode = VK_RMENU; + mIsKeyDown[keyCode] = true; for (auto kv : *menuIDMap) @@ -899,7 +957,7 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (!mIsMenuKeyHandled) { - if (mKeyDownFunc(this, (int) wParam, (lParam & 0x7FFF) != 0)) + if ((CheckKeyReleases(true)) && (mKeyDownFunc(this, keyCode, (lParam & 0x7FFF) != 0))) { mIsMenuKeyHandled = true; doResult = true; @@ -918,8 +976,9 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar (aMenu->mKeyShift == mIsKeyDown[VK_SHIFT]) && (aMenu->mKeyCtrl == mIsKeyDown[VK_CONTROL]) && (aMenu->mKeyAlt == mIsKeyDown[VK_MENU])) - { - doResult = true; + { + if (CheckKeyReleases(true)) + doResult = true; break; } } @@ -936,11 +995,14 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar case WM_KEYUP: { int keyCode = (int) wParam; + if ((mKeyLayoutHasAltGr) && (keyCode == VK_MENU) && ((lParam & 0x01000000) != 0)) + keyCode = VK_RMENU; if (mIsKeyDown[keyCode]) { - mKeyUpFunc(this, (int) wParam); + mKeyUpFunc(this, keyCode); mIsKeyDown[keyCode] = false; } + CheckKeyReleases(false); } break; case WM_SYSCOMMAND: @@ -976,7 +1038,7 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if ((!isFocused) && (mHasFocus)) { mSoftHasFocus = false; - mHasFocus = false; + mHasFocus = false; LostFocus(NULL); mLostFocusFunc(this); //OutputDebugStrF("Timer detected lost focus %p\r\n", hWnd); @@ -1023,6 +1085,10 @@ LRESULT WinBFWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (gBFApp->mSysDialogCnt == 0) gBFApp->Process(); break; + + case WM_INPUTLANGCHANGE: + mKeyLayoutHasAltGr = (KeyboardLayoutHasAltGr((HKL)lParam) == TRUE); + break; } app->mInMsgProc = false; diff --git a/BeefySysLib/platform/win/WinBFApp.h b/BeefySysLib/platform/win/WinBFApp.h index ec9b8f3b..c97c42cb 100644 --- a/BeefySysLib/platform/win/WinBFApp.h +++ b/BeefySysLib/platform/win/WinBFApp.h @@ -50,13 +50,17 @@ public: bool mMouseVisible; bool mHasFocus; bool mSoftHasFocus; // Mostly tracks mHasFocus except for when we get an explicit 'LostFocus' callback - + bool mAwaitKeyReleases; + DWORD mAwaitKeyEventTick; + DWORD mFocusLostTick; bool mNeedsStateReset; + bool mKeyLayoutHasAltGr; public: virtual LRESULT WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK WindowProcStub(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); void RehupMouseOver(bool isMouseOver); + bool CheckKeyReleases(bool isKeyDown); public: WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags); diff --git a/BeefySysLib/util/Array.h b/BeefySysLib/util/Array.h index 9a81ed8a..e485023c 100644 --- a/BeefySysLib/util/Array.h +++ b/BeefySysLib/util/Array.h @@ -744,6 +744,14 @@ public: SetBufferSize(this->mAllocSize + this->mAllocSize / 2 + 1); new (&this->mVals[this->mSize++]) T(val); } + + T& GetSafeRef(int idx) + { + BF_ASSERT(idx >= 0); + while (idx >= this->mSize) + this->Add(T()); + return this->mVals[idx]; + } }; // POD @@ -1055,6 +1063,14 @@ public: this->mVals[this->mSize++] = val; } + T& GetSafeRef(int idx) + { + BF_ASSERT(idx >= 0); + while (idx >= this->mSize) + this->Add(T()); + return this->mVals[idx]; + } + T* GrowUninitialized(int addSize) { if (this->mSize + addSize > this->mAllocSize) @@ -1107,6 +1123,46 @@ public: } }; +template +class OwnedArray : public Array +{ +public: + typedef Array _Base; + + ~OwnedArray() + { + for (auto item : *this) + delete item; + } + + void Clear() + { + for (auto item : *this) + delete item; + _Base::Clear(); + } + + void ClearWithoutDeleting() + { + _Base::Clear(); + } + + T* Alloc() + { + T* item = new T(); + _Base::push_back(item); + return item; + } + + template + T2* Alloc() + { + T2* item = new T2(); + _Base::push_back(item); + return item; + } +}; + NS_BF_END; namespace std diff --git a/BeefySysLib/util/Compress.cpp b/BeefySysLib/util/Compress.cpp new file mode 100644 index 00000000..6edb00d7 --- /dev/null +++ b/BeefySysLib/util/Compress.cpp @@ -0,0 +1,125 @@ +#include "Compress.h" +#include "third_party/zlib/zlib.h" +#include "TLSingleton.h" + +#pragma warning(disable:4190) + +USING_NS_BF; + +static TLSingleton> gCompression_TLDataReturn; + +bool Compression::Compress(Span inData, Array& outData) +{ + outData.Reserve(128); + + z_stream zs; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + zs.avail_in = (int)inData.mSize; + zs.next_in = inData.mVals; + zs.next_out = outData.mVals; + zs.avail_out = outData.mAllocSize; + + deflateInit(&zs, Z_BEST_COMPRESSION); + + bool isDone = false; + bool hadError = false; + + while (true) + { + bool isDone = zs.avail_in == 0; + + int err = deflate(&zs, isDone ? Z_FINISH : Z_NO_FLUSH); + outData.mSize = (int)(zs.next_out - outData.mVals); + + if (err < 0) + { + hadError = true; + break; + } + + if ((isDone) && (err == Z_STREAM_END)) + break; + + if (zs.avail_out == 0) + { + outData.Reserve((int)outData.mAllocSize + (int)outData.mAllocSize / 2 + 1); + zs.next_out = outData.mVals + outData.mSize; + zs.avail_out = outData.mAllocSize - outData.mSize; + } + } + + deflateEnd(&zs); + return !hadError; +} + +bool Compression::Decompress(Span inData, Array& outData) +{ + outData.Reserve(128); + + z_stream zs; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + zs.avail_in = (int)inData.mSize; + zs.next_in = inData.mVals; + zs.next_out = outData.mVals; + zs.avail_out = outData.mAllocSize; + + inflateInit(&zs); + + bool isDone = false; + bool hadError = false; + + while (true) + { + bool isDone = zs.avail_in == 0; + + int err = inflate(&zs, isDone ? Z_FINISH : Z_NO_FLUSH); + outData.mSize = (int)(zs.next_out - outData.mVals); + + if (err < 0) + { + hadError = true; + break; + } + + if ((isDone) && (err == Z_STREAM_END)) + break; + + if (zs.avail_out == 0) + { + outData.Reserve((int)outData.mAllocSize + (int)outData.mAllocSize / 2 + 1); + zs.next_out = outData.mVals + outData.mSize; + zs.avail_out = outData.mAllocSize - outData.mSize; + } + } + + inflateEnd(&zs); + return !hadError; +} + +////////////////////////////////////////////////////////////////////////// + +BF_EXPORT bool BF_CALLTYPE Compression_Compress(void* ptr, intptr size, void** outPtr, intptr* outSize) +{ + auto& outData = *gCompression_TLDataReturn.Get(); + outData.Reserve(128); + if (!Compression::Compress(Span((uint8*)ptr, size), outData)) + return false; + *outPtr = outData.mVals; + *outSize = outData.mSize; + return true; +} + +BF_EXPORT bool BF_CALLTYPE Compression_Decompress(void* ptr, intptr size, void** outPtr, intptr* outSize) +{ + auto& outData = *gCompression_TLDataReturn.Get(); + outData.Reserve(128); + if (!Compression::Decompress(Span((uint8*)ptr, size), outData)) + return false; + *outPtr = outData.mVals; + *outSize = outData.mSize; + return true; +} diff --git a/BeefySysLib/util/Compress.h b/BeefySysLib/util/Compress.h new file mode 100644 index 00000000..06785abb --- /dev/null +++ b/BeefySysLib/util/Compress.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../Common.h" +#include "Array.h" +#include "Span.h" + +NS_BF_BEGIN; + +class Compression +{ +public: + static bool Compress(Span inData, Array& outData); + static bool Decompress(Span inData, Array& outData); +}; + +NS_BF_END; diff --git a/BeefySysLib/util/Hash.h b/BeefySysLib/util/Hash.h index 46eb44cb..7353689a 100644 --- a/BeefySysLib/util/Hash.h +++ b/BeefySysLib/util/Hash.h @@ -62,6 +62,14 @@ public: { return StrFormat("%lX%lX", mHigh, mLow); } + + Val128 operator+(int rLow) + { + Val128 result; + result.mLow += mLow + rLow; + result.mHigh = mHigh; + return result; + } }; static bool operator!=(const Val128& l, int rLow) diff --git a/BeefySysLib/util/Heap.cpp b/BeefySysLib/util/Heap.cpp index be7f8228..ca420148 100644 --- a/BeefySysLib/util/Heap.cpp +++ b/BeefySysLib/util/Heap.cpp @@ -1,5 +1,6 @@ #include "Heap.h" #include "DLIList.h" +#include "HashSet.h" USING_NS_BF; @@ -129,46 +130,73 @@ ContiguousHeap::~ContiguousHeap() free(mMetadata); } +//#define BFCE_DEBUG_HEAP + void ContiguousHeap::Clear(int maxAllocSize) { +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF("ContiguousHeap::Clear\n"); +#endif + if (mBlockDataOfs == 0) return; - - mBlockDataOfs = 0; - mFreeList.Clear(); + if ((mMemorySize != -1) && (mMemorySize > maxAllocSize)) - { + { free(mMetadata); mMetadata = NULL; mMemorySize = 0; + mBlockDataOfs = 0; + mFreeList.Clear(); return; } + for (auto idx : mFreeList) + { + auto block = CH_REL_TO_ABS(idx); + block->mKind = (ChBlockKind)0; + } + auto blockList = (ChList*)mMetadata; if (blockList->mHead != -1) { auto block = CH_REL_TO_ABS(blockList->mHead); while (block != NULL) { - block->mKind = ChBlockKind_Bad; + block->mKind = (ChBlockKind)0; if (block->mNext == -1) break; block = CH_REL_TO_ABS(block->mNext); } - } + } + blockList->mHead = -1; - blockList->mTail = -1; + blockList->mTail = -1; + mBlockDataOfs = 0; + mFreeList.Clear(); + + Validate(); } +static int gAllocCount = 0; + ContiguousHeap::AllocRef ContiguousHeap::Alloc(int size) { if (size == 0) return 0; +#ifdef BFCE_DEBUG_HEAP + int allocCount = ++gAllocCount; + if (allocCount == 358) + { + NOP; + } +#endif + size = BF_ALIGN(size, 16); auto blockList = (ChList*)mMetadata; - + if (mFreeIdx >= mFreeList.mSize) mFreeIdx = 0; while (true) @@ -179,11 +207,15 @@ ContiguousHeap::AllocRef ContiguousHeap::Alloc(int size) if (block->mKind == ChBlockKind_Merged) { +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF("ContiguousHeap::Alloc %d removing merged %d\n", allocCount, (uint8*)block - (uint8*)mMetadata); +#endif + itr--; - if (mFreeIdx >= mFreeList.mSize) - mFreeIdx = 0; block->mKind = (ChBlockKind)0; mFreeList.RemoveAtFast(mFreeIdx); + if (mFreeIdx >= mFreeList.mSize) + mFreeIdx = 0; continue; } @@ -194,17 +226,45 @@ ContiguousHeap::AllocRef ContiguousHeap::Alloc(int size) mFreeList.RemoveAtFast(mFreeIdx); if (block->mSize >= size + 64) { + int oldSize = block->mSize; + // Split block - auto newBlock = new ((uint8*)block + size) ChBlock(); + auto newBlock = (ChBlock*)((uint8*)block + size); + if (newBlock->mKind == 0) + { + mFreeList.Add(CH_ABS_TO_REL(newBlock)); + } + else + { + BF_ASSERT(newBlock->mKind == ChBlockKind_Merged); +#ifdef BFCE_DEBUG_HEAP + BF_ASSERT(mFreeList.Contains(CH_ABS_TO_REL(newBlock))); +#endif + } + newBlock->mPrev = -1; + newBlock->mNext = -1; newBlock->mSize = block->mSize - size; - newBlock->mKind = ChBlockKind_Unused; + newBlock->mKind = ChBlockKind_Unused; blockList->AddAfter(CH_ABS_TO_REL(block), CH_ABS_TO_REL(newBlock)); - block->mSize = size; + block->mSize = size; - mFreeList.Add(CH_ABS_TO_REL(newBlock)); +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF("ContiguousHeap::Alloc %d alloc %d size: %d remainder in %d size: %d\n", allocCount, CH_ABS_TO_REL(block), size, CH_ABS_TO_REL(newBlock), newBlock->mSize); +#endif + } + else + { +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF("ContiguousHeap::Alloc %d alloc %d size: %d\n", allocCount, CH_ABS_TO_REL(block), size); +#endif } block->mKind = ChBlockKind_Used; + +#ifdef BFCE_DEBUG_HEAP + Validate(); +#endif + return CH_ABS_TO_REL(block); } @@ -212,28 +272,36 @@ ContiguousHeap::AllocRef ContiguousHeap::Alloc(int size) } int wantSize = BF_MAX(mMemorySize + mMemorySize / 2, mMemorySize + BF_MAX(size, 64 * 1024)); + wantSize = BF_ALIGN(wantSize, 16); mMetadata = realloc(mMetadata, wantSize); + int prevSize = mMemorySize; - memset((uint8*)mMetadata + mMemorySize, 0, wantSize - mMemorySize); + memset((uint8*)mMetadata + prevSize, 0, wantSize - prevSize); blockList = (ChList*)mMetadata; mMemorySize = wantSize; + ChBlock* block; if (mBlockDataOfs == 0) { blockList = new (mMetadata) ChList(); - mBlockDataOfs = sizeof(ChList); + mBlockDataOfs = BF_ALIGN(sizeof(ChList), 16); + prevSize = mBlockDataOfs; } + blockList->mMetadata = mMetadata; - - auto block = new ((uint8*)mMetadata + mBlockDataOfs) ChBlock(); - block->mSize = mMemorySize - mBlockDataOfs; - block->mKind = ChBlockKind_Unused; - mBlockDataOfs += block->mSize; + block = new ((uint8*)mMetadata + prevSize) ChBlock(); + block->mSize = wantSize - prevSize; + block->mKind = ChBlockKind_Unused; blockList->PushBack(CH_ABS_TO_REL(block)); mFreeList.Add(CH_ABS_TO_REL(block)); +#ifdef BFCE_DEBUG_HEAP + Validate(); + OutputDebugStrF("ContiguousHeap::Alloc %d alloc %d size: %d\n", allocCount, (uint8*)block - (uint8*)mMetadata, block->mSize); +#endif + if (mFreeIdx >= mFreeList.mSize) mFreeIdx = 0; } @@ -244,12 +312,30 @@ bool ContiguousHeap::Free(AllocRef ref) if ((ref < 0) || (ref > mMemorySize - sizeof(ChBlock))) return false; +#ifdef BFCE_DEBUG_HEAP + int allocCount = ++gAllocCount; + if (allocCount == 64) + { + NOP; + } +#endif + auto blockList = (ChList*)mMetadata; auto block = CH_REL_TO_ABS(ref); if (block->mKind != ChBlockKind_Used) + { +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF("Invalid free\n"); + Validate(); +#endif return false; + } +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF("ContiguousHeap::Free %d block:%d size: %d\n", allocCount, CH_ABS_TO_REL(block), block->mSize); +#endif + int headAccSize = 0; auto mergeHead = block; while (mergeHead->mPrev != -1) @@ -262,6 +348,9 @@ bool ContiguousHeap::Free(AllocRef ref) mergeHead->mKind = ChBlockKind_Merged; blockList->Remove(CH_ABS_TO_REL(mergeHead)); mergeHead = checkBlock; +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF(" Setting Merged block %d\n", CH_ABS_TO_REL(mergeHead)); +#endif } int tailAccSize = 0; @@ -276,6 +365,9 @@ bool ContiguousHeap::Free(AllocRef ref) tailAccSize += mergeTail->mSize; mergeTail->mKind = ChBlockKind_Merged; blockList->Remove(CH_ABS_TO_REL(mergeTail)); +#ifdef BFCE_DEBUG_HEAP + OutputDebugStrF(" Setting Merged block %d\n", CH_ABS_TO_REL(mergeTail)); +#endif if (nextBlock == NULL) break; mergeTail = nextBlock; @@ -289,6 +381,17 @@ bool ContiguousHeap::Free(AllocRef ref) mFreeList.Add(CH_ABS_TO_REL(mergeHead)); } mergeHead->mKind = ChBlockKind_Unused; + + if (mergeHead != block) + { + // We weren't in the free list so don't mark as Merged + block->mKind = (ChBlockKind)0; + } + +#ifdef BFCE_DEBUG_HEAP + Validate(); +#endif + return true; } @@ -318,7 +421,7 @@ void ContiguousHeap::DebugDump() str += "Merged"; break; default: - str += "??????"; + str += "??????"; } str += "\n"; @@ -334,9 +437,103 @@ void ContiguousHeap::DebugDump() } str += "\nFree List:\n"; - for (auto val : mFreeList) - str += StrFormat("@%d\n", val); + for (auto idx : mFreeList) + { + auto block = CH_REL_TO_ABS(idx); + const char* kind = "??"; + if (block->mKind == ChBlockKind_Unused) + kind = "Unused"; + else + kind = "Merged"; + str += StrFormat("@%d %s\n", idx, kind); + } str += "\n"; OutputDebugStrF(str.c_str()); } + +void ContiguousHeap::Validate() +{ + if (!mFreeList.IsEmpty()) + { + BF_ASSERT_REL(mMetadata != NULL); + } + + HashSet freeSet; + for (auto idx : mFreeList) + freeSet.Add(idx); + + auto blockList = (ChList*)mMetadata; + + bool deepValidate = true; + + if (deepValidate) + { + if (blockList->mHead != -1) + { + int totalSize = 0; + + auto block = CH_REL_TO_ABS(blockList->mHead); + auto blockEnd = (ChBlock*)((uint8*)blockList + mMemorySize); + + while (block != blockEnd) + { + switch (block->mKind) + { + case (ChBlockKind)0: + BF_ASSERT_REL(!freeSet.Contains(CH_ABS_TO_REL(block))); + break; + case ChBlockKind_Unused: + BF_ASSERT_REL(freeSet.Remove(CH_ABS_TO_REL(block))); + break; + case ChBlockKind_Merged: + BF_ASSERT_REL(freeSet.Remove(CH_ABS_TO_REL(block))); + break; + case ChBlockKind_Used: + BF_ASSERT_REL(!freeSet.Contains(CH_ABS_TO_REL(block))); + break; + default: + BF_FATAL("Invalid state"); + } + + block = (ChBlock*)((uint8*)block + 16); + } + BF_ASSERT_REL(freeSet.IsEmpty()); + } + } + else + { + if (blockList->mHead != -1) + { + int totalSize = 0; + + auto block = CH_REL_TO_ABS(blockList->mHead); + while (block != NULL) + { + switch (block->mKind) + { + case ChBlockKind_Unused: + BF_ASSERT_REL(freeSet.Remove(CH_ABS_TO_REL(block))); + break; + case ChBlockKind_Used: + BF_ASSERT_REL(!freeSet.Contains(CH_ABS_TO_REL(block))); + break; + default: + BF_FATAL("Invalid state"); + } + + if (block->mNext == -1) + break; + block = CH_REL_TO_ABS(block->mNext); + } + } + } + + for (auto idx : freeSet) + { + auto block = CH_REL_TO_ABS(idx); + BF_ASSERT_REL(block->mKind == ChBlockKind_Merged); + } + + //BF_ASSERT(freeSet.IsEmpty()); +} diff --git a/BeefySysLib/util/Heap.h b/BeefySysLib/util/Heap.h index d1a8cf57..929783cc 100644 --- a/BeefySysLib/util/Heap.h +++ b/BeefySysLib/util/Heap.h @@ -26,6 +26,7 @@ public: AllocRef Alloc(int size); bool Free(AllocRef ref); + void Validate(); void DebugDump(); }; diff --git a/BeefySysLib/util/MathUtils.cpp b/BeefySysLib/util/MathUtils.cpp new file mode 100644 index 00000000..c5399ebd --- /dev/null +++ b/BeefySysLib/util/MathUtils.cpp @@ -0,0 +1,145 @@ +#include "MathUtils.h" +#include "Sphere.h" + +USING_NS_BF; + +bool Beefy::RayIntersectsTriangle(const Vector3& rayOrigin, const Vector3& rayVector, const Vector3& vtx0, const Vector3& vtx1, const Vector3& vtx2, Vector3* outIntersectionPoint, float* outDistance) +{ + const float EPSILON = 0.0000001f; + Vector3 edge1, edge2, h, s, q; + float a, f, u, v; + edge1 = vtx1 - vtx0; + edge2 = vtx2 - vtx0; + + h = Vector3::CrossProduct(rayVector, edge2);//rayVector.crossProduct(edge2); + a = Vector3::Dot(edge1, h); //edge1.dotProduct(h); + if (a > -EPSILON && a < EPSILON) + return false; // This ray is parallel to this triangle. + f = 1.0f / a; + s = rayOrigin - vtx0; + u = f * Vector3::Dot(s, h); //s.dotProduct(h); + if (u < 0.0 || u > 1.0) + return false; + q = Vector3::CrossProduct(s, edge1); //s.crossProduct(edge1); + v = f * Vector3::Dot(rayVector, q); //rayVector.dotProduct(q); + if (v < 0.0 || u + v > 1.0) + return false; + // At this stage we can compute t to find out where the intersection point is on the line. + float distance = f * Vector3::Dot(edge2, q); //edge2.dotProduct(q); + if (distance > EPSILON) // ray intersection + { + if (outIntersectionPoint != NULL) + *outIntersectionPoint = rayOrigin + rayVector * distance; + if (outDistance != NULL) + *outDistance = distance; + return true; + } + else // This means that there is a line intersection but not a ray intersection. + return false; +} + +bool Beefy::RayIntersectsCircle(const Vector3& rayOrigin, const Vector3& rayVector, const Sphere& sphere, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance) +{ + Vector3 e = sphere.mCenter - rayOrigin; + float rSq = sphere.mRadius * sphere.mRadius; + + float eSq = e.GetMagnitudeSquare(); + float a = Vector3::Dot(e, rayVector); // ray.direction is assumed to be normalized + float bSq = /*sqrtf(*/eSq - (a * a)/*)*/; + float f = sqrtf(fabsf((rSq)- /*(b * b)*/bSq)); + + // Assume normal intersection! + float t = a - f; + + // No collision has happened + if (rSq - (eSq - a * a) < 0.0f) + { + return false; + } + // Ray starts inside the sphere + else if (eSq < rSq) + { + // Just reverse direction + t = a + f; + } + + if (outDistance != NULL) + *outDistance = t; + if ((outIntersectionPoint != NULL) || (outNormal != NULL)) + { + Vector3 intersect = rayOrigin + rayVector * t; + if (outIntersectionPoint != NULL) + *outIntersectionPoint = intersect; + if (outNormal != NULL) + *outNormal = Vector3::Normalize(intersect - sphere.mCenter); + } + + return true; +} + +bool Beefy::RayIntersectsAABB(const Vector3& rayOrigin, const Vector3& rayVector, const AABB& aabb, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance) +{ + const Vector3& min = aabb.mMin; + const Vector3& max = aabb.mMax; + + // Any component of direction could be 0! + // Address this by using a small number, close to + // 0 in case any of directions components are 0 + float t1 = (min.mX - rayOrigin.mX) / ((fabs(rayVector.mX) < 0.00001f) ? 0.00001f : rayVector.mX); + float t2 = (max.mX - rayOrigin.mX) / ((fabs(rayVector.mX) < 0.00001f) ? 0.00001f : rayVector.mX); + float t3 = (min.mY - rayOrigin.mY) / ((fabs(rayVector.mY) < 0.00001f) ? 0.00001f : rayVector.mY); + float t4 = (max.mY - rayOrigin.mY) / ((fabs(rayVector.mY) < 0.00001f) ? 0.00001f : rayVector.mY); + float t5 = (min.mZ - rayOrigin.mZ) / ((fabs(rayVector.mZ) < 0.00001f) ? 0.00001f : rayVector.mZ); + float t6 = (max.mZ - rayOrigin.mZ) / ((fabs(rayVector.mZ) < 0.00001f) ? 0.00001f : rayVector.mZ); + + float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6)); + float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6)); + + // if tmax < 0, ray is intersecting AABB + // but entire AABB is being it's origin + if (tmax < 0) { + return false; + } + + // if tmin > tmax, ray doesn't intersect AABB + if (tmin > tmax) { + return false; + } + + float t_result = tmin; + + // If tmin is < 0, tmax is closer + if (tmin < 0.0f) { + t_result = tmax; + } + + if ((outIntersectionPoint != NULL) || (outDistance != NULL)) + { + Vector3 hitPoint = rayOrigin + rayVector * t_result; + if (outIntersectionPoint != NULL) + *outIntersectionPoint = hitPoint; + if (outDistance != NULL) + *outDistance = (rayOrigin - hitPoint).GetMagnitude(); + } + + if (outNormal != NULL) + { + static Vector3 normals[] = { + Vector3(-1, 0, 0), + Vector3(1, 0, 0), + Vector3(0, -1, 0), + Vector3(0, 1, 0), + Vector3(0, 0, -1), + Vector3(0, 0, 1) + }; + float t[] = { t1, t2, t3, t4, t5, t6 }; + + for (int i = 0; i < 6; ++i) + { + if (t_result == t[i]) + *outNormal = normals[i]; + } + } + + return true; +} diff --git a/BeefySysLib/util/MathUtils.h b/BeefySysLib/util/MathUtils.h new file mode 100644 index 00000000..fa3911be --- /dev/null +++ b/BeefySysLib/util/MathUtils.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Vector.h" +#include "Matrix4.h" + +NS_BF_BEGIN + +class AABB +{ +public: + Vector3 mMin; + Vector3 mMax; +}; + +class Sphere; + +bool RayIntersectsTriangle(const Vector3& rayOrigin, const Vector3& rayVector, const Vector3& vtx0, const Vector3& vtx1, const Vector3& vtx2, Vector3* outIntersectionPoint, float* distance); +bool RayIntersectsCircle(const Vector3& rayOrigin, const Vector3& rayVector, const Sphere& sphere, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance); +bool RayIntersectsAABB(const Vector3& rayOrigin, const Vector3& rayVector, const AABB& aabb, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance); + +NS_BF_END \ No newline at end of file diff --git a/BeefySysLib/util/Matrix4.h b/BeefySysLib/util/Matrix4.h index 5d9096d3..ce9844f2 100644 --- a/BeefySysLib/util/Matrix4.h +++ b/BeefySysLib/util/Matrix4.h @@ -109,6 +109,15 @@ public: } static Matrix4 CreateTransform(const Vector3& position, const Vector3& scale, const Quaternion& orientation); + + static float Determinant(float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33) + { + return m11 * (m22 * m33 - m32 * m23) - + m21 * (m12 * m33 - m32 * m13) + + m31 * (m12 * m23 - m22 * m13); + } }; NS_BF_END; diff --git a/BeefySysLib/util/Quaternion.cpp b/BeefySysLib/util/Quaternion.cpp index 56185450..8216f8fb 100644 --- a/BeefySysLib/util/Quaternion.cpp +++ b/BeefySysLib/util/Quaternion.cpp @@ -26,11 +26,11 @@ Beefy::Quaternion Beefy::Quaternion::Slerp(float fT, const Quaternion& rkP, cons if ((fabs(fCos) < 1.0f - BF_MS_EPSILON) && (false)) { // Standard case (slerp) - float fSin = sqrt(1.0f - (fCos * fCos)); - float fAngle = atan2(fSin, fCos); + float fSin = sqrtf(1.0f - (fCos * fCos)); + float fAngle = atan2f(fSin, fCos); float fInvSin = 1.0f / fSin; - float fCoeff0 = sin((1.0f - fT) * fAngle) * fInvSin; - float fCoeff1 = sin(fT * fAngle) * fInvSin; + float fCoeff0 = sinf((1.0f - fT) * fAngle) * fInvSin; + float fCoeff1 = sinf(fT * fAngle) * fInvSin; return fCoeff0 * rkP + fCoeff1 * rkT; } else diff --git a/BeefySysLib/util/Quaternion.h b/BeefySysLib/util/Quaternion.h index 6d06bf54..3c42bf7f 100644 --- a/BeefySysLib/util/Quaternion.h +++ b/BeefySysLib/util/Quaternion.h @@ -114,7 +114,7 @@ public: static Quaternion Normalise(const Quaternion& quat) { float len = quat.Norm(); - float factor = 1.0f / sqrt(len); + float factor = 1.0f / sqrtf(len); return quat * factor; } }; diff --git a/BeefySysLib/util/Sphere.cpp b/BeefySysLib/util/Sphere.cpp new file mode 100644 index 00000000..fc939717 --- /dev/null +++ b/BeefySysLib/util/Sphere.cpp @@ -0,0 +1,195 @@ +#include "Sphere.h" +#include "Matrix4.h" + +USING_NS_BF; + +static const float radiusEpsilon = 1e-4f; // NOTE: To avoid numerical inaccuracies + +Sphere::Sphere() +{ + mRadius = -1; +} + +Sphere::Sphere(const Sphere& S) +{ + mRadius = S.mRadius; + mCenter = S.mCenter; +} + +Sphere::Sphere(const Vector3& O) +{ + mRadius = 0 + radiusEpsilon; + mCenter = O; +} + +Sphere::Sphere(const Vector3& O, float R) +{ + mRadius = R; + mCenter = O; +} + +Sphere::Sphere(const Vector3& O, const Vector3& A) +{ + Vector3 a = A - O; + + Vector3 o = a * 0.5f; + + mRadius = o.GetMagnitude() + radiusEpsilon; + mCenter = O + o; +} + +Sphere::Sphere(const Vector3& O, const Vector3& A, const Vector3& B) +{ + Vector3 a = A - O; + Vector3 b = B - O; + + Vector3 axb = Vector3::CrossProduct(a, b); + + float Denominator = 2.0f * Vector3::Dot(axb, axb); + + Vector3 o = + ((Vector3::CrossProduct(axb, a) * Vector3::Dot(b, b)) + + (Vector3::CrossProduct(b, axb) * Vector3::Dot(a, a))) / Denominator; + + mRadius = o.GetMagnitude() + radiusEpsilon; + mCenter = O + o; +} + +Sphere::Sphere(const Vector3& O, const Vector3& A, const Vector3& B, const Vector3& C) +{ + Vector3 a = A - O; + Vector3 b = B - O; + Vector3 c = C - O; + + float Denominator = 2.0f * Matrix4::Determinant( + a.mX, a.mY, a.mZ, + b.mX, b.mY, b.mZ, + c.mX, c.mY, c.mZ); + + Vector3 o = ( + (Vector3::CrossProduct(a, b) * Vector3::Dot(c, c)) + + (Vector3::CrossProduct(c, a) * Vector3::Dot(b, b)) + + (Vector3::CrossProduct(b, c) * Vector3::Dot(a, a))) / Denominator; + + mRadius = o.GetMagnitude() + radiusEpsilon; + mCenter = O + o; +} + +Sphere& Sphere::operator=(const Sphere& S) +{ + mRadius = S.mRadius; + mCenter = S.mCenter; + + return *this; +} + +float Sphere::GetDistance(const Vector3& P) const +{ + return Vector3::GetDistance(P, mCenter) - mRadius; +} + +float Sphere::GetDistanceSquare(const Vector3& P) const +{ + return Vector3::GetDistanceSquare(P, mCenter) - mRadius * mRadius; +} + +float Sphere::GetDistance(const Sphere& S, const Vector3& P) +{ + return Vector3::GetDistance(P, S.mCenter) - S.mRadius; +} + +float Sphere::GetDistance(const Vector3& P, const Sphere& S) +{ + return Vector3::GetDistance(P, S.mCenter) - S.mRadius; +} + +float Sphere::GetDistanceSquare(const Sphere& S, const Vector3& P) +{ + return Vector3::GetDistanceSquare(P, S.mCenter) - S.mRadius * S.mRadius; +} + +float Sphere::GetDistanceSquare(const Vector3& P, const Sphere& S) +{ + return Vector3::GetDistanceSquare(P, S.mCenter) - S.mRadius * S.mRadius; +} + +Sphere Sphere::RecurseMini(Vector3* P[], int p, int b) +{ + Sphere MB; + + switch (b) + { + case 0: + MB = Sphere(); + break; + case 1: + MB = Sphere(*P[-1]); + break; + case 2: + MB = Sphere(*P[-1], *P[-2]); + break; + case 3: + MB = Sphere(*P[-1], *P[-2], *P[-3]); + break; + case 4: + MB = Sphere(*P[-1], *P[-2], *P[-3], *P[-4]); + return MB; + } + + for (int i = 0; i < p; i++) + { + if (MB.GetDistanceSquare(*P[i]) > 0) // Signed square distance to sphere + { + for (int j = i; j > 0; j--) + { + Vector3* T = P[j]; + P[j] = P[j - 1]; + P[j - 1] = T; + } + + MB = RecurseMini(P + 1, i, b + 1); + } + } + + return MB; +} + +Sphere Sphere::MiniBall(Vector3 P[], int p) +{ + Vector3** L = new Vector3* [p]; + + for (int i = 0; i < p; i++) + L[i] = &P[i]; + + Sphere MB = RecurseMini(L, p); + + delete[] L; + + return MB; +} + +Sphere Sphere::SmallBall(Vector3 P[], int p) +{ + Vector3 mCenter; + float mRadius = -1; + + if (p > 0) + { + for (int i = 0; i < p; i++) + mCenter += P[i]; + + mCenter /= (float)p; + + for (int i = 0; i < p; i++) + { + float d2 = Vector3::GetDistanceSquare(P[i], mCenter); + + if (d2 > mRadius) + mRadius = d2; + } + + mRadius = sqrtf(mRadius) + radiusEpsilon; + } + + return Sphere(mCenter, mRadius); +} diff --git a/BeefySysLib/util/Sphere.h b/BeefySysLib/util/Sphere.h new file mode 100644 index 00000000..95c3e3e0 --- /dev/null +++ b/BeefySysLib/util/Sphere.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Common.h" +#include "Vector.h" + +NS_BF_BEGIN + +class Vector3; +class Matrix4; + +class Sphere +{ +public: + Vector3 mCenter; + float mRadius; + + Sphere(); + Sphere(const Sphere& X); + Sphere(const Vector3& O); // Point-Sphere + Sphere(const Vector3& O, float R); // Center and radius (not squared) + Sphere(const Vector3& O, const Vector3& A); // Sphere through two points + Sphere(const Vector3& O, const Vector3& A, const Vector3& B); // Sphere through three points + Sphere(const Vector3& O, const Vector3& A, const Vector3& B, const Vector3& C); // Sphere through four points + + Sphere& operator=(const Sphere& S); + + float GetDistance(const Vector3& P) const; // Distance from p to boundary of the Sphere + float GetDistanceSquare(const Vector3& P) const; // Square distance from p to boundary of the Sphere + + static float GetDistance(const Sphere& S, const Vector3& P); // Distance from p to boundary of the Sphere + static float GetDistance(const Vector3& P, const Sphere& S); // Distance from p to boundary of the Sphere + + static float GetDistanceSquare(const Sphere& S, const Vector3& P); // Square distance from p to boundary of the Sphere + static float GetDistanceSquare(const Vector3& P, const Sphere& S); // Square distance from p to boundary of the Sphere + + static Sphere MiniBall(Vector3 P[], int p); // Smallest enclosing sphere + static Sphere SmallBall(Vector3 P[], int p); // Enclosing sphere approximation + +private: + static Sphere RecurseMini(Vector3* P[], int p, int b = 0); +}; + +NS_BF_END diff --git a/BeefySysLib/util/String.cpp b/BeefySysLib/util/String.cpp index 0e1c41d3..a64766bc 100644 --- a/BeefySysLib/util/String.cpp +++ b/BeefySysLib/util/String.cpp @@ -690,6 +690,16 @@ void StringImpl::ReplaceLargerHelper(const StringView& find, const StringView& r mLength = (int_strsize)destLength; } +void StringImpl::Replace(char find, char replace) +{ + auto ptr = GetMutablePtr(); + for (int i = 0; i < mLength; i++) + { + if (ptr[i] == find) + ptr[i] = replace; + } +} + void StringImpl::Replace(const StringView& find, const StringView & replace) { if (replace.mLength > find.mLength) diff --git a/BeefySysLib/util/String.h b/BeefySysLib/util/String.h index f86cf312..3d7d8299 100644 --- a/BeefySysLib/util/String.h +++ b/BeefySysLib/util/String.h @@ -182,6 +182,18 @@ public: this->mPtr = sv.mPtr; this->mLength = sv.mLength; } + + StringView(const StringView& sv, int offset) + { + this->mPtr = sv.mPtr + offset; + this->mLength = sv.mLength - offset; + } + + StringView(const StringView& sv, int offset, int length) + { + this->mPtr = sv.mPtr + offset; + this->mLength = length; + } StringView(const StringImpl& str); StringView(const StringImpl& str, int offset); @@ -908,6 +920,15 @@ public: void Append(const StringImpl& str, const StringImpl& str2, const StringImpl& str3); void Append(char c, int count = 1); + void Release() + { + if (IsDynAlloc()) + DeletePtr(); + this->mLength = 0; + this->mPtr = NULL; + this->mAllocSizeAndFlags = 0; + } + void Clear() { this->mLength = 0; @@ -994,6 +1015,7 @@ public: } void ReplaceLargerHelper(const StringView& find, const StringView& replace); + void Replace(char find, char replace); void Replace(const StringView& find, const StringView& replace); void TrimEnd(); void TrimStart(); diff --git a/BeefySysLib/util/Vector.cpp b/BeefySysLib/util/Vector.cpp index 042d371f..5987ee35 100644 --- a/BeefySysLib/util/Vector.cpp +++ b/BeefySysLib/util/Vector.cpp @@ -4,6 +4,13 @@ USING_NS_BF; +Vector3::Vector3() +{ + mX = 0; + mY = 0; + mZ = 0; +} + Vector3::Vector3(float x, float y, float z) { mX = x; @@ -13,7 +20,12 @@ Vector3::Vector3(float x, float y, float z) float Vector3::GetMagnitude() const { - return sqrt(mX*mX + mY*mY + mZ*mZ); + return sqrtf(mX*mX + mY*mY + mZ*mZ); +} + +float Vector3::GetMagnitudeSquare() const +{ + return mX * mX + mY * mY + mZ * mZ; } Vector3 Vector3::Normalize(const Vector3& vec) @@ -39,6 +51,14 @@ Vector3 Vector3::CrossProduct(const Vector3& vec1, const Vector3& vec2) } Vector3 Vector3::Transform(const Vector3& vec, const Matrix4& matrix) +{ + return Vector3( + (matrix.m00 * vec.mX + matrix.m01 * vec.mY + matrix.m02 * vec.mZ + matrix.m03), + (matrix.m10 * vec.mX + matrix.m11 * vec.mY + matrix.m12 * vec.mZ + matrix.m13), + (matrix.m20 * vec.mX + matrix.m21 * vec.mY + matrix.m22 * vec.mZ + matrix.m23)); +} + +Vector3 Vector3::TransformW(const Vector3& vec, const Matrix4& matrix) { float fInvW = 1.0f / (matrix.m30 * vec.mX + matrix.m31 * vec.mY + matrix.m32 * vec.mZ + matrix.m33); @@ -75,4 +95,14 @@ Vector3 Vector3::Transform2(const Vector3& vec, const Quaternion& quat) result.mZ = vec.mZ + z * quat.mW + (quat.mX * y - quat.mY * x); return result; +} + +/// + +Vector4::Vector4(float x, float y, float z, float w) +{ + mX = x; + mY = y; + mZ = z; + mW = w; } \ No newline at end of file diff --git a/BeefySysLib/util/Vector.h b/BeefySysLib/util/Vector.h index cbe06428..200cac2c 100644 --- a/BeefySysLib/util/Vector.h +++ b/BeefySysLib/util/Vector.h @@ -36,13 +36,25 @@ public: float mZ; public: - Vector3(float x = 0, float y = 0, float z = 0); + Vector3(); + Vector3(float x, float y, float z); float GetMagnitude() const; + float GetMagnitudeSquare() const; static Vector3 Normalize(const Vector3& vec); static float Dot(const Vector3& vec1, const Vector3& vec2); static Vector3 CrossProduct(const Vector3& vec1, const Vector3& vec2); + static float GetDistance(const Vector3& v0, const Vector3& v1) + { + return (v0 - v1).GetMagnitude(); + } + + static float GetDistanceSquare(const Vector3& v0, const Vector3& v1) + { + return (v0 - v1).GetMagnitudeSquare(); + } + bool operator==(const Vector3& check) const { return (mX == check.mX) && (mY == check.mY) && (mZ == check.mZ); @@ -54,8 +66,9 @@ public: } static Vector3 Transform(const Vector3& vec, const Matrix4& matrix); + static Vector3 TransformW(const Vector3& vec, const Matrix4& matrix); static Vector3 Transform(const Vector3& vec, const Quaternion& quat); - static Vector3 Transform2(const Vector3& vec, const Quaternion& quat); + static Vector3 Transform2(const Vector3& vec, const Quaternion& quat); static Vector3 Scale(const Vector3& vec, float scale) { @@ -77,6 +90,24 @@ public: return Vector3(mX * scale, mY * scale, mZ * scale); } + Vector3 operator /(float scale) const + { + return Vector3(mX / scale, mY / scale, mZ / scale); + } + + float operator^(const Vector3& v) // Angle between vectors + { + return acosf(Dot(*this / this->GetMagnitude(), v / v.GetMagnitude())); + } + + inline Vector3& operator += (const Vector3& vec) + { + mX += vec.mX; + mY += vec.mY; + mZ += vec.mZ; + return *this; + } + inline Vector3& operator -= (const Vector3& vec) { mX -= vec.mX; @@ -85,6 +116,40 @@ public: return *this; } + inline Vector3& operator *= (float num) + { + mX *= num; + mY *= num; + mZ *= num; + return *this; + } + + inline Vector3& operator /= (float num) + { + mX /= num; + mY /= num; + mZ /= num; + return *this; + } + + inline Vector3 operator - (const Vector3& vec) const + { + Vector3 result; + result.mX = mX - vec.mX; + result.mY = mY - vec.mY; + result.mZ = mZ - vec.mZ; + return result; + } + + inline Vector3 operator + (const Vector3& vec) + { + Vector3 result; + result.mX = mX + vec.mX; + result.mY = mY + vec.mY; + result.mZ = mZ + vec.mZ; + return result; + } + inline Vector3& operator *= (const Vector3& vec) { mX *= vec.mX; @@ -94,4 +159,64 @@ public: } }; +class Vector4 +{ +public: + float mX; + float mY; + float mZ; + float mW; + +public: + Vector4(float x = 0, float y = 0, float z = 0, float w = 0); + + bool operator==(const Vector4& check) const + { + return (mX == check.mX) && (mY == check.mY) && (mZ == check.mZ); + } + + bool operator!=(const Vector4& check) const + { + return (mX != check.mX) || (mY != check.mY) || (mZ != check.mZ); + } + + static Vector4 Scale(const Vector4& vec, float scale) + { + return Vector4(vec.mX * scale, vec.mY * scale, vec.mZ * scale, vec.mW * scale); + } + + Vector4 operator +(const Vector4& v2) const + { + return Vector4(mX + v2.mX, mY + v2.mY, mZ + v2.mZ, mW + v2.mW); + } + + Vector4 operator *(const Vector4& v2) const + { + return Vector4(mX * v2.mX, mY * v2.mY, mZ * v2.mZ, mW * v2.mW); + } + + Vector4 operator *(float scale) const + { + return Vector4(mX * scale, mY * scale, mZ * scale, mW * scale); + } + + inline Vector4& operator -= (const Vector4& vec) + { + mX -= vec.mX; + mY -= vec.mY; + mZ -= vec.mZ; + mW -= vec.mW; + return *this; + } + + inline Vector4& operator *= (const Vector4& vec) + { + mX *= vec.mX; + mY *= vec.mY; + mZ *= vec.mZ; + mW *= vec.mW; + return *this; + } +}; + NS_BF_END; diff --git a/IDE/BeefProj.toml b/IDE/BeefProj.toml index 0db6b480..568bb44c 100644 --- a/IDE/BeefProj.toml +++ b/IDE/BeefProj.toml @@ -13,7 +13,7 @@ Description = "Beef IDE" Company = "BeefyTech LLC" Product = "Beef IDE" Copyright = "Copyright 2019 BeefyTech" -FileVersion = "0.43.1" +FileVersion = "0.43.2" ProductVersion = "0000000000000000" [Configs.Debug.Win32] @@ -48,7 +48,7 @@ OtherLinkFlags = "" TargetDirectory = "$(WorkspaceDir)/dist" TargetName = "BeefIDE_d2" OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib BeefySysLib64_d.lib wsock32.lib" -DebugCommandArguments = "-workspace=C:\\proj\\BeefTest" +DebugCommandArguments = "-workspace=c:\\proj\\BeefTest" DebugWorkingDirectory = "$(ProjectDir)\\dist" EnvironmentVars = ["_NO_DEBUG_HEAP=1"] diff --git a/IDE/Tests/BugW007/BeefProj.toml b/IDE/Tests/BugW007/BeefProj.toml new file mode 100644 index 00000000..f0c46edc --- /dev/null +++ b/IDE/Tests/BugW007/BeefProj.toml @@ -0,0 +1,5 @@ +FileVersion = 1 + +[Project] +Name = "Bug" +StartupObject = "Bug.Program" diff --git a/IDE/Tests/BugW007/BeefSpace.toml b/IDE/Tests/BugW007/BeefSpace.toml new file mode 100644 index 00000000..c389207f --- /dev/null +++ b/IDE/Tests/BugW007/BeefSpace.toml @@ -0,0 +1,6 @@ +FileVersion = 1 +Projects = {Bug = {Path = "."}} + +[Workspace] +StartupProject = "Bug" + diff --git a/IDE/Tests/BugW007/scripts/Test.txt b/IDE/Tests/BugW007/scripts/Test.txt new file mode 100644 index 00000000..6d91cf86 --- /dev/null +++ b/IDE/Tests/BugW007/scripts/Test.txt @@ -0,0 +1,13 @@ +# This tests that renaming namespace properly deletes types + +ShowFile("src/Program.bf") + +Compile() + +Sleep(1000) +GotoText("//Test") +AdjustCursor(-1, 0) +InsertText("2") +Sleep(1000) + +Compile() \ No newline at end of file diff --git a/IDE/Tests/BugW007/src/Program.bf b/IDE/Tests/BugW007/src/Program.bf new file mode 100644 index 00000000..2c27d90a --- /dev/null +++ b/IDE/Tests/BugW007/src/Program.bf @@ -0,0 +1,29 @@ +#pragma warning disable 168 + +using System; +using System.Collections; + +namespace Bug //Test +{ + struct Zonkle + { + int mA; + } + + class Zorp + { + Dictionary mDict; + } +} + +namespace Bug +{ + + class Program + { + public static int Main(String[] args) + { + return 0; + } + } +} diff --git a/IDE/Tests/CompileFail001/src/Cases.bf b/IDE/Tests/CompileFail001/src/Cases.bf index 786aa0b6..4104590b 100644 --- a/IDE/Tests/CompileFail001/src/Cases.bf +++ b/IDE/Tests/CompileFail001/src/Cases.bf @@ -21,5 +21,29 @@ namespace IDETest int a = val1; //FAIL } } + + int Switch1(Result res) + { + switch (res) + { + case .Ok(let a): + fallthrough; + case .Err(let b): //FAIL + return 1; + } + } + + int Switch2(Result res) + { + switch (res) + { + case .Ok(let a): + if (a > 0) + break; + fallthrough; + case .Err: + return 1; + } + } //FAIL } } diff --git a/IDE/Tests/CompileFail001/src/Generics.bf b/IDE/Tests/CompileFail001/src/Generics.bf index d896a9fc..8a27fbf4 100644 --- a/IDE/Tests/CompileFail001/src/Generics.bf +++ b/IDE/Tests/CompileFail001/src/Generics.bf @@ -85,5 +85,23 @@ namespace IDETest { } + + public static void TestGen(T val) + where T : IEnumerator + where TItem : var + { + Console.WriteLine(typeof(decltype(val)).ToString(.. scope .())); + } + + public static void TestPreGen() + { + T a = default; + TestGen(a); //FAIL Unable to determine generic argument 'TItem' + } + + public static void TestGenBug() + { + TestPreGen>(); + } } } diff --git a/IDE/Tests/CompileFail001/src/Methods.bf b/IDE/Tests/CompileFail001/src/Methods.bf index 2794fa2b..51c2505d 100644 --- a/IDE/Tests/CompileFail001/src/Methods.bf +++ b/IDE/Tests/CompileFail001/src/Methods.bf @@ -1,4 +1,5 @@ using System; +using System.Collections; namespace IDETest { @@ -55,11 +56,39 @@ namespace IDETest } + public static mixin MixinA() + { + MixinA!(); //FAIL + } + + public static mixin MixinB(T val) where T : var + { + } + + public static mixin MixinB(T val) where T : List + { + MixinB!(val); + } + + public static mixin MixinC(T val) where T : var + { + } + + public static mixin MixinC(T val) where T : List + { + if (!val.IsEmpty) + MixinC!(val.Front); + } + public static void Test() { ClassB cb = scope .(); MethodA(cb, 123); //FAIL MethodB(cb, 234); //FAIL + + List> list = scope .(); + MixinB!(list); //FAIL + MixinC!(list); } } } diff --git a/IDE/mintest/minlib/src/System/Compiler.bf b/IDE/mintest/minlib/src/System/Compiler.bf index be3e3bdd..2c559e2c 100644 --- a/IDE/mintest/minlib/src/System/Compiler.bf +++ b/IDE/mintest/minlib/src/System/Compiler.bf @@ -2,6 +2,12 @@ namespace System { class Compiler { + public static class Options + { + [LinkName("#AllocStackCount")] + public static extern int32 AllocStackCount; + } + [LinkName("#CallerLineNum")] public static extern int CallerLineNum; @@ -21,7 +27,7 @@ namespace System public static extern String CallerProject; [LinkName("#CallerExpression")] - public static extern String[Int32.MaxValue] CallerExpression; + public static extern String[0x0FFFFFFF] CallerExpression; [LinkName("#ProjectName")] public static extern String ProjectName; diff --git a/IDE/mintest/minlib/src/System/Object.bf b/IDE/mintest/minlib/src/System/Object.bf index 1c422a4c..eb70801f 100644 --- a/IDE/mintest/minlib/src/System/Object.bf +++ b/IDE/mintest/minlib/src/System/Object.bf @@ -10,6 +10,11 @@ namespace System void Dispose() mut; } + interface IInteger + { + + } + interface IPrintable { void Print(String outString); diff --git a/IDE/mintest/minlib/src/System/Platform.bf b/IDE/mintest/minlib/src/System/Platform.bf index c4259103..35a72d33 100644 --- a/IDE/mintest/minlib/src/System/Platform.bf +++ b/IDE/mintest/minlib/src/System/Platform.bf @@ -234,6 +234,7 @@ namespace System CreateAlways, CreateIfNotExists, OpenExisting, + OpenAlways, }; public enum BfpFileCreateFlags : int32 @@ -303,7 +304,7 @@ namespace System [CallingConvention(.Stdcall), CLink] public static extern int64 BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind); [CallingConvention(.Stdcall), CLink] - public static extern void BfpFile_Truncate(BfpFile* file); + public static extern void BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult); [CallingConvention(.Stdcall), CLink] public static extern BfpTimeStamp BfpFile_GetTime_LastWrite(char8* path); [CallingConvention(.Stdcall), CLink] diff --git a/IDE/mintest/minlib/src/System/Range.bf b/IDE/mintest/minlib/src/System/Range.bf new file mode 100644 index 00000000..837688e6 --- /dev/null +++ b/IDE/mintest/minlib/src/System/Range.bf @@ -0,0 +1,89 @@ +using System.Collections; +using System.Diagnostics; + +namespace System +{ + enum Index + { + case FromFront(int offset); + case FromEnd(int offset); + } + + struct IndexRange + { + public Index mStart; + public Index mEnd; + public bool mIsClosed; + + public this() + { + this = default; + } + + public this(Index start, Index end, bool isClosed=true) + { + mStart = start; + mEnd = end; + mIsClosed = isClosed; + } + + public this(int start, Index end, bool isClosed=true) + { + mStart = .FromFront(start); + mEnd = end; + mIsClosed = isClosed; + } + + public this(Index start, int end, bool isClosed=true) + { + mStart = start; + mEnd = .FromFront(end); + mIsClosed = isClosed; + } + + public this(int start, int end, bool isClosed=true) + { + mStart = .FromFront(start); + mEnd = .FromFront(end); + mIsClosed = isClosed; + } + } + + struct Range + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + } + + struct ClosedRange + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + } +} diff --git a/IDE/mintest/minlib/src/System/String.bf b/IDE/mintest/minlib/src/System/String.bf index 50d14c5c..60019c59 100644 --- a/IDE/mintest/minlib/src/System/String.bf +++ b/IDE/mintest/minlib/src/System/String.bf @@ -716,7 +716,7 @@ namespace System ++inIdx; ++outIdx; } - else // We need to physically move char8acters once we've found an equal span + else // We need to physically move characters once we've found an equal span { ptr[outIdx++] = ptr[inIdx++]; } diff --git a/IDE/mintest/minlib/src/System/Type.bf b/IDE/mintest/minlib/src/System/Type.bf index 22804438..0e7be62b 100644 --- a/IDE/mintest/minlib/src/System/Type.bf +++ b/IDE/mintest/minlib/src/System/Type.bf @@ -20,13 +20,14 @@ namespace System protected const BindingFlags cDefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - protected int32 mSize; - protected TypeId mTypeId; - protected TypeId mBoxedType; - protected TypeFlags mTypeFlags; - protected int32 mMemberDataOffset; - protected TypeCode mTypeCode; - protected uint8 mAlign; + protected int32 mSize; + protected TypeId mTypeId; + protected TypeId mBoxedType; + protected TypeFlags mTypeFlags; + protected int32 mMemberDataOffset; + protected TypeCode mTypeCode; + protected uint8 mAlign; + protected uint8 mAllocStackCountOverride; public static TypeId TypeIdEnd { @@ -679,6 +680,7 @@ namespace System.Reflection public int32 mMethodIdx; public int32 mVirtualIdx; public int32 mCustomAttributesIdx; + public int32 mReturnCustomAttributesIdx; } public enum ParamFlags : int16 @@ -695,6 +697,7 @@ namespace System.Reflection public TypeId mType; public ParamFlags mParamFlags; public int32 mDefaultIdx; + public int32 mCustomAttributesIdx; } public struct InterfaceData @@ -1115,7 +1118,10 @@ namespace System.Reflection let genericType = GetGenericArg(0); let arraySize = [Friend]mInstSize - genericType.Size + genericType.Stride * count; #if BF_ENABLE_OBJECT_DEBUG_FLAGS - obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, 1); + int32 stackCount = Compiler.Options.AllocStackCount; + if (mAllocStackCountOverride != 0) + stackCount = mAllocStackCountOverride; + obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, stackCount); #else void* mem = new [Align(16)] uint8[arraySize]* (?); obj = Internal.UnsafeCastToObject(mem); diff --git a/IDE/src/BuildContext.bf b/IDE/src/BuildContext.bf index 211d5f3e..21ec2e17 100644 --- a/IDE/src/BuildContext.bf +++ b/IDE/src/BuildContext.bf @@ -180,10 +180,13 @@ namespace IDE #endif //String error = scope String(); - bool isTest = options.mBuildOptions.mBuildKind == .Test; bool isExe = ((project.mGeneralOptions.mTargetType != Project.TargetType.BeefLib) && (project.mGeneralOptions.mTargetType != Project.TargetType.BeefTest)) || (isTest); - if (!isExe) + if ((options.mBuildOptions.mBuildKind == .StaticLib) || (options.mBuildOptions.mBuildKind == .DynamicLib)) + { + // Okay + } + else if (!isExe) return true; String arCmds = scope String(""); //-O2 -Rpass=inline @@ -191,11 +194,42 @@ namespace IDE arCmds.AppendF("CREATE {}\n", targetPath); + void AddObject(StringView obj) + { + if (obj.IsEmpty) + return; + + if (obj.EndsWith(".lib", .OrdinalIgnoreCase)) + arCmds.AppendF("ADDLIB {}\n", obj); + else + arCmds.AppendF("ADDMOD {}\n", obj); + } + + bool inQuote = false; + int lastEnd = -1; + for (int i < objectsArg.Length) + { + var c = objectsArg[i]; + if (c == '"') + { + if (inQuote) + AddObject(objectsArg.Substring(lastEnd + 1, i - lastEnd - 1)); + inQuote = !inQuote; + lastEnd = i; + } + else if ((c == ' ') && (!inQuote)) + { + AddObject(objectsArg.Substring(lastEnd + 1, i - lastEnd - 1)); + lastEnd = i; + } + } + AddObject(objectsArg.Substring(lastEnd + 1)); + for (let obj in objectsArg.Split(' ')) { if (!obj.IsEmpty) { - arCmds.AppendF("ADDMOD {}\n", obj); + } } arCmds.AppendF("SAVE\n"); @@ -245,6 +279,12 @@ namespace IDE project.mLastDidBuild = true; } + else + { + var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project); + tagetCompletedCmd.mOnlyIfNotFailed = true; + gApp.mExecutionQueue.Add(tagetCompletedCmd); + } return true; } @@ -297,7 +337,10 @@ namespace IDE bool isTest = options.mBuildOptions.mBuildKind == .Test; bool isExe = ((project.mGeneralOptions.mTargetType != Project.TargetType.BeefLib) && (project.mGeneralOptions.mTargetType != Project.TargetType.BeefTest)) || (isTest); - bool isDynLib = project.mGeneralOptions.mTargetType == Project.TargetType.BeefDynLib; + bool isDynLib = (project.mGeneralOptions.mTargetType == Project.TargetType.BeefLib) && (options.mBuildOptions.mBuildKind == .DynamicLib); + + if (options.mBuildOptions.mBuildKind == .StaticLib) + isExe = false; if (isExe || isDynLib) { @@ -347,7 +390,9 @@ namespace IDE //TODO: Make an option if (options.mBuildOptions.mCLibType == Project.CLibType.Static) { - linkLine.Append("-static-libgcc -static-libstdc++ "); + if (mPlatformType != .macOS) + linkLine.Append("-static-libgcc "); + linkLine.Append("-static-libstdc++ "); } else { @@ -507,6 +552,12 @@ namespace IDE project.mLastDidBuild = true; } } + else + { + var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project); + tagetCompletedCmd.mOnlyIfNotFailed = true; + gApp.mExecutionQueue.Add(tagetCompletedCmd); + } return true; } @@ -521,7 +572,7 @@ namespace IDE bool isTest = options.mBuildOptions.mBuildKind == .Test; bool isExe = ((project.mGeneralOptions.mTargetType != Project.TargetType.BeefLib) && (project.mGeneralOptions.mTargetType != Project.TargetType.BeefTest)) || (isTest); - bool isDynLib = project.mGeneralOptions.mTargetType == Project.TargetType.BeefDynLib; + bool isDynLib = (project.mGeneralOptions.mTargetType == Project.TargetType.BeefLib) && (options.mBuildOptions.mBuildKind == .DynamicLib); if (isExe || isDynLib) { @@ -607,6 +658,12 @@ namespace IDE project.mLastDidBuild = true; } } + else + { + var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project); + tagetCompletedCmd.mOnlyIfNotFailed = true; + gApp.mExecutionQueue.Add(tagetCompletedCmd); + } return true; } @@ -757,7 +814,8 @@ namespace IDE } } - if (depProject.mGeneralOptions.mTargetType == .BeefDynLib) + bool depIsDynLib = (depProject.mGeneralOptions.mTargetType == Project.TargetType.BeefLib) && (depOptions.mBuildOptions.mBuildKind == .DynamicLib); + if (depIsDynLib) { if (mImpLibMap.TryGetValue(depProject, var libPath)) { @@ -793,6 +851,9 @@ namespace IDE bool isTest = options.mBuildOptions.mBuildKind == .Test; bool isExe = ((project.mGeneralOptions.mTargetType != Project.TargetType.BeefLib) && (project.mGeneralOptions.mTargetType != Project.TargetType.BeefTest)) || (isTest); + if (options.mBuildOptions.mBuildKind == .DynamicLib) + isExe = true; + if (isExe) { String linkLine = scope String(); @@ -807,7 +868,7 @@ namespace IDE linkLine.Append("-subsystem:windows "); else if (project.mGeneralOptions.mTargetType == .C_GUIApplication) linkLine.Append("-subsystem:console "); - else if (project.mGeneralOptions.mTargetType == .BeefDynLib) + else if (project.mGeneralOptions.mTargetType == .BeefLib) { linkLine.Append("-dll "); @@ -878,7 +939,7 @@ namespace IDE linkLine.Append("-nologo "); - if ((project.mGeneralOptions.mTargetType == .BeefDynLib) && (workspaceOptions.mAllowHotSwapping) && (is64Bit)) + if ((project.mGeneralOptions.mTargetType == .BeefLib) && (workspaceOptions.mAllowHotSwapping) && (is64Bit)) { // This helps to ensure that DLLs have enough hot swapping space after them int nameHash = targetPath.GetHashCode(); @@ -1112,6 +1173,12 @@ namespace IDE project.mLastDidBuild = true; } } + else + { + var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project); + tagetCompletedCmd.mOnlyIfNotFailed = true; + gApp.mExecutionQueue.Add(tagetCompletedCmd); + } return true; } @@ -1197,7 +1264,7 @@ namespace IDE Directory.CreateDirectory(targetDir).IgnoreError(); } - if (project.mGeneralOptions.mTargetType == .BeefDynLib) + if (project.mGeneralOptions.mTargetType == .BeefLib) { if (targetPath.EndsWith(".dll", .InvariantCultureIgnoreCase)) { @@ -1223,6 +1290,10 @@ namespace IDE if (project.mGeneralOptions.mTargetType == .CustomBuild) { + var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project); + tagetCompletedCmd.mOnlyIfNotFailed = true; + gApp.mExecutionQueue.Add(tagetCompletedCmd); + return true; } @@ -1379,8 +1450,19 @@ namespace IDE gApp.OutputErrorLine("Project '{}' cannot be linked with the Windows Toolset for platform '{}'", project.mProjectName, mPlatformType); return false; } - else if (!QueueProjectMSLink(project, targetPath, configSelection.mConfig, workspaceOptions, options, objectsArg)) - return false; + else + { + if (options.mBuildOptions.mBuildKind == .StaticLib) + { + if (!QueueProjectGNUArchive(project, targetPath, workspaceOptions, options, objectsArg)) + return false; + } + else + { + if (!QueueProjectMSLink(project, targetPath, configSelection.mConfig, workspaceOptions, options, objectsArg)) + return false; + } + } } return true; diff --git a/IDE/src/Commands.bf b/IDE/src/Commands.bf index 7fa1de36..f0b2de76 100644 --- a/IDE/src/Commands.bf +++ b/IDE/src/Commands.bf @@ -198,6 +198,7 @@ namespace IDE Add("Compile File", new => gApp.Cmd_CompileFile); Add("Debug All Tests", new () => { gApp.[Friend]RunTests(true, true); }); Add("Debug Normal Tests", new () => { gApp.[Friend]RunTests(false, true); }); + Add("Delete All Right", new => gApp.[Friend]DeleteAllRight); Add("Duplicate Line", new () => { gApp.[Friend]DuplicateLine(); }); Add("Exit", new => gApp.[Friend]Cmd_Exit); Add("Find All References", new => gApp.Cmd_FindAllReferences); diff --git a/IDE/src/Debugger/DebugManager.bf b/IDE/src/Debugger/DebugManager.bf index 046b01c8..d10ef3c3 100644 --- a/IDE/src/Debugger/DebugManager.bf +++ b/IDE/src/Debugger/DebugManager.bf @@ -140,7 +140,7 @@ namespace IDE.Debugger static extern bool Debugger_OpenMiniDump(char8* filename); [CallingConvention(.Stdcall),CLink] - static extern bool Debugger_OpenFile(char8* launchPath, char8* targetPath, char8* args, char8* workingDir, void* envBlockPtr, int32 envBlockLen); + static extern bool Debugger_OpenFile(char8* launchPath, char8* targetPath, char8* args, char8* workingDir, void* envBlockPtr, int32 envBlockLen, bool hotSwapEnabled); [CallingConvention(.Stdcall),CLink] static extern bool Debugger_Attach(int32 processId, AttachFlags attachFlags); @@ -429,7 +429,7 @@ namespace IDE.Debugger mIsRunningCompiled = isCompiled; mIsRunningWithHotSwap = hotSwapEnabled; - return Debugger_OpenFile(launchPath, targetPath, args, workingDir, envBlock.Ptr, (int32)envBlock.Length); + return Debugger_OpenFile(launchPath, targetPath, args, workingDir, envBlock.Ptr, (int32)envBlock.Length, hotSwapEnabled); } public void SetSymSrvOptions(String symCacheDir, String symSrvStr, SymSrvFlags symSrvFlags) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index b5622773..0131b705 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -119,7 +119,7 @@ namespace IDE public class IDEApp : BFApp { public static String sRTVersionStr = "042"; - public const String cVersion = "0.43.1"; + public const String cVersion = "0.43.2"; #if BF_PLATFORM_WINDOWS public static readonly String sPlatform64Name = "Win64"; @@ -851,6 +851,7 @@ namespace IDE var fileDialog = scope OpenFileDialog(); fileDialog.Title = "Open File"; + fileDialog.SetFilter("All files (*.*)|*.*"); fileDialog.Multiselect = true; fileDialog.ValidateNames = true; if (!fullDir.IsEmpty) @@ -1460,8 +1461,14 @@ namespace IDE public bool SaveFileAs(SourceViewPanel sourceViewPanel) { #if !CLI + String fullDir = scope .(); + Path.GetDirectoryPath(sourceViewPanel.mFilePath, fullDir); + SaveFileDialog dialog = scope .(); + dialog.SetFilter("All files (*.*)|*.*"); //dialog.ValidateNames = true; + if (!fullDir.IsEmpty) + dialog.InitialDirectory = fullDir; if (sourceViewPanel.mFilePath != null) { @@ -2402,6 +2409,12 @@ namespace IDE //CloseWorkspace(); //FinishShowingNewWorkspace(); } + + [IDECommand] + void DeleteAllRight() + { + GetActiveSourceEditWidgetContent()?.DeleteAllRight(); + } [IDECommand] void DuplicateLine() @@ -5794,7 +5807,7 @@ namespace IDE TabbedView.TabButton SetupTab(TabbedView tabView, String name, float width, Widget content, bool ownsContent) // 2 { - TabbedView.TabButton tabButton = tabView.AddTab(name, width, content, ownsContent); + TabbedView.TabButton tabButton = tabView.AddTab(name, width, content, ownsContent, GetTabInsertIndex(tabView)); if ((var panel = content as Panel) && (var darkTabButton = tabButton as DarkTabbedView.DarkTabButton)) { darkTabButton.mTabWidthOffset = panel.TabWidthOffset; @@ -5827,7 +5840,7 @@ namespace IDE newTabButton.mWantWidth = newTabButton.GetWantWidth(); newTabButton.mHeight = tabbedView.mTabHeight; newTabButton.mContent = disassemblyPanel; - tabbedView.AddTab(newTabButton); + tabbedView.AddTab(newTabButton, GetTabInsertIndex(tabbedView)); newTabButton.mCloseClickedEvent.Add(new () => CloseDocument(disassemblyPanel)); newTabButton.Activate(); @@ -5837,6 +5850,14 @@ namespace IDE return disassemblyPanel; } + int GetTabInsertIndex(TabbedView tabs) + { + if (mSettings.mUISettings.mInsertNewTabs == .RightOfExistingTabs) + return tabs.mTabs.Count; + else + return 0; + } + public class SourceViewTab : DarkTabbedView.DarkTabButton { public float GetWantWidth() @@ -6008,7 +6029,7 @@ namespace IDE tabButton.mIsRightTab = false; var darkTabbedView = (DarkTabbedView)tabButton.mTabbedView; darkTabbedView.SetRightTab(null, false); - darkTabbedView.AddTab(tabButton); + darkTabbedView.AddTab(tabButton, GetTabInsertIndex(darkTabbedView)); tabButton.Activate(); } @@ -6245,7 +6266,8 @@ namespace IDE } int32 emitRevision = -1; - // + + if (useFilePath != null) { int barPos = useFilePath.IndexOf('|'); if (barPos != -1) @@ -6383,7 +6405,7 @@ namespace IDE tabbedView.SetRightTab(newTabButton); } else - tabbedView.AddTab(newTabButton); + tabbedView.AddTab(newTabButton, GetTabInsertIndex(tabbedView)); newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(sourceViewPanel)); newTabButton.Activate(setFocus); if ((setFocus) && (sourceViewPanel.mWidgetWindow != null)) @@ -8872,7 +8894,13 @@ namespace IDE { targetType = .BeefTest; if (mTestManager != null) - mTestManager.AddProject(project); + { + String workingDirRel = scope String(); + ResolveConfigString(mPlatformName, workspaceOptions, project, options, "$(WorkingDir)", "debug working directory", workingDirRel); + var workingDir = scope String(); + Path.GetAbsolutePath(workingDirRel, project.mProjectDir, workingDir); + mTestManager.AddProject(project, workingDir); + } } else { @@ -8884,11 +8912,15 @@ namespace IDE { if (project.mGeneralOptions.mTargetType.IsBeefApplication) targetType = .BeefApplication_StaticLib; + else if (project.mGeneralOptions.mTargetType == .BeefLib) + targetType = .BeefLib_Static; } else if (options.mBuildOptions.mBuildKind == .DynamicLib) { if (project.mGeneralOptions.mTargetType.IsBeefApplication) targetType = .BeefApplication_DynamicLib; + else if (project.mGeneralOptions.mTargetType == .BeefLib) + targetType = .BeefLib_Dynamic; } } @@ -9418,48 +9450,28 @@ namespace IDE let platformType = Workspace.PlatformType.GetFromName(platformName); - if (options.mBuildOptions.mBuildKind.IsApplicationLib) + switch (platformType) { - switch (platformType) - { - case .Windows: + case .Windows: + if (options.mBuildOptions.mBuildKind == .DynamicLib) + newString.Append(".dll"); + else if ((options.mBuildOptions.mBuildKind == .StaticLib) || (project.mGeneralOptions.mTargetType == .BeefLib)) newString.Append(".lib"); - case .iOS: - if (options.mBuildOptions.mBuildKind == .DynamicLib) - newString.Append(".dylib"); - else - newString.Append(".a"); - case .Wasm: - if (!newString.Contains('.')) - newString.Append(".html"); - default: - if (options.mBuildOptions.mBuildKind == .DynamicLib) - newString.Append(".so"); - else - newString.Append(".a"); - } - } - else - { - switch (platformType) - { - case .Windows: - if (project.mGeneralOptions.mTargetType == .BeefLib) - newString.Append(".lib"); - else if (project.mGeneralOptions.mTargetType == .BeefDynLib) - newString.Append(".dll"); - else if (project.mGeneralOptions.mTargetType != .CustomBuild) - newString.Append(".exe"); - case .macOS: - if (project.mGeneralOptions.mTargetType == .BeefDynLib) - newString.Append(".dylib"); - case .Wasm: - if (!newString.Contains('.')) - newString.Append(".html"); - default: - if (project.mGeneralOptions.mTargetType == .BeefDynLib) - newString.Append(".so"); - } + else if (project.mGeneralOptions.mTargetType != .CustomBuild) + newString.Append(".exe"); + case .macOS: + if (options.mBuildOptions.mBuildKind == .DynamicLib) + newString.Append(".dylib"); + else if (options.mBuildOptions.mBuildKind == .StaticLib) + newString.Append(".a"); + case .Wasm: + if (!newString.Contains('.')) + newString.Append(".html"); + default: + if (options.mBuildOptions.mBuildKind == .DynamicLib) + newString.Append(".so"); + else if (options.mBuildOptions.mBuildKind == .StaticLib) + newString.Append(".a"); } } IDEUtils.FixFilePath(newString); @@ -9480,9 +9492,11 @@ namespace IDE case "LinkFlags": newString = scope:ReplaceBlock String(); + bool isBeefDynLib = (project.mGeneralOptions.mTargetType == .BeefLib) && (options.mBuildOptions.mBuildKind == .DynamicLib); + if ((project.mGeneralOptions.mTargetType == .BeefConsoleApplication) || (project.mGeneralOptions.mTargetType == .BeefGUIApplication) || - (project.mGeneralOptions.mTargetType == .BeefDynLib) || + (isBeefDynLib) || (options.mBuildOptions.mBuildKind == .Test)) { let platformType = Workspace.PlatformType.GetFromName(platformName); @@ -10578,7 +10592,7 @@ namespace IDE } } - if ((mWorkspace.mStartupProject != null) && (mWorkspace.mStartupProject.mGeneralOptions.mTargetType == .BeefTest)) + if ((compileKind != .Test) && (mWorkspace.mStartupProject != null) && (mWorkspace.mStartupProject.mGeneralOptions.mTargetType == .BeefTest)) { OutputErrorLine("Test project '{}' has been selected as the Startup Project. Use the 'Test' menu to run or debug tests.", mWorkspace.mStartupProject.mProjectName); return false; @@ -12295,16 +12309,16 @@ namespace IDE } hadMessages = true; int paramIdx = msg.IndexOf(' '); - String cmd = scope String(); - String param = scope String(); + StringView cmd = default; + StringView param = default; if (paramIdx > 0) { - cmd.Append(msg, 0, paramIdx); - param.Append(msg, paramIdx + 1); + cmd = msg.Substring(0, paramIdx); + param = msg.Substring(paramIdx + 1); } else - cmd.Append(msg); + cmd = msg; bool isOutput = (cmd == "msg") || (cmd == "dbgEvalMsg") || (cmd == "log"); if (cmd == "msgLo") @@ -12342,7 +12356,7 @@ namespace IDE while (true) { - String errorMsg = null; + StringView errorMsg = default; int infoPos = param.IndexOf("\x01"); if (infoPos == 0) @@ -12354,7 +12368,7 @@ namespace IDE if (endPos == -1) break; String leakStr = scope String(param, 1, endPos - 1); - param.Remove(0, endPos + 1); + param.RemoveFromStart(endPos + 1); int itemIdx = 0; for (var itemView in leakStr.Split('\t')) { @@ -12396,12 +12410,12 @@ namespace IDE tempStr.Clear(); tempStr.Append(param, 0, infoPos); errorMsg = tempStr; - param.Remove(0, infoPos); + param.RemoveFromStart(infoPos); } else errorMsg = param; - if (errorMsg != null) + if (!errorMsg.IsEmpty) { if (isFirstMsg) { @@ -12413,19 +12427,19 @@ namespace IDE mOutputPanel.Update(); } - OutputLineSmart(scope String("ERROR: ", errorMsg)); + OutputLineSmart(scope String("ERROR: ", scope String(errorMsg))); if (gApp.mRunningTestScript) { // The 'OutputLineSmart' would already call 'Fail' when running test scripts } else { - Fail(errorMsg); + Fail(scope String(errorMsg)); } isFirstMsg = false; } else - Output(errorMsg); + Output(scope String(errorMsg)); } if (infoPos == -1) diff --git a/IDE/src/IDEUtils.bf b/IDE/src/IDEUtils.bf index 6f6b957c..7fc29d01 100644 --- a/IDE/src/IDEUtils.bf +++ b/IDE/src/IDEUtils.bf @@ -17,13 +17,17 @@ namespace IDE public const char8 cNativeSlash = Path.DirectorySeparatorChar; public const char8 cOtherSlash = Path.AltDirectorySeparatorChar; - public static void AppendWithOptionalQuotes(String targetStr, String srcFileName) + public static void AppendWithOptionalQuotes(String targetStr, StringView srcFileName) { bool hasSpace = srcFileName.Contains(' '); bool alreadyQuoted = (srcFileName.Length > 0 && srcFileName[0] == '"' && srcFileName[srcFileName.Length - 1] == '"'); if (hasSpace && !alreadyQuoted) - targetStr.Append("\"", srcFileName, "\""); + { + targetStr.Append("\""); + targetStr.Append(srcFileName); + targetStr.Append("\""); + } else targetStr.Append(srcFileName); } diff --git a/IDE/src/Project.bf b/IDE/src/Project.bf index 2b3c3699..7418ea2a 100644 --- a/IDE/src/Project.bf +++ b/IDE/src/Project.bf @@ -866,14 +866,6 @@ namespace IDE case StaticLib; case DynamicLib; case NotSupported; - - public bool IsApplicationLib - { - get - { - return (this == .StaticLib) || (this == .DynamicLib); - } - } } public enum COptimizationLevel @@ -916,13 +908,14 @@ namespace IDE case BeefConsoleApplication, BeefGUIApplication, BeefLib, - BeefDynLib, CustomBuild, BeefTest, C_ConsoleApplication, C_GUIApplication, BeefApplication_StaticLib, - BeefApplication_DynamicLib; + BeefApplication_DynamicLib, + BeefLib_Static, + BeefLib_Dynamic; public bool IsBeef { @@ -933,7 +926,6 @@ namespace IDE case BeefConsoleApplication, BeefGUIApplication, BeefLib, - BeefDynLib, BeefTest: return true; default: @@ -956,6 +948,22 @@ namespace IDE } } } + + public bool IsBeefApplicationOrLib + { + get + { + switch (this) + { + case BeefConsoleApplication, + BeefGUIApplication, + BeefLib: + return true; + default: + return false; + } + } + } } public class WindowsOptions @@ -1863,7 +1871,8 @@ namespace IDE strs.Add(cmd); } } - + + bool isBeefDynLib = false; using (data.Open("Project")) { if (!IsSingleFile) @@ -1889,6 +1898,9 @@ namespace IDE mGeneralOptions.mTargetType = .BeefGUIApplication; case "C_WindowsApplication": mGeneralOptions.mTargetType = .C_GUIApplication; + case "BeefDynLib": + mGeneralOptions.mTargetType = .BeefLib; + isBeefDynLib = true; default: mGeneralOptions.mTargetType = data.GetEnum("TargetType", GetDefaultTargetType()); } @@ -1962,6 +1974,8 @@ namespace IDE // Build options.mBuildOptions.mBuildKind = data.GetEnum("BuildKind", isTest ? .Test : .Normal); + if ((isBeefDynLib) && (options.mBuildOptions.mBuildKind == .Normal)) + options.mBuildOptions.mBuildKind = .DynamicLib; data.GetString("TargetDirectory", options.mBuildOptions.mTargetDirectory, "$(BuildDir)"); data.GetString("TargetName", options.mBuildOptions.mTargetName, "$(ProjectName)"); data.GetString("OtherLinkFlags", options.mBuildOptions.mOtherLinkFlags, "$(LinkFlags)"); diff --git a/IDE/src/ScriptManager.bf b/IDE/src/ScriptManager.bf index 4c0bafe9..b8861223 100644 --- a/IDE/src/ScriptManager.bf +++ b/IDE/src/ScriptManager.bf @@ -963,35 +963,38 @@ namespace IDE return false; }*/ - if (gApp.mLastActiveSourceViewPanel != null) + if (!ScriptManager.sActiveManager.mIsBuildScript) { - var sourceViewPanel = gApp.mLastActiveSourceViewPanel; - if (sourceViewPanel.HasFocus()) + if (gApp.mLastActiveSourceViewPanel != null) { - if (sourceViewPanel.[Friend]mOldVerLoadExecutionInstance != null) - return false; - if (!sourceViewPanel.mDeferredResolveResults.IsEmpty) - return false; + var sourceViewPanel = gApp.mLastActiveSourceViewPanel; + if (sourceViewPanel.HasFocus()) + { + if (sourceViewPanel.[Friend]mOldVerLoadExecutionInstance != null) + return false; + if (!sourceViewPanel.mDeferredResolveResults.IsEmpty) + return false; - if (sourceViewPanel.[Friend]mWantsFastClassify) - return false; - if (sourceViewPanel.[Friend]mWantsFullClassify) - return false; - if (sourceViewPanel.[Friend]mWantsFullRefresh) + if (sourceViewPanel.[Friend]mWantsFastClassify) + return false; + if (sourceViewPanel.[Friend]mWantsFullClassify) + return false; + if (sourceViewPanel.[Friend]mWantsFullRefresh) + return false; + } + } + + if ((gApp.mBfResolveCompiler != null) && (gApp.mBfResolveCompiler.IsPerformingBackgroundOperation())) + return false; + if (gApp.[Friend]mDeferredOpen != .None) + return false; + if ((gApp.mExecutionPaused) && (gApp.mDebugger.IsPaused())) + { + if (gApp.mWantsRehupCallstack) return false; } } - if ((gApp.mBfResolveCompiler != null) && (gApp.mBfResolveCompiler.IsPerformingBackgroundOperation())) - return false; - if (gApp.[Friend]mDeferredOpen != .None) - return false; - - if ((gApp.mExecutionPaused) && (gApp.mDebugger.IsPaused())) - { - if (gApp.mWantsRehupCallstack) - return false; - } if (gApp.mWantsClean || gApp.mWantsBeefClean) return false; @@ -1011,7 +1014,7 @@ namespace IDE return false; } - if (gApp.mDebugger == null) + if ((gApp.mDebugger == null) || (ScriptManager.sActiveManager.mIsBuildScript)) return true; if ((!ScriptManager.sActiveManager.mIsBuildScript) && (gApp.AreTestsRunning())) diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index 1fb81417..6065643e 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -416,8 +416,15 @@ namespace IDE public class UISettings { + public enum InsertNewTabsKind + { + LeftOfExistingTabs, + RightOfExistingTabs, + } + public Colors mColors = new .() ~ delete _; public float mScale = 100; + public InsertNewTabsKind mInsertNewTabs = .LeftOfExistingTabs; public List mTheme = new .() ~ DeleteContainerAndItems!(_); public void SetDefaults() @@ -425,6 +432,7 @@ namespace IDE DeleteAndNullify!(mColors); mColors = new .(); mScale = 100; + mInsertNewTabs = .LeftOfExistingTabs; ClearAndDeleteItems(mTheme); } @@ -557,6 +565,7 @@ namespace IDE for (let str in mTheme) sd.Add(str); } + sd.Add("InsertNewTabs", mInsertNewTabs); } public void Deserialize(StructuredData sd) @@ -569,6 +578,7 @@ namespace IDE sd.GetCurString(str); mTheme.Add(str); } + sd.Get("InsertNewTabs", ref mInsertNewTabs); } } diff --git a/IDE/src/TestManager.bf b/IDE/src/TestManager.bf index 1bb59775..a9541a64 100644 --- a/IDE/src/TestManager.bf +++ b/IDE/src/TestManager.bf @@ -16,6 +16,7 @@ namespace IDE public int32 mExecutedCount; public int32 mSkipCount; public int32 mFailedCount; + public String mWorkingDir ~ delete _; } public class TestEntry @@ -78,10 +79,11 @@ namespace IDE return true; } - public void AddProject(Project project) + public void AddProject(Project project, StringView workingDir) { var projectInfo = new ProjectInfo(); projectInfo.mProject = project; + projectInfo.mWorkingDir = new String(workingDir); mProjectInfos.Add(projectInfo); } @@ -141,7 +143,7 @@ namespace IDE startInfo.CreateNoWindow = !gApp.mTestEnableConsole; startInfo.SetFileName(curProjectInfo.mTestExePath); startInfo.SetArguments(testInstance.mArgs); - startInfo.SetWorkingDirectory(testInstance.mWorkingDir); + startInfo.SetWorkingDirectory(curProjectInfo.mWorkingDir); mTestInstance.mProcess = new SpawnedProcess(); if (testInstance.mProcess.Start(startInfo) case .Err) { @@ -301,7 +303,7 @@ namespace IDE testEntry.mExecuted = true; String clientCmd = scope $":TestRun\t{testInstance.mCurTestIdx}"; - if ((gApp.mTestBreakOnFailure) && (mDebug)) + if ((gApp.mTestBreakOnFailure) && (mDebug) && (!testEntry.mShouldFail)) clientCmd.Append("\tFailBreak"); clientCmd.Append("\n"); @@ -591,7 +593,7 @@ namespace IDE //mTestInstance.mWorkingDir = new String(); //Path.GetDirectoryName(curProjectInfo.mTestExePath, mTestInstance.mWorkingDir); - mTestInstance.mWorkingDir = new String(gApp.mInstallDir); + mTestInstance.mWorkingDir = new String(curProjectInfo.mWorkingDir); mTestInstance.mPipeServer = new NamedPipe(); if (mTestInstance.mPipeServer.Create(".", mTestInstance.mPipeName, .AllowTimeouts) case .Err) diff --git a/IDE/src/Workspace.bf b/IDE/src/Workspace.bf index 396d9e24..2bc48597 100644 --- a/IDE/src/Workspace.bf +++ b/IDE/src/Workspace.bf @@ -34,7 +34,7 @@ namespace IDE case Android; case Wasm; - public static PlatformType GetFromName(String name) + public static PlatformType GetFromName(StringView name) { switch (name) { @@ -1401,7 +1401,7 @@ namespace IDE { if (curColumn == column) return charId; - // For column == -1, use first non-whitespace char8 + // For column == -1, use first non-whitespace char if ((column == -1) && (!c.IsWhiteSpace)) return charId; } diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 178081fe..1e0a4640 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -999,10 +999,18 @@ namespace IDE.ui } if (sectionIdx == cursorSection) { - int lastSpace = sectionStr.LastIndexOf(' '); + paramName = sectionStr; + int eqPos = paramName.IndexOf('='); + if (eqPos != -1) + { + paramName.RemoveToEnd(eqPos); + paramName.Trim(); + } + + int lastSpace = paramName.LastIndexOf(' '); if (lastSpace != -1) { - paramName = .(sectionStr, lastSpace + 1); + paramName = .(paramName, lastSpace + 1); if (paramName.EndsWith(',')) paramName.RemoveFromEnd(1); } @@ -1722,7 +1730,7 @@ namespace IDE.ui if ((!SelectEntry(curString)) && (curString.Length > 0)) { - // If we can't find any matches, at least select a string that starts with the right char8acter + // If we can't find any matches, at least select a string that starts with the right character curString.RemoveToEnd(1); SelectEntry(curString); } diff --git a/IDE/src/ui/BinaryDataWidget.bf b/IDE/src/ui/BinaryDataWidget.bf index bc8be3f5..fe372aba 100644 --- a/IDE/src/ui/BinaryDataWidget.bf +++ b/IDE/src/ui/BinaryDataWidget.bf @@ -1553,7 +1553,7 @@ namespace IDE.ui bool inStrView = false; bool checkSubChar = true; float testX = curX; - curX += 4; // offset by half a char8acter + curX += 4; // offset by half a character testX -= GS!(mColumnDisplayStart); testX /= (float)GS!(mColumnDisplayStride); if (testX < 0.0f) diff --git a/IDE/src/ui/ClassViewPanel.bf b/IDE/src/ui/ClassViewPanel.bf index 8211dfe6..3088cccc 100644 --- a/IDE/src/ui/ClassViewPanel.bf +++ b/IDE/src/ui/ClassViewPanel.bf @@ -13,6 +13,8 @@ namespace IDE.ui { class ClassViewPanel : Panel { + private static String sLastSearchString = new String() ~ delete _; + public class ClassViewListViewItem : IDEListViewItem { public float mLabelOffset; @@ -407,6 +409,15 @@ namespace IDE.ui { mWantsSubmit = true; }); + + mSearchEdit.SetText(sLastSearchString); + mSearchEdit.Content.SelectAll(); + + findClassDialog.mOnClosed.Add(new () => + { + sLastSearchString.Clear(); + mSearchEdit.GetText(sLastSearchString); + }); } //mListView.mDragEndHandler.Add(new => HandleDragEnd); diff --git a/IDE/src/ui/HoverWatch.bf b/IDE/src/ui/HoverWatch.bf index 5287370c..4b8bbfe8 100644 --- a/IDE/src/ui/HoverWatch.bf +++ b/IDE/src/ui/HoverWatch.bf @@ -259,6 +259,8 @@ namespace IDE.ui float mOrigY; float mOrigScreenX; float mOrigScreenY; + float mTaskbarXOffset; + float mTaskbarYOffset; bool mIsShown; bool mCreatedWindow; bool mClosed; @@ -1078,6 +1080,9 @@ namespace IDE.ui WidgetWindow.sOnKeyDown.Add(new => HandleKeyDown); widgetWindow.mOnWindowClosed.Add(new (window) => Close()); + + mTaskbarXOffset = mOrigScreenX - widgetWindow.mNormX; + mTaskbarYOffset = mOrigScreenY - widgetWindow.mNormY; } else { @@ -1090,7 +1095,7 @@ namespace IDE.ui if (autocomplete != null) autocomplete.SetIgnoreMove(true); - mWidgetWindow.Resize((int32)(mWidgetWindow.mNormX + minX), (int32)(mWidgetWindow.mNormY + minY), (int32)(maxX - minX), (int32)(maxY - minY)); + mWidgetWindow.Resize((int32)(mOrigScreenX - mTaskbarXOffset + minX), (int32)(mOrigScreenY - mTaskbarYOffset + minY), (int32)(maxX - minX), (int32)(maxY - minY)); if (autocomplete != null) autocomplete.SetIgnoreMove(false); } @@ -1293,6 +1298,7 @@ namespace IDE.ui actualMaxWidth = Math.Max(actualMaxWidth, fontMetrics.mMaxWidth); float addHeight = nameHeight - listViewItem.mSelfHeight; + listViewItem.mSelfHeight = nameHeight; height += addHeight; } diff --git a/IDE/src/ui/InstalledProjectDialog.bf b/IDE/src/ui/InstalledProjectDialog.bf index 3b650c3a..88dbf864 100644 --- a/IDE/src/ui/InstalledProjectDialog.bf +++ b/IDE/src/ui/InstalledProjectDialog.bf @@ -128,6 +128,7 @@ namespace IDE.ui mEditWidget.GetText(filterString); filterString.Trim(); + mFilteredList.Clear(); for (var installedProject in mInstalledProjectList) { if ((!filterString.IsEmpty) && (installedProject.mName.IndexOf(filterString, true) == -1)) diff --git a/IDE/src/ui/NewProjectDialog.bf b/IDE/src/ui/NewProjectDialog.bf index 06a1ed2a..87fedd24 100644 --- a/IDE/src/ui/NewProjectDialog.bf +++ b/IDE/src/ui/NewProjectDialog.bf @@ -16,11 +16,10 @@ namespace IDE.ui public PathEditWidget mDirectoryEdit; public EditWidget mNameEdit; public DarkComboBox mTargetComboBox; - static String[6] sApplicationTypeNames = + static String[5] sApplicationTypeNames = .("Console Application", "GUI Application", "Library", - "Dynamic Library", "Custom Build", "Test"); public bool mNameChanged; diff --git a/IDE/src/ui/OpenFileInSolutionDialog.bf b/IDE/src/ui/OpenFileInSolutionDialog.bf index 0cc1b7a2..835121fb 100644 --- a/IDE/src/ui/OpenFileInSolutionDialog.bf +++ b/IDE/src/ui/OpenFileInSolutionDialog.bf @@ -64,6 +64,8 @@ namespace IDE.ui public bool mFilterChanged; public volatile bool mExitingThread; public Thread mDateThread; + + static String sLastSearchString = new String() ~ delete _; public this() { @@ -91,9 +93,15 @@ namespace IDE.ui AddWidget(mFileList); mTabWidgets.Add(mFileList); - mEditWidget = AddEdit(""); + mEditWidget = AddEdit(sLastSearchString); mEditWidget.mOnKeyDown.Add(new => EditKeyDownHandler); mEditWidget.mOnContentChanged.Add(new (evt) => { mFilterChanged = true; }); + + mOnClosed.Add(new () => + { + sLastSearchString.Clear(); + mEditWidget.GetText(sLastSearchString); + }); } void ShutdownThread() diff --git a/IDE/src/ui/OutputPanel.bf b/IDE/src/ui/OutputPanel.bf index cc40a3fc..8d002fa1 100644 --- a/IDE/src/ui/OutputPanel.bf +++ b/IDE/src/ui/OutputPanel.bf @@ -84,6 +84,8 @@ namespace IDE.ui List mQueuedDisplayChanges = new List() ~ delete _; List mInlineWidgets = new List() ~ delete _; public int32 mHoverWatchLine; + public float mLastInlineMinY; + public float mLastInlineMaxY; public override SourceEditWidget EditWidget { @@ -198,10 +200,10 @@ namespace IDE.ui } } - public override void Update() + public override void UpdateAll() { - base.Update(); - + base.UpdateAll(); + var editData = mOutputWidget.mEditWidgetContent.mData; int lineStartZero = -1; @@ -211,6 +213,8 @@ namespace IDE.ui Debug.Assert(lineStartZero <= editData.mTextLength); } + bool hasNewInlineWidgets = false; + UpdateHoverWatch(); if (mQueuedText.Length > 0) { @@ -232,7 +236,7 @@ namespace IDE.ui if (queuedDisplayChange.mWidget != null) { var widget = queuedDisplayChange.mWidget; - mOutputWidget.mEditWidgetContent.AddWidget(queuedDisplayChange.mWidget); + hasNewInlineWidgets = true; mOutputWidget.Content.GetLineCharAtIdx(startLen + queuedDisplayChange.mOfs, out line, out lineChar); mOutputWidget.Content.GetTextCoordAtLineChar(line, lineChar, var xOfs, var yOfs); @@ -257,6 +261,27 @@ namespace IDE.ui mQueuedText.Clear(); mQueuedDisplayChanges.Clear(); } + + float minY = -mOutputWidget.mEditWidgetContent.mY; + float maxY = minY + mOutputWidget.mHeight; + + if ((gApp.mIsUpdateBatchStart) || (hasNewInlineWidgets) || (mLastInlineMinY != minY) || (mLastInlineMaxY != maxY)) + { + for (var inlineWidget in ref mInlineWidgets) + { + bool isVisible = (inlineWidget.mWidget.mY + inlineWidget.mWidget.mHeight >= minY) && (inlineWidget.mWidget.mY < maxY); + if ((inlineWidget.mWidget.mParent != null) != isVisible) + { + if (isVisible) + mOutputWidget.mEditWidgetContent.AddWidget(inlineWidget.mWidget); + else + inlineWidget.mWidget.RemoveSelf(); + } + } + + mLastInlineMinY = minY; + mLastInlineMaxY = maxY; + } } public void WriteSmart(StringView text) diff --git a/IDE/src/ui/ProfilePanel.bf b/IDE/src/ui/ProfilePanel.bf index 2bc7fff4..2ca2beb8 100644 --- a/IDE/src/ui/ProfilePanel.bf +++ b/IDE/src/ui/ProfilePanel.bf @@ -657,13 +657,13 @@ namespace IDE.ui { g.DrawString(StackStringFormat!("Rate: {0} Hz", mShowOverview.mSamplesPerSecond), GS!(320), GS!(2)); int32 seconds = (mShowOverview.mRecordedTicks / 1000); - g.DrawString(StackStringFormat!("Length: {0}:{1:02}.{2}", seconds / 60, seconds % 60, (mShowOverview.mRecordedTicks % 1000)/100), GS!(320), GS!(22)); + g.DrawString(StackStringFormat!("Length: {0}:{1:00}.{2}", seconds / 60, seconds % 60, (mShowOverview.mRecordedTicks % 1000)/100), GS!(320), GS!(22)); seconds = (mShowOverview.mEndedTicks / 1000); if (seconds > 60*60) - g.DrawString(StackStringFormat!("Age: {0}:{1:02}:{2:02}", seconds / 60 / 60, (seconds / 60) % 60, seconds % 60), GS!(420), GS!(22)); + g.DrawString(StackStringFormat!("Age: {0}:{1:00}:{2:00}", seconds / 60 / 60, (seconds / 60) % 60, seconds % 60), GS!(420), GS!(22)); else - g.DrawString(StackStringFormat!("Age: {0}:{1:02}", seconds / 60, seconds % 60), GS!(420), GS!(22)); + g.DrawString(StackStringFormat!("Age: {0}:{1:00}", seconds / 60, seconds % 60), GS!(420), GS!(22)); g.DrawString(StackStringFormat!("Samples: {0}", mShowOverview.mTotalActualSamples), GS!(420), GS!(2)); g.DrawString(StackStringFormat!("Missed Samples: {0}", mShowOverview.mTotalVirtualSamples - mShowOverview.mTotalActualSamples), GS!(550), GS!(2)); diff --git a/IDE/src/ui/ProjectProperties.bf b/IDE/src/ui/ProjectProperties.bf index 99e88090..bbc35e9c 100644 --- a/IDE/src/ui/ProjectProperties.bf +++ b/IDE/src/ui/ProjectProperties.bf @@ -622,7 +622,6 @@ namespace IDE.ui "Console Application", "GUI Application", "Library", - "Dynamic Library", "Custom Build", "Test" )); diff --git a/IDE/src/ui/PropertiesDialog.bf b/IDE/src/ui/PropertiesDialog.bf index 6935ec5f..014dd468 100644 --- a/IDE/src/ui/PropertiesDialog.bf +++ b/IDE/src/ui/PropertiesDialog.bf @@ -44,7 +44,8 @@ namespace IDE.ui { if ((evt.mKeyCode == .Control) || (evt.mKeyCode == .Shift) || - (evt.mKeyCode == .Alt)) + (evt.mKeyCode == .Alt) || + (evt.mKeyCode == .RAlt)) return; if ((evt.mKeyFlags == 0) && diff --git a/IDE/src/ui/SettingsDialog.bf b/IDE/src/ui/SettingsDialog.bf index 6add48a8..38cf60b3 100644 --- a/IDE/src/ui/SettingsDialog.bf +++ b/IDE/src/ui/SettingsDialog.bf @@ -78,6 +78,7 @@ namespace IDE.ui AddPropertiesItem(category, "Scale", "mScale"); AddPropertiesItem(category, "Theme", "mTheme"); + AddPropertiesItem(category, "Insert New Tabs", "mInsertNewTabs"); category.Open(true, true); } diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index cbef21bb..dbb19ef7 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -2237,6 +2237,41 @@ namespace IDE.ui return false; } + + public void DeleteAllRight() + { + int startPos; + int endPos; + if (HasSelection()) + { + mSelection.ValueRef.GetAsForwardSelect(out startPos, out endPos); + } + else + { + startPos = endPos = CursorTextPos; + } + + int line; + int lineChar; + GetLineCharAtIdx(endPos, out line, out lineChar); + + let lineText = scope String(); + GetLineText(line, lineText); + + endPos += lineText.Length - lineChar; + + if (startPos == endPos) + { + return; + } + + mSelection = EditSelection(); + mSelection.ValueRef.mStartPos = (int32)startPos; + mSelection.ValueRef.mEndPos = (int32)endPos; + DeleteSelection(); + + CursorTextPos = startPos; + } public void DuplicateLine() { @@ -2758,7 +2793,7 @@ namespace IDE.ui return; } - if (mIsReadOnly) + if ((AllowChar(keyChar)) && (CheckReadOnly())) { base.KeyChar(keyChar); return; @@ -3394,7 +3429,8 @@ namespace IDE.ui int textStartPos = lineChar - (int32)trimmedLineText.Length; String tabStartStr = scope String(); - tabStartStr.Append(lineText, 0, textStartPos); + if (textStartPos > 0) + tabStartStr.Append(lineText, 0, textStartPos); int32 columnPos = (int32)(GetTabbedWidth(tabStartStr, 0) / mCharWidth + 0.001f); if (wantLineColumn > columnPos) { @@ -3818,7 +3854,7 @@ namespace IDE.ui }); // Fixits - if ((mSourceViewPanel.mIsBeefSource) && (mSourceViewPanel.mProjectSource != null) && (gApp.mSymbolReferenceHelper?.IsLocked != true)) + if ((mSourceViewPanel.mIsBeefSource) && (mSourceViewPanel.FilteredProjectSource != null) && (gApp.mSymbolReferenceHelper?.IsLocked != true)) { ResolveParams resolveParams = scope .(); mSourceViewPanel.DoClassify(ResolveType.GetFixits, resolveParams, true); diff --git a/IDE/src/ui/SourceViewPanel.bf b/IDE/src/ui/SourceViewPanel.bf index ce748449..e94ca7d2 100644 --- a/IDE/src/ui/SourceViewPanel.bf +++ b/IDE/src/ui/SourceViewPanel.bf @@ -520,7 +520,7 @@ namespace IDE.ui } } - ProjectSource FilteredProjectSource + public ProjectSource FilteredProjectSource { get { @@ -4277,7 +4277,7 @@ namespace IDE.ui if (mProcessingCharData[srcIdx].mCharId != destText[destIdx].mCharId) { - // Id doesn't match, char8acter must have been deleted + // Id doesn't match, character must have been deleted srcIdx++; continue; } @@ -4951,7 +4951,7 @@ namespace IDE.ui if ((!String.IsNullOrEmpty(mHoverResolveTask?.mResult))) { origDebugExpr = scope:: String(); - origDebugExpr.Set(debugExpr); + origDebugExpr.Set(""); debugExpr.Set(mHoverResolveTask.mResult); diff --git a/IDE/src/util/DefinesSet.bf b/IDE/src/util/DefinesSet.bf index 44f599d3..d55feafa 100644 --- a/IDE/src/util/DefinesSet.bf +++ b/IDE/src/util/DefinesSet.bf @@ -18,7 +18,7 @@ namespace IDE.util return; } - if (!mDefinesSet.ContainsWith(str)) + if (!mDefinesSet.ContainsAlt(str)) { var strCopy = new String(str); mDefines.Add(strCopy); diff --git a/IDEHelper/Backend/BeMCContext.cpp b/IDEHelper/Backend/BeMCContext.cpp index 40485635..1112dca9 100644 --- a/IDEHelper/Backend/BeMCContext.cpp +++ b/IDEHelper/Backend/BeMCContext.cpp @@ -977,10 +977,8 @@ void BeMCColorizer::GenerateRegCosts() { auto inst = mcBlock->mInstructions[instIdx]; - if ((inst->IsMov()) && (inst->mArg1.IsNativeReg()) && (!inst->mArg0.IsNativeReg())) + if ((inst->IsMov()) && (inst->mArg1.IsNativeReg()) && (inst->mArg0.IsVReg())) { - BF_ASSERT(inst->mArg0.IsVReg()); - int vregIdx = mContext->GetUnderlyingVReg(inst->mArg0.mVRegIdx); auto reg = mContext->GetFullRegister(inst->mArg1.mReg); @@ -1263,11 +1261,6 @@ void BeMCColorizer::AssignRegs(RegKind regKind) int totalRegs32 = 0; int totalRegs16 = 0; - if (mContext->mDebugging) - { - NOP; - } - SizedArray validRegs; if (regKind == BeMCColorizer::RegKind_Ints) { @@ -1623,11 +1616,6 @@ void BeMCColorizer::AssignRegs(RegKind regKind) } } - if (vregInfo->mType->IsVector()) - { - NOP; - } - if (vregInfo->mSpilled) { if (bestReg != X64Reg_None) @@ -3787,7 +3775,7 @@ BeMCOperand BeMCContext::AllocVirtualReg(BeType* type, int refCount, bool mustBe if (mDebugging) { - if (mcOperand.mVRegIdx == 227) + if (mcOperand.mVRegIdx == 8) { NOP; } @@ -4185,6 +4173,33 @@ bool BeMCContext::CouldBeReg(const BeMCOperand& operand) return true; } +// bool BeMCContext::CouldBeReg(const BeMCOperand& operand) +// { +// if (operand.mKind != BeMCOperandKind_VReg) +// return false; +// +// auto vregInfo = GetVRegInfo(operand); +// if ((vregInfo->mIsRetVal) && (mCompositeRetVRegIdx != -1) && (mCompositeRetVRegIdx != operand.mVRegIdx)) +// { +// return CouldBeReg(BeMCOperand::FromVReg(mCompositeRetVRegIdx)); +// } +// +// if (vregInfo->mReg != X64Reg_None) +// return true; +// +// if (vregInfo->mForceMem) +// return false; +// +// if (vregInfo->mIsExpr) +// { +// if (vregInfo->mRelOffset) +// return false; +// return CouldBeReg(vregInfo->mRelTo); +// } +// +// return !vregInfo->mType->IsNonVectorComposite(); +// } + bool BeMCContext::CheckForce(BeMCVRegInfo* vregInfo) { if (!vregInfo->mIsRetVal) @@ -6054,6 +6069,12 @@ void BeMCContext::GetValAddr(const BeMCOperand& operand, X64CPURegister& reg, in vregInfo = GetVRegInfo(vregInfo->mRelTo); } + if ((mCompositeRetVRegIdx == operand.mVRegIdx) && (vregInfo->mReg != X64Reg_None)) + { + reg = vregInfo->mReg; + return; + } + reg = mUseBP ? X64Reg_RBP : X64Reg_RSP; offset = mStackSize + vregInfo->mFrameOffset; } @@ -7669,13 +7690,18 @@ void BeMCContext::DoInstCombinePass() continue; } } - else if ((mcAddr.mKind == BeMCOperandKind_Symbol) || (mcAddr.mKind == BeMCOperandKind_SymbolAddr)) + else if (mcAddr.mKind == BeMCOperandKind_SymbolAddr) { // No actual load needed - just keep it as a Direct reference inst->mKind = BeMCInstKind_Def; //wantUnwrapVRegs.Add(inst->mArg0.mVRegIdx); continue; } + else if (mcAddr.mKind == BeMCOperandKind_Symbol) + { + //TODO: We used to have this as a 'inst->mKind = BeMCInstKind_Def;' case also, but that messed up post-increment (ie: gVal++) values + continue; + } bool hadPointerDeref = false; for (int checkInstIdx = instIdx + 1; checkInstIdx < (int)mcBlock->mInstructions.size(); checkInstIdx++) @@ -7814,6 +7840,8 @@ void BeMCContext::DoInstCombinePass() if ((vregInfoDest->mIsExpr) || (vregInfoCheck->mIsExpr)) continue; + if ((vregInfoCheck->mForceMem) && (!vregInfoDest->mForceMem)) + continue; if ((!vregInfoDest->mForceMerge) && (!vregInfoDest->mIsRetVal)) { @@ -8384,12 +8412,7 @@ void BeMCContext::DoRegAssignPass() void BeMCContext::DoFrameObjPass() { - BF_ASSERT(mBlocks.size() == 1); - - if (mDebugging) - { - NOP; - } + BF_ASSERT(mBlocks.size() == 1); SetCurrentInst(NULL); @@ -9621,13 +9644,15 @@ bool BeMCContext::DoLegalization() } else { - // This may be a local variable that failed to be assigned to a reg, create a scratch local with a forced reg - auto scratchReg = AllocVirtualReg(errorVRegInfo->mType, 2, false); - auto scratchVRegInfo = mVRegInfo[scratchReg.mVRegIdx]; + // This may be a local variable that failed to be assigned to a reg, create a scratch local with a forced reg auto errorVReg = BeMCOperand::FromVReg(rmInfo.mErrorVReg); + auto errorVRegLoad = BeMCOperand::ToLoad(errorVReg); if ((vregInfo->mRelTo == errorVReg) || (vregInfo->mRelOffset == errorVReg)) { + auto scratchReg = AllocVirtualReg(errorVRegInfo->mType, 2, false); + auto scratchVRegInfo = mVRegInfo[scratchReg.mVRegIdx]; + CreateDefineVReg(scratchReg, instIdx++); AllocInst(BeMCInstKind_Mov, scratchReg, errorVReg, instIdx++); isFinalRun = false; @@ -9648,6 +9673,32 @@ bool BeMCContext::DoLegalization() vregInfo->mRelOffset.mVRegIdx = scratchReg.mVRegIdx; } } + else if ((vregInfo->mRelTo == errorVRegLoad) || (vregInfo->mRelOffset == errorVRegLoad)) + { + auto scratchType = GetType(errorVRegLoad); + auto scratchReg = AllocVirtualReg(scratchType, 2, false); + auto scratchVRegInfo = mVRegInfo[scratchReg.mVRegIdx]; + + CreateDefineVReg(scratchReg, instIdx++); + AllocInst(BeMCInstKind_Mov, scratchReg, errorVRegLoad, instIdx++); + isFinalRun = false; + if (debugging) + OutputDebugStrF(" RM failed, scratch vreg\n"); + vregExprChangeSet.Add(scratchReg.mVRegIdx); + + if (vregInfo->mRelTo == errorVRegLoad) + { + scratchVRegInfo->mForceReg = true; + CheckForce(scratchVRegInfo); + vregInfo->mRelTo = scratchReg; + } + else if (vregInfo->mRelOffset == errorVRegLoad) + { + scratchVRegInfo->mForceReg = true; + CheckForce(scratchVRegInfo); + vregInfo->mRelOffset = scratchReg; + } + } else { // This should be impossible - a previous def for an inner expr should have caught this case @@ -9764,7 +9815,6 @@ bool BeMCContext::DoLegalization() for (int* argIdxPtr : vregIndices) { auto mcArg = BeMCOperand::FromEncoded(*argIdxPtr); - BeRMParamsInfo rmInfo; GetRMParams(mcArg, rmInfo); if ((rmInfo.mMode != BeMCRMMode_Direct) || (rmInfo.mRegB != X64Reg_None) || (rmInfo.mRegA == X64Reg_R11)) @@ -9878,6 +9928,57 @@ bool BeMCContext::DoLegalization() auto arg0Type = GetType(arg0); if (arg0Type->IsInt()) { + // We can't allow division by RDX because we need RAX:RDX for the dividend + bool needRegDisable = false; + std::function _CheckReg = [&](BeMCOperand operand) + { + if (!operand) + return; + + if (operand.mKind == BeMCOperandKind_NativeReg) + { + auto divisorReg = GetFullRegister(operand.mReg); + if ((divisorReg == X64Reg_RDX) || (divisorReg == X64Reg_RAX)) + needRegDisable = true; + } + + auto vregInfo = GetVRegInfo(operand); + if (vregInfo != NULL) + { + auto divisorReg = GetFullRegister(vregInfo->mReg); + if ((divisorReg == X64Reg_RDX) || (divisorReg == X64Reg_RAX)) + needRegDisable = true; + + _CheckReg(vregInfo->mRelTo); + _CheckReg(vregInfo->mRelOffset); + + if ((needRegDisable) && + ((!vregInfo->mDisableRAX) || (!vregInfo->mDisableRDX))) + { + vregInfo->mDisableRAX = true; + vregInfo->mDisableRDX = true; + isFinalRun = false; + if (debugging) + OutputDebugStrF(" Div/Rem invalid reg\n"); + } + } + }; + + needRegDisable = false; + _CheckReg(arg1); + if ((instIdx > 0) && (regPreserveDepth > 0)) + { + auto prevInst = mcBlock->mInstructions[instIdx - 1]; + if (prevInst->mKind == BeMCInstKind_Mov) + { + // Check replaced 'mov' (which is inside PreserveVolatile sections) + needRegDisable = false; + _CheckReg(prevInst->mArg1); + } + } + + /// + auto checkArg1 = arg1; if (checkArg1.mKind == BeMCOperandKind_VRegLoad) { @@ -10380,9 +10481,8 @@ bool BeMCContext::DoLegalization() isFinalRun = false; } continue; - } - // Struct = Struct - else //if (arg1.mKind == BeMCOperandKind_VReg) + } + else // Struct = Struct { auto arg0Addr = OperandToAddr(arg0); auto arg1Addr = OperandToAddr(arg1); @@ -11251,11 +11351,6 @@ void BeMCContext::DoRegFinalization() if (inst->IsMov()) { - if (mDebugging) - { - NOP; - } - bool removeInst = false; if (GetFixedOperand(inst->mArg0) == GetFixedOperand(inst->mArg1)) @@ -11285,11 +11380,6 @@ void BeMCContext::DoRegFinalization() { case BeMCInstKind_PreserveVolatiles: { - if (mDebugging) - { - NOP; - } - int preserveIdx; BeMCInst* preserveInst; BeMCInst* restoreInst; @@ -13123,7 +13213,7 @@ void BeMCContext::DoCodeEmission() X64CPURegister srcReg = X64Reg_R11; int destOfs = 0; int srcOfs = 0; - + if (inst->mArg1) { BF_ASSERT(inst->mArg1.mKind == BeMCOperandKind_VRegPair); @@ -13363,18 +13453,8 @@ void BeMCContext::DoCodeEmission() break; case BeMCInstKind_Mov: { - if (mDebugging) - { - NOP; - } - if (inst->mArg1.mKind == BeMCOperandKind_ConstAgg) { - if (mDebugging) - { - NOP; - } - EmitAggMov(inst->mArg0, inst->mArg1); break; } @@ -15850,7 +15930,7 @@ void BeMCContext::Generate(BeFunction* function) mDbgPreferredRegs[32] = X64Reg_R8;*/ //mDbgPreferredRegs[8] = X64Reg_RAX; - //mDebugging = (function->mName == "??Kint2@bf@@SA?A01@01@0@Z"); + //mDebugging = (function->mName == "?Load@TestProgram@BeefTest@bf@@SA?AUHandle@23@XZ"); // || (function->mName == "?MethodA@TestProgram@BeefTest@bf@@CAXXZ"); // || (function->mName == "?Hey@Blurg@bf@@SAXXZ") // ; @@ -16147,7 +16227,7 @@ void BeMCContext::Generate(BeFunction* function) mcMemberRef.mKind = BeMCOperandKind_VRegLoad; AllocInst(BeMCInstKind_Mov, mcMemberRef, mcValue); - // Our InsertValue always modifies the source aggregrate, it does not make a copy like LLVM's InsertValue would infer. + // Our InsertValue always modifies the source aggregate, it does not make a copy like LLVM's InsertValue would infer. // This is okay because of Beef front end knowledge, but is not general purpose. result = mcAgg; } @@ -16157,6 +16237,7 @@ void BeMCContext::Generate(BeFunction* function) auto castedInst = (BeNumericCastInst*)inst; auto mcValue = GetOperand(castedInst->mValue); auto fromType = GetType(mcValue); + if (fromType == castedInst->mToType) { // If it's just a sign change then leave it alone @@ -16179,6 +16260,10 @@ void BeMCContext::Generate(BeFunction* function) bool doSignExtension = (toType->IsIntable()) && (fromType->IsIntable()) && (toType->mSize > fromType->mSize) && (castedInst->mToSigned) && (castedInst->mValSigned); if ((toType->IsFloat()) && (fromType->IsIntable()) && (castedInst->mValSigned)) doSignExtension = true; + + if (mcValue.IsImmediate()) + doSignExtension = false; + if (doSignExtension) { AllocInst(BeMCInstKind_MovSX, toValue, mcValue); @@ -16663,7 +16748,16 @@ void BeMCContext::Generate(BeFunction* function) auto castedInst = (BeLifetimeEndInst*)inst; auto mcPtr = GetOperand(castedInst->mPtr, false, true); if (mcPtr) - AllocInst(BeMCInstKind_LifetimeStart, mcPtr); + { + auto vregInfo = GetVRegInfo(mcPtr); + if ((vregInfo != NULL) && (vregInfo->mHasDynLife)) + { + // This alloca had an assignment (ie: `mov vregX, [RBP+homeSize0]`) so it must be defined at the mov + // This may indicate incorrectly generated code where we thought an alloca would be in the head but it isn't + } + else + AllocInst(BeMCInstKind_LifetimeStart, mcPtr); + } } break; case BeLifetimeExtendInst::TypeId: @@ -17514,6 +17608,40 @@ void BeMCContext::Generate(BeFunction* function) AllocInst(BeMCInstKind_DbgBreak); } break; + case BfIRIntrinsic_Index: + { + auto valPtr = GetOperand(castedInst->mArgs[0].mValue); + auto idx = GetOperand(castedInst->mArgs[1].mValue); + + auto valType = GetType(valPtr); + if (!valType->IsPointer()) + { + SoftFail("Non-pointer index target", castedInst->mDbgLoc); + break; + } + valType = ((BePointerType*)valType)->mElementType; + + if (!valType->IsVector()) + { + SoftFail("Non-vector index target", castedInst->mDbgLoc); + break; + } + + auto vectorType = (BeVectorType*)valType; + + auto elementPtrType = mModule->mContext->GetPointerTo(vectorType->mElementType); + + result = AllocVirtualReg(elementPtrType); + CreateDefineVReg(result); + auto vregInfo = GetVRegInfo(result); + vregInfo->mRelTo = valPtr; + vregInfo->mRelOffset = idx; + vregInfo->mRelOffsetScale = vectorType->mElementType->mSize; + vregInfo->mIsExpr = true; + + result = CreateLoad(result); + } + break; case BfIRIntrinsic_MemSet: { if (auto constVal = BeValueDynCast(castedInst->mArgs[1].mValue)) diff --git a/IDEHelper/Backend/BeModule.cpp b/IDEHelper/Backend/BeModule.cpp index 3beea000..db13739b 100644 --- a/IDEHelper/Backend/BeModule.cpp +++ b/IDEHelper/Backend/BeModule.cpp @@ -564,7 +564,13 @@ void BeFunction::HashContent(BeHashContext& hashCtx) hashCtx.Mixin(TypeId); hashCtx.MixinStr(mName); hashCtx.Mixin(mLinkageType); - hashCtx.Mixin(mAlwaysInline); + hashCtx.Mixin(mAlwaysInline); + hashCtx.Mixin(mNoUnwind); + hashCtx.Mixin(mUWTable); + hashCtx.Mixin(mNoReturn); + hashCtx.Mixin(mNoFramePointerElim); + hashCtx.Mixin(mIsDLLExport); + hashCtx.Mixin(mIsDLLImport); hashCtx.Mixin(mCallingConv); for (auto block : mBlocks) diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index 2b7a57c8..c55e9d5d 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -346,6 +346,11 @@ void BfStructuralVisitor::Visit(BfStrideOfExpression* strideOfExpr) Visit(strideOfExpr->ToBase()); } +void BfStructuralVisitor::Visit(BfOffsetOfExpression* offsetOfExpr) +{ + Visit(offsetOfExpr->ToBase()); +} + void BfStructuralVisitor::Visit(BfDefaultExpression* defaultExpr) { Visit(defaultExpr->ToBase()); @@ -1195,6 +1200,23 @@ bool BfTypeReference::IsTypeDefTypeReference() return IsA() || IsA() || IsA(); } +String BfTypeReference::ToCleanAttributeString() +{ + // ToString might return something like "System.InlineAttribute", which we want to clean before we test for "Inline" + auto typeRefName = ToString(); + if (typeRefName.EndsWith("Attribute")) + { + int attribNameStart = (int)typeRefName.LastIndexOf('.'); + if (attribNameStart != -1) + typeRefName.Remove(0, attribNameStart + 1); + + if (typeRefName.EndsWith("Attribute")) + typeRefName.RemoveFromEnd(9); + } + + return typeRefName; +} + ////////////////////////////////////////////////////////////////////////// BfPropertyMethodDeclaration* BfPropertyDeclaration::GetMethod(const StringImpl& findName) @@ -1262,6 +1284,10 @@ bool BfAttributeDirective::Contains(const StringImpl& findName) return true; if (name.EndsWith("Attribute")) { + int attribNameStart = (int)name.LastIndexOf('.'); + if (attribNameStart != -1) + name.Remove(0, attribNameStart + 1); + name.RemoveToEnd(name.length() - 9); if (findName == name) return true; @@ -1377,6 +1403,8 @@ const char* Beefy::BfTokenToString(BfToken token) return "null"; case BfToken_Nullable: return "nullable"; + case BfToken_OffsetOf: + return "offsetof"; case BfToken_Operator: return "operator"; case BfToken_Out: @@ -1479,6 +1507,12 @@ const char* Beefy::BfTokenToString(BfToken token) return ">>="; case BfToken_AndEquals: return "&="; + case BfToken_AndMinus: + return "&-"; + case BfToken_AndPlus: + return "&+"; + case BfToken_AndStar: + return "&*"; case BfToken_OrEquals: return "|="; case BfToken_XorEquals: @@ -1518,6 +1552,8 @@ const char* Beefy::BfTokenToString(BfToken token) return ".."; case BfToken_DotDotDot: return "..."; + case BfToken_DotDotLess: + return "..<"; case BfToken_QuestionDot: return "?."; case BfToken_QuestionLBracket: @@ -1609,22 +1645,27 @@ int Beefy::BfGetBinaryOpPrecendence(BfBinaryOp binOp) switch (binOp) { case BfBinaryOp_Multiply: + case BfBinaryOp_OverflowMultiply: case BfBinaryOp_Divide: - case BfBinaryOp_Modulus: - return 13; + case BfBinaryOp_Modulus: + return 14; case BfBinaryOp_Add: - case BfBinaryOp_Subtract: - return 12; + case BfBinaryOp_Subtract: + case BfBinaryOp_OverflowAdd: + case BfBinaryOp_OverflowSubtract: + return 13; case BfBinaryOp_LeftShift: case BfBinaryOp_RightShift: - return 11; + return 12; case BfBinaryOp_BitwiseAnd: - return 10; + return 11; case BfBinaryOp_ExclusiveOr: - return 9; + return 10; case BfBinaryOp_BitwiseOr: + return 9; + case BfBinaryOp_Range: + case BfBinaryOp_ClosedRange: return 8; - // "Range" inserted here if we were copying swift case BfBinaryOp_Is: case BfBinaryOp_As: return 7; @@ -1661,6 +1702,9 @@ const char* Beefy::BfGetOpName(BfBinaryOp binOp) case BfBinaryOp_Add: return "+"; case BfBinaryOp_Subtract: return "-"; case BfBinaryOp_Multiply: return "*"; + case BfBinaryOp_OverflowAdd: return "&+"; + case BfBinaryOp_OverflowSubtract: return "&-"; + case BfBinaryOp_OverflowMultiply: return "&*"; case BfBinaryOp_Divide: return "/"; case BfBinaryOp_Modulus: return "%"; case BfBinaryOp_BitwiseAnd: return "&"; @@ -1682,6 +1726,8 @@ const char* Beefy::BfGetOpName(BfBinaryOp binOp) case BfBinaryOp_NullCoalesce: return "??"; case BfBinaryOp_Is: return "is"; case BfBinaryOp_As: return "as"; + case BfBinaryOp_Range: return "..<"; + case BfBinaryOp_ClosedRange: return "..."; default: return "???"; } } @@ -1707,6 +1753,10 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp) case BfUnaryOp_Mut: return "mut"; case BfUnaryOp_Params: return "params"; case BfUnaryOp_Cascade: return ".."; + case BfUnaryOp_FromEnd: return "^"; + case BfUnaryOp_PartialRangeUpTo: return "..<"; + case BfUnaryOp_PartialRangeThrough: return "..."; + case BfUnaryOp_PartialRangeFrom: return "..."; default: return "???"; } } @@ -1721,6 +1771,12 @@ BfBinaryOp Beefy::BfTokenToBinaryOp(BfToken token) return BfBinaryOp_Subtract; case BfToken_Star: return BfBinaryOp_Multiply; + case BfToken_AndPlus: + return BfBinaryOp_OverflowAdd; + case BfToken_AndMinus: + return BfBinaryOp_OverflowSubtract; + case BfToken_AndStar: + return BfBinaryOp_OverflowMultiply; case BfToken_ForwardSlash: return BfBinaryOp_Divide; case BfToken_Modulus: @@ -1759,6 +1815,10 @@ BfBinaryOp Beefy::BfTokenToBinaryOp(BfToken token) return BfBinaryOp_ConditionalOr; case BfToken_DblQuestion: return BfBinaryOp_NullCoalesce; + case BfToken_DotDotLess: + return BfBinaryOp_Range; + case BfToken_DotDotDot: + return BfBinaryOp_ClosedRange; default: return BfBinaryOp_None; } @@ -1796,6 +1856,12 @@ BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token) return BfUnaryOp_Params; case BfToken_DotDot: return BfUnaryOp_Cascade; + case BfToken_Carat: + return BfUnaryOp_FromEnd; + case BfToken_DotDotDot: + return BfUnaryOp_PartialRangeThrough; + case BfToken_DotDotLess: + return BfUnaryOp_PartialRangeUpTo; default: return BfUnaryOp_None; } diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index d103fac7..62a34a59 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -158,6 +158,7 @@ enum BfToken : uint8 BfToken_New, BfToken_Null, BfToken_Nullable, + BfToken_OffsetOf, BfToken_Operator, BfToken_Out, BfToken_Override, @@ -210,6 +211,9 @@ enum BfToken : uint8 BfToken_ShiftLeftEquals, BfToken_ShiftRightEquals, BfToken_AndEquals, + BfToken_AndMinus, + BfToken_AndPlus, + BfToken_AndStar, BfToken_OrEquals, BfToken_XorEquals, BfToken_NullCoalsceEquals, @@ -229,6 +233,7 @@ enum BfToken : uint8 BfToken_Dot, BfToken_DotDot, BfToken_DotDotDot, + BfToken_DotDotLess, BfToken_QuestionDot, BfToken_QuestionLBracket, BfToken_AutocompleteDot, @@ -363,6 +368,7 @@ class BfTypedValueExpression; class BfTypeAttrExpression; class BfSizeOfExpression; class BfAlignOfExpression; +class BfOffsetOfExpression; class BfStrideOfExpression; class BfDefaultExpression; class BfUninitializedExpression; @@ -486,6 +492,7 @@ public: virtual void Visit(BfSizeOfExpression* sizeOfExpr); virtual void Visit(BfAlignOfExpression* alignOfExpr); virtual void Visit(BfStrideOfExpression* strideOfExpr); + virtual void Visit(BfOffsetOfExpression* offsetOfExpr); virtual void Visit(BfDefaultExpression* defaultExpr); virtual void Visit(BfUninitializedExpression* uninitializedExpr); virtual void Visit(BfCheckTypeExpression* checkTypeExpr); @@ -1795,6 +1802,9 @@ enum BfBinaryOp BfBinaryOp_Add, BfBinaryOp_Subtract, BfBinaryOp_Multiply, + BfBinaryOp_OverflowAdd, + BfBinaryOp_OverflowSubtract, + BfBinaryOp_OverflowMultiply, BfBinaryOp_Divide, BfBinaryOp_Modulus, BfBinaryOp_BitwiseAnd, @@ -1815,7 +1825,9 @@ enum BfBinaryOp BfBinaryOp_ConditionalOr, BfBinaryOp_NullCoalesce, BfBinaryOp_Is, - BfBinaryOp_As + BfBinaryOp_As, + BfBinaryOp_Range, + BfBinaryOp_ClosedRange, }; enum BfAssignmentOp @@ -1853,7 +1865,11 @@ enum BfUnaryOp BfUnaryOp_Out, BfUnaryOp_Mut, BfUnaryOp_Params, - BfUnaryOp_Cascade + BfUnaryOp_Cascade, + BfUnaryOp_FromEnd, + BfUnaryOp_PartialRangeUpTo, + BfUnaryOp_PartialRangeThrough, + BfUnaryOp_PartialRangeFrom, }; class BfTokenNode : public BfAstNode @@ -2329,6 +2345,7 @@ public: bool IsNamedTypeReference(); bool IsTypeDefTypeReference(); + String ToCleanAttributeString(); }; BF_AST_DECL(BfTypeReference, BfAstNode); @@ -2639,7 +2656,7 @@ public: class BfAlignOfExpression : public BfTypeAttrExpression { public: - BF_AST_TYPE(BfAlignOfExpression, BfTypeAttrExpression); + BF_AST_TYPE(BfAlignOfExpression, BfTypeAttrExpression); }; BF_AST_DECL(BfAlignOfExpression, BfTypeAttrExpression); class BfStrideOfExpression : public BfTypeAttrExpression @@ -2648,6 +2665,15 @@ public: BF_AST_TYPE(BfStrideOfExpression, BfTypeAttrExpression); }; BF_AST_DECL(BfStrideOfExpression, BfTypeAttrExpression); +class BfOffsetOfExpression : public BfTypeAttrExpression +{ +public: + BF_AST_TYPE(BfOffsetOfExpression, BfTypeAttrExpression); + + BfTokenNode* mCommaToken; + BfIdentifierNode* mMemberName; +}; BF_AST_DECL(BfOffsetOfExpression, BfTypeAttrExpression); + class BfDefaultExpression : public BfExpression { public: @@ -3073,6 +3099,7 @@ public: BfPropertyDeclaration* mPropertyDeclaration; BfAttributeDirective* mAttributes; BfAstNode* mProtectionSpecifier; + BfTokenNode* mSetRefSpecifier; BfTokenNode* mMutSpecifier; BfIdentifierNode* mNameNode; BfTokenNode* mFatArrowToken; diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 1120e462..aca82e51 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -27,8 +27,15 @@ AutoCompleteBase::~AutoCompleteBase() AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry, const StringImpl& filter) { - if (!DoesFilterMatch(entry.mDisplay, filter.c_str())) - return NULL; + if ((!DoesFilterMatch(entry.mDisplay, filter.c_str())) || (entry.mNamePrefixCount < 0)) + return NULL; + return AddEntry(entry); +} + +AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry, const char* filter) +{ + if ((!DoesFilterMatch(entry.mDisplay, filter)) || (entry.mNamePrefixCount < 0)) + return NULL; return AddEntry(entry); } @@ -44,9 +51,10 @@ AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry) { insertedEntry->mEntryType = entry.mEntryType; - int size = (int)strlen(entry.mDisplay) + 1; + const char* display = entry.mDisplay; + int size = (int)strlen(display) + 1; insertedEntry->mDisplay = (char*)mAlloc.AllocBytes(size); - memcpy((char*)insertedEntry->mDisplay, entry.mDisplay, size); + memcpy((char*)insertedEntry->mDisplay, display, size); } return insertedEntry; @@ -445,7 +453,7 @@ bool BfAutoComplete::IsAttribute(BfTypeInstance* typeInst) auto checkTypeInst = typeInst; while (checkTypeInst != NULL) { - if (checkTypeInst->mTypeDef == mModule->mCompiler->mAttributeTypeDef) + if (checkTypeInst->mTypeDef->GetLatest() == mModule->mCompiler->mAttributeTypeDef->GetLatest()) return true; checkTypeInst = checkTypeInst->mBaseType; @@ -455,8 +463,15 @@ bool BfAutoComplete::IsAttribute(BfTypeInstance* typeInst) void BfAutoComplete::AddMethod(BfTypeInstance* typeInstance, BfMethodDef* methodDef, BfMethodInstance* methodInstance, BfMethodDeclaration* methodDecl, const StringImpl& methodName, const StringImpl& filter) { + int wantPrefixCount = 0; + const char* filterStr = filter.c_str(); + while (filterStr[0] == '@') + { + filterStr++; + wantPrefixCount++; + } String replaceName; - AutoCompleteEntry entry("method", methodName); + AutoCompleteEntry entry("method", methodName, methodDef->mNamePrefixCount - wantPrefixCount); if (methodDecl != NULL) { if (methodDecl->mMixinSpecifier != NULL) @@ -470,7 +485,7 @@ void BfAutoComplete::AddMethod(BfTypeInstance* typeInstance, BfMethodDef* method if (methodDef->mMethodType == BfMethodType_Extension) entry.mEntryType = "extmethod"; - if (auto entryAdded = AddEntry(entry, filter)) + if (auto entryAdded = AddEntry(entry, filterStr)) { if (methodDecl != NULL) { @@ -515,6 +530,8 @@ void BfAutoComplete::AddMethod(BfTypeInstance* typeInstance, BfMethodDef* method void BfAutoComplete::AddTypeDef(BfTypeDef* typeDef, const StringImpl& filter, bool onlyAttribute) { + BF_ASSERT(typeDef->mDefState != BfTypeDef::DefState_Emitted); + if (typeDef->mTypeDeclaration == NULL) return; @@ -565,10 +582,14 @@ void BfAutoComplete::AddTypeDef(BfTypeDef* typeDef, const StringImpl& filter, bo { if ((CheckDocumentation(entryAdded, NULL)) && (entryAdded->mDocumentation == NULL)) { - auto typeInst = mModule->ResolveTypeDef(typeDef, BfPopulateType_IdentityNoRemapAlias); + auto type = mModule->ResolveTypeDef(typeDef, BfPopulateType_IdentityNoRemapAlias); StringT<1024> str; - if (typeInst != NULL) - str = mModule->TypeToString(typeInst, BfTypeNameFlag_ExtendedInfo); + if (type != NULL) + { + SetAndRestoreValue prevTypeInst(mModule->mCurTypeInstance, type->ToTypeInstance()); + SetAndRestoreValue prevMethodInst(mModule->mCurMethodInstance, NULL); + str = mModule->TypeToString(type, (BfTypeNameFlags)(BfTypeNameFlag_ExtendedInfo | BfTypeNameFlag_ResolveGenericParamNames)); + } if (typeDef->mTypeDeclaration->mDocumentation != NULL) { if (!str.IsEmpty()) @@ -580,8 +601,13 @@ void BfAutoComplete::AddTypeDef(BfTypeDef* typeDef, const StringImpl& filter, bo } } -bool BfAutoComplete::CheckProtection(BfProtection protection, bool allowProtected, bool allowPrivate) +bool BfAutoComplete::CheckProtection(BfProtection protection, BfTypeDef* typeDef, bool allowProtected, bool allowPrivate) { + if ((protection == BfProtection_Internal) && (typeDef != NULL)) + { + return mModule->CheckProtection(protection, typeDef, allowProtected, allowPrivate); + } + return (mHasFriendSet) || (protection == BfProtection_Public) || ((protection == BfProtection_Protected) && (allowProtected)) || ((protection == BfProtection_Private) && (allowPrivate)); @@ -603,7 +629,7 @@ void BfAutoComplete::AddInnerTypes(BfTypeInstance* typeInst, const StringImpl& f { for (auto innerType : typeInst->mTypeDef->mNestedTypes) { - if (CheckProtection(innerType->mProtection, allowProtected, allowPrivate)) + if (CheckProtection(innerType->mProtection, innerType, allowProtected, allowPrivate)) AddTypeDef(innerType, filter); } @@ -615,9 +641,9 @@ void BfAutoComplete::AddInnerTypes(BfTypeInstance* typeInst, const StringImpl& f void BfAutoComplete::AddCurrentTypes(BfTypeInstance* typeInst, const StringImpl& filter, bool allowProtected, bool allowPrivate, bool onlyAttribute) { if (typeInst != mModule->mCurTypeInstance) - AddTypeDef(typeInst->mTypeDef, filter, onlyAttribute); + AddTypeDef(typeInst->mTypeDef->GetDefinition(), filter, onlyAttribute); - auto typeDef = typeInst->mTypeDef; + auto typeDef = typeInst->mTypeDef->GetDefinition(); for (auto nestedTypeDef : typeDef->mNestedTypes) { if (nestedTypeDef->mIsPartial) @@ -627,7 +653,7 @@ void BfAutoComplete::AddCurrentTypes(BfTypeInstance* typeInst, const StringImpl& continue; } - if (CheckProtection(nestedTypeDef->mProtection, allowProtected, allowPrivate)) + if (CheckProtection(nestedTypeDef->mProtection, nestedTypeDef, allowProtected, allowPrivate)) AddTypeDef(nestedTypeDef, filter, onlyAttribute); } @@ -643,8 +669,15 @@ void BfAutoComplete::AddCurrentTypes(BfTypeInstance* typeInst, const StringImpl& void BfAutoComplete::AddField(BfTypeInstance* typeInst, BfFieldDef* fieldDef, BfFieldInstance* fieldInstance, const StringImpl& filter) { - AutoCompleteEntry entry(GetTypeName(fieldInstance->mResolvedType), fieldDef->mName); - if (auto entryAdded = AddEntry(entry, filter)) + int wantPrefixCount = 0; + const char* filterStr = filter.c_str(); + while (filterStr[0] == '@') + { + filterStr++; + wantPrefixCount++; + } + AutoCompleteEntry entry(GetTypeName(fieldInstance->mResolvedType), fieldDef->mName, fieldDef->mNamePrefixCount - wantPrefixCount); + if (auto entryAdded = AddEntry(entry, filterStr)) { auto documentation = (fieldDef->mFieldDeclaration != NULL) ? fieldDef->mFieldDeclaration->mDocumentation : NULL; if (CheckDocumentation(entryAdded, documentation)) @@ -682,11 +715,18 @@ void BfAutoComplete::AddField(BfTypeInstance* typeInst, BfFieldDef* fieldDef, Bf void BfAutoComplete::AddProp(BfTypeInstance* typeInst, BfPropertyDef* propDef, const StringImpl& filter) { + int wantPrefixCount = 0; + const char* filterStr = filter.c_str(); + while (filterStr[0] == '@') + { + filterStr++; + wantPrefixCount++; + } BfCommentNode* documentation = NULL; if (propDef->mFieldDeclaration != NULL) documentation = propDef->mFieldDeclaration->mDocumentation; - AutoCompleteEntry entry("property", propDef->mName); - if (auto entryAdded = AddEntry(entry, filter)) + AutoCompleteEntry entry("property", propDef->mName, propDef->mNamePrefixCount - wantPrefixCount); + if (auto entryAdded = AddEntry(entry, filterStr)) { if (CheckDocumentation(entryAdded, documentation)) { @@ -881,7 +921,7 @@ void BfAutoComplete::AddSelfResultTypeMembers(BfTypeInstance* typeInst, BfTypeIn if (fieldDef->mIsNoShow) continue; - if ((fieldDef->mIsStatic) && (CheckProtection(fieldDef->mProtection, allowProtected, allowPrivate))) + if ((fieldDef->mIsStatic) && (CheckProtection(fieldDef->mProtection, fieldDef->mDeclaringType, allowProtected, allowPrivate))) { if (!mModule->CanCast(BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), fieldInst.mResolvedType), selfType)) continue; @@ -914,7 +954,7 @@ void BfAutoComplete::AddSelfResultTypeMembers(BfTypeInstance* typeInst, BfTypeIn bool canUseMethod; canUseMethod = (methodDef->mMethodType == BfMethodType_Normal) || (methodDef->mMethodType == BfMethodType_Mixin); - canUseMethod &= CheckProtection(methodDef->mProtection, allowProtected, allowPrivate); + canUseMethod &= CheckProtection(methodDef->mProtection, methodDef->mDeclaringType, allowProtected, allowPrivate); if (methodDef->mMethodType != BfMethodType_Normal) continue; @@ -967,7 +1007,7 @@ void BfAutoComplete::AddSelfResultTypeMembers(BfTypeInstance* typeInst, BfTypeIn if (methodInstance->mReturnType != selfType) continue; - if (CheckProtection(propDef->mProtection, allowProtected, allowPrivate)) + if (CheckProtection(propDef->mProtection, propDef->mDeclaringType, allowProtected, allowPrivate)) { if (propDef->HasExplicitInterface()) continue; @@ -987,11 +1027,12 @@ void BfAutoComplete::AddSelfResultTypeMembers(BfTypeInstance* typeInst, BfTypeIn bool BfAutoComplete::InitAutocomplete(BfAstNode* dotNode, BfAstNode* nameNode, String& filter) { + bool isDot = (dotNode != NULL) && (dotNode->mToken == BfToken_Dot); if (IsAutocompleteNode(nameNode)) { auto bfParser = nameNode->GetSourceData()->ToParser(); - if (mIsGetDefinition) + if ((mIsGetDefinition) || (!isDot)) { mInsertStartIdx = nameNode->GetSrcStart(); mInsertEndIdx = nameNode->GetSrcEnd(); @@ -1002,11 +1043,15 @@ bool BfAutoComplete::InitAutocomplete(BfAstNode* dotNode, BfAstNode* nameNode, S mInsertEndIdx = std::min(bfParser->mCursorIdx + 1, nameNode->GetSrcEnd()); } - filter.Append(bfParser->mSrc + mInsertStartIdx, mInsertEndIdx - mInsertStartIdx); + filter.Append(bfParser->mSrc + nameNode->GetSrcStart(), mInsertEndIdx - nameNode->GetSrcStart()); return true; } - if ((dotNode != NULL) && (IsAutocompleteNode(dotNode, 0, 1))) + int lenAdd = 0; + if (!isDot) + lenAdd++; + + if ((dotNode != NULL) && (IsAutocompleteNode(dotNode, lenAdd, 1))) { mInsertStartIdx = dotNode->GetSrcEnd(); mInsertEndIdx = dotNode->GetSrcEnd(); @@ -1027,7 +1072,7 @@ void BfAutoComplete::AddEnumTypeMembers(BfTypeInstance* typeInst, const StringIm auto fieldDef = fieldInst.GetFieldDef(); if ((fieldDef != NULL) && (fieldDef->mIsConst) && ((fieldInst.mResolvedType == typeInst) || (fieldInst.mIsEnumPayloadCase)) && - (CheckProtection(fieldDef->mProtection, allowProtected, allowPrivate))) + (CheckProtection(fieldDef->mProtection, fieldDef->mDeclaringType, allowProtected, allowPrivate))) { if ((!typeInst->IsTypeMemberIncluded(fieldDef->mDeclaringType, activeTypeDef, mModule)) || (!typeInst->IsTypeMemberAccessible(fieldDef->mDeclaringType, activeTypeDef))) @@ -1077,7 +1122,7 @@ void BfAutoComplete::AddExtensionMethods(BfTypeInstance* targetType, BfTypeInsta continue; bool canUseMethod = true; - canUseMethod &= CheckProtection(methodDef->mProtection, allowProtected, allowPrivate); + canUseMethod &= CheckProtection(methodDef->mProtection, methodDef->mDeclaringType, allowProtected, allowPrivate); auto methodInstance = mModule->GetRawMethodInstanceAtIdx(extensionContainer, methodDef->mIdx); if (methodInstance == NULL) @@ -1417,9 +1462,9 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress } } - if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mTypeInstance != NULL)) + if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mType != NULL)) { - BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mTypeInstance); + BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mType); BfGlobalLookup globalLookup; globalLookup.mKind = BfGlobalLookup::Kind_All; @@ -1492,9 +1537,9 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress (*findIdx)++; } - if (*findIdx == varSkipCount) + if (varSkipCount - local->mNamePrefixCount - *findIdx == 0) { - if ((AddEntry(AutoCompleteEntry(GetTypeName(local->mResolvedType), local->mName), wantName)) && (mIsGetDefinition)) + if ((AddEntry(AutoCompleteEntry(GetTypeName(local->mResolvedType), local->mName, varSkipCount - local->mNamePrefixCount - *findIdx), wantName)) && (mIsGetDefinition)) { } } @@ -1558,9 +1603,10 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress "alignof", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "default", "defer", "delegate", "delete", "do", "else", "false", "finally", "fixed", "for", "function", "if", "implicit", "in", "internal", "is", "new", "mixin", "null", - "out", "params", "ref", "rettype", "return", + "offsetof", "out", "params", "ref", "rettype", "return", "sealed", "sizeof", "scope", "static", "strideof", "struct", "switch", /*"this",*/ "try", "true", "typeof", "unchecked", "using", "var", "virtual", "volatile", "where", "while", + "alloctype", "comptype", "decltype", "nullable", }; for (int i = 0; i < sizeof(tokens) / sizeof(char*); i++) @@ -1611,8 +1657,16 @@ String BfAutoComplete::GetFilter(BfAstNode* node) auto bfParser = node->GetSourceData()->ToParser(); int cursorIdx = bfParser->mCursorIdx; filter = filter.Substring(0, BF_CLAMP(cursorIdx - node->GetSrcStart(), 0, (int)filter.length())); - mInsertEndIdx = cursorIdx; + mInsertEndIdx = cursorIdx; } + + const char* cPtr = filter.c_str(); + while (cPtr[0] == '@') + { + mInsertStartIdx++; + cPtr++; + } + return filter; } @@ -1647,7 +1701,7 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken if (attrIdentifier != NULL) { BfAttributeState attributeState; - attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_MemberAccess); + attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_MemberAccess | BfAttributeTargets_Invocation); attributeState.mCustomAttributes = mModule->GetCustomAttributes(attrIdentifier->mAttributes, attributeState.mTarget); if ((attributeState.mCustomAttributes != NULL) && (attributeState.mCustomAttributes->Contains(mModule->mCompiler->mFriendAttributeTypeDef))) { @@ -1782,9 +1836,9 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken checkTypeInst = mModule->GetOuterType(checkTypeInst); } - if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mTypeInstance != NULL)) + if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mType != NULL)) { - BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mTypeInstance); + BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mType); BfGlobalLookup globalLookup; globalLookup.mKind = BfGlobalLookup::Kind_All; @@ -1905,6 +1959,8 @@ bool BfAutoComplete::CheckExplicitInterface(BfTypeInstance* interfaceType, BfAst else return false; + mModule->PopulateType(interfaceType, BfPopulateType_DataAndMethods); + String filter; if (isAutocompletingName) filter = GetFilter(memberName); @@ -2335,6 +2391,8 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho if (!isInterface) impl += "override "; + else if (methodDef->mIsStatic) + impl += "static "; BfType* propType = methodInst->mReturnType; if (methodDef->mMethodType == BfMethodType_PropertySetter) @@ -2536,7 +2594,7 @@ void BfAutoComplete::CheckProperty(BfPropertyDeclaration* propertyDeclaration) { BfTypeInstance* typeInst = NULL; - auto type = mModule->ResolveTypeRef(propertyDeclaration->mExplicitInterface, BfPopulateType_DataAndMethods); + auto type = mModule->ResolveTypeRef(propertyDeclaration->mExplicitInterface, BfPopulateType_Identity); if (type != NULL) typeInst = type->ToTypeInstance(); @@ -2615,7 +2673,7 @@ void BfAutoComplete::CheckVarResolution(BfAstNode* varTypeRef, BfType* resolvedT if (mResolveType == BfResolveType_GetResultString) { mResultString = ":"; - mResultString += mModule->TypeToString(resolvedType); + mResultString += mModule->TypeToString(resolvedType, (BfTypeNameFlags)(BfTypeNameFlag_ExtendedInfo | BfTypeNameFlag_ResolveGenericParamNames)); } } } @@ -2639,11 +2697,11 @@ void BfAutoComplete::CheckResult(BfAstNode* node, const BfTypedValue& typedValue auto constant = mModule->mBfIRBuilder->GetConstant(typedValue.mValue); if (BfIRConstHolder::IsInt(constant->mTypeCode)) { - mResultString = StrFormat("%lld", constant->mInt64); + mResultString = StrFormat(":%lld", constant->mInt64); } else if (BfIRConstHolder::IsFloat(constant->mTypeCode)) { - mResultString = StrFormat("%f", constant->mDouble); + mResultString = StrFormat(":%f", constant->mDouble); } } @@ -2967,6 +3025,9 @@ void BfAutoComplete::CheckInterfaceFixit(BfTypeInstance* typeInstance, BfAstNode if (typeInstance == NULL) return; + if (typeInstance->IsInterface()) + return; + for (auto& ifaceTypeInst : typeInstance->mInterfaces) { Array missingMethods; diff --git a/IDEHelper/Compiler/BfAutoComplete.h b/IDEHelper/Compiler/BfAutoComplete.h index ad9f035f..183bf514 100644 --- a/IDEHelper/Compiler/BfAutoComplete.h +++ b/IDEHelper/Compiler/BfAutoComplete.h @@ -15,10 +15,12 @@ public: const char* mEntryType; const char* mDisplay; const char* mDocumentation; + int8 mNamePrefixCount; public: AutoCompleteEntry() { + mNamePrefixCount = 0; } AutoCompleteEntry(const char* entryType, const char* display) @@ -26,6 +28,7 @@ public: mEntryType = entryType; mDisplay = display; mDocumentation = NULL; + mNamePrefixCount = 0; } AutoCompleteEntry(const char* entryType, const StringImpl& display) @@ -33,6 +36,15 @@ public: mEntryType = entryType; mDisplay = display.c_str(); mDocumentation = NULL; + mNamePrefixCount = 0; + } + + AutoCompleteEntry(const char* entryType, const StringImpl& display, int namePrefixCount) + { + mEntryType = entryType; + mDisplay = display.c_str(); + mDocumentation = NULL; + mNamePrefixCount = (int8)namePrefixCount; } bool operator==(const AutoCompleteEntry& other) const @@ -88,8 +100,9 @@ public: int mInsertStartIdx; int mInsertEndIdx; - bool DoesFilterMatch(const char* entry, const char* filter); + bool DoesFilterMatch(const char* entry, const char* filter); AutoCompleteEntry* AddEntry(const AutoCompleteEntry& entry, const StringImpl& filter); + AutoCompleteEntry* AddEntry(const AutoCompleteEntry& entry, const char* filter); AutoCompleteEntry* AddEntry(const AutoCompleteEntry& entry); AutoCompleteBase(); @@ -179,7 +192,7 @@ public: int mDefTypeGenericParamIdx; public: - bool CheckProtection(BfProtection protection, bool allowProtected, bool allowPrivate); + bool CheckProtection(BfProtection protection, BfTypeDef* typeDef, bool allowProtected, bool allowPrivate); String GetFilter(BfAstNode* node); const char* GetTypeName(BfType* type); int GetCursorIdx(BfAstNode* node); diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 86012be6..f7757a1d 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -382,6 +382,10 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mArray3TypeDef = NULL; mArray4TypeDef = NULL; mSpanTypeDef = NULL; + mRangeTypeDef = NULL; + mClosedRangeTypeDef = NULL; + mIndexTypeDef = NULL; + mIndexRangeTypeDef = NULL; mAttributeTypeDef = NULL; mAttributeUsageAttributeTypeDef = NULL; mClassVDataTypeDef = NULL; @@ -418,6 +422,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mCompilerTypeDef = NULL; mDiagnosticsDebugTypeDef = NULL; mIDisposableTypeDef = NULL; + mIIntegerTypeDef = NULL; mIPrintableTypeDef = NULL; mIHashableTypeDef = NULL; mIComptimeTypeApply = NULL; @@ -520,11 +525,12 @@ bool BfCompiler::IsTypeUsed(BfType* checkType, BfProject* curProject) BfTypeInstance* typeInst = checkType->ToTypeInstance(); if (typeInst != NULL) { - if ((typeInst->mTypeDef->mProject != NULL) && (typeInst->mTypeDef->mProject != curProject)) - { - if (typeInst->mTypeDef->mProject->mTargetType == BfTargetType_BeefDynLib) - return false; - } + //TODO: Why was this here? +// if ((typeInst->mTypeDef->mProject != NULL) && (typeInst->mTypeDef->mProject != curProject)) +// { +// if (typeInst->mTypeDef->mProject->mTargetType == BfTargetType_BeefDynLib) +// return false; +// } if (checkType->IsInterface()) return typeInst->mIsReified; @@ -1216,11 +1222,12 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) baseType = baseType->mBaseType; } - if (module->mProject != bfModule->mProject) - { - if ((module->mProject != NULL) && (module->mProject->mTargetType == BfTargetType_BeefDynLib)) - continue; - } + //TODO: What was this for? +// if (module->mProject != bfModule->mProject) +// { +// if ((module->mProject != NULL) && (module->mProject->mTargetType == BfTargetType_BeefDynLib)) +// continue; +// } if (typeInst->mHasStaticInitMethod) sortedStaticInitMap.insert(std::make_pair(bfModule->TypeToString(type), typeInst)); @@ -1291,6 +1298,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) } BfTypeInstance* stringType = bfModule->ResolveTypeDef(mStringTypeDef, BfPopulateType_Data)->ToTypeInstance(); + BfTypeInstance* stringViewType = bfModule->ResolveTypeDef(mStringViewTypeDef, BfPopulateType_Data)->ToTypeInstance(); BfTypeInstance* reflectSpecializedTypeInstance = bfModule->ResolveTypeDef(mReflectSpecializedGenericType)->ToTypeInstance(); BfTypeInstance* reflectUnspecializedTypeInstance = bfModule->ResolveTypeDef(mReflectUnspecializedGenericType)->ToTypeInstance(); BfTypeInstance* reflectArrayTypeInstance = bfModule->ResolveTypeDef(mReflectArrayType)->ToTypeInstance(); @@ -1301,7 +1309,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) bool needsTypeList = bfModule->IsMethodImplementedAndReified(typeDefType, "GetType"); bool needsObjectTypeData = needsTypeList || bfModule->IsMethodImplementedAndReified(vdataContext->mBfObjectType, "RawGetType") || bfModule->IsMethodImplementedAndReified(vdataContext->mBfObjectType, "GetType"); bool needsTypeNames = bfModule->IsMethodImplementedAndReified(typeDefType, "GetName") || bfModule->IsMethodImplementedAndReified(typeDefType, "GetFullName"); - bool needsStringLiteralList = (mOptions.mAllowHotSwapping) || (bfModule->IsMethodImplementedAndReified(stringType, "Intern")); + bool needsStringLiteralList = (mOptions.mAllowHotSwapping) || (bfModule->IsMethodImplementedAndReified(stringType, "Intern")) || (bfModule->IsMethodImplementedAndReified(stringViewType, "Intern")); Dictionary usedStringIdMap; @@ -1661,8 +1669,18 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) if ((targetType == BfTargetType_BeefWindowsApplication) && (mOptions.mPlatformType != BfPlatformType_Windows)) targetType = BfTargetType_BeefConsoleApplication; - bool isPosixDynLib = (targetType == BfTargetType_BeefDynLib) && (mOptions.mPlatformType != BfPlatformType_Windows); + bool isConsoleApplication = (targetType == BfTargetType_BeefConsoleApplication) || (targetType == BfTargetType_BeefTest); + if ((targetType == BfTargetType_BeefWindowsApplication) && (mOptions.mPlatformType != BfPlatformType_Windows)) + isConsoleApplication = true; + + bool isDllMain = (targetType == BfTargetType_BeefLib_DynamicLib) && (mOptions.mPlatformType == BfPlatformType_Windows); + bool isPosixDynLib = ((targetType == BfTargetType_BeefLib_DynamicLib) || (targetType == BfTargetType_BeefLib_StaticLib)) && + (mOptions.mPlatformType != BfPlatformType_Windows); + bool mainHasArgs = (targetType != BfTargetType_BeefLib_DynamicLib) && (targetType != BfTargetType_BeefLib_StaticLib) && + (mOptions.mPlatformType != BfPlatformType_Wasm); + bool mainHasRet = ((targetType != BfTargetType_BeefLib_DynamicLib) && (targetType != BfTargetType_BeefLib_StaticLib)) || (isDllMain); + // Generate "main" if (!IsHotCompile()) { @@ -1670,7 +1688,9 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) BfIRFunctionType mainFuncType; BfIRFunction mainFunc; - if ((targetType == BfTargetType_BeefConsoleApplication) || (targetType == BfTargetType_BeefTest)) + BfIRFunctionType shutdownFuncType; + BfIRFunction shutdownFunc; + if (isConsoleApplication) { SmallVector paramTypes; paramTypes.push_back(int32Type); @@ -1679,7 +1699,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) mainFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "main"); bfModule->SetupIRMethod(NULL, mainFunc, false); } - else if (targetType == BfTargetType_BeefDynLib) + else if (isDllMain) { SmallVector paramTypes; paramTypes.push_back(nullPtrType); // hinstDLL @@ -1707,15 +1727,43 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) } else { - SmallVector paramTypes; - if (mOptions.mPlatformType != BfPlatformType_Wasm) + BfIRFunction combinedFunc; + + SmallVector paramTypes; + if (mainHasArgs) { paramTypes.push_back(int32Type); paramTypes.push_back(nullPtrType); } - mainFuncType = bfModule->mBfIRBuilder->CreateFunctionType(int32Type, paramTypes, false); - mainFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "BeefMain"); + mainFuncType = bfModule->mBfIRBuilder->CreateFunctionType(mainHasRet ? int32Type : voidType, paramTypes, false); + mainFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "BeefStart"); bfModule->SetupIRMethod(NULL, mainFunc, false); + + combinedFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "BeefMain"); + bfModule->SetupIRMethod(NULL, combinedFunc, false); + + paramTypes.clear(); + shutdownFuncType = bfModule->mBfIRBuilder->CreateFunctionType(voidType, paramTypes, false); + shutdownFunc = bfModule->mBfIRBuilder->CreateFunction(shutdownFuncType, BfIRLinkageType_External, "BeefStop"); + bfModule->SetupIRMethod(NULL, shutdownFunc, false); + + bfModule->mBfIRBuilder->SetActiveFunction(combinedFunc); + auto entryBlock = bfModule->mBfIRBuilder->CreateBlock("entry", true); + bfModule->mBfIRBuilder->SetInsertPoint(entryBlock); + + SmallVector args; + if (mainHasArgs) + { + args.push_back(bfModule->mBfIRBuilder->GetArgument(0)); + args.push_back(bfModule->mBfIRBuilder->GetArgument(1)); + } + auto res = bfModule->mBfIRBuilder->CreateCall(mainFunc, args); + args.clear(); + bfModule->mBfIRBuilder->CreateCall(shutdownFunc, args); + if (mainHasArgs) + bfModule->mBfIRBuilder->CreateRet(res); + else + bfModule->mBfIRBuilder->CreateRetVoid(); } bfModule->mBfIRBuilder->SetActiveFunction(mainFunc); @@ -1745,13 +1793,16 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) bfModule->SetupIRMethod(NULL, setCmdLineFunc, false); SmallVector args; - args.push_back(bfModule->mBfIRBuilder->GetArgument(0)); - args.push_back(bfModule->mBfIRBuilder->GetArgument(1)); + if (mainHasArgs) + { + args.push_back(bfModule->mBfIRBuilder->GetArgument(0)); + args.push_back(bfModule->mBfIRBuilder->GetArgument(1)); + } bfModule->mBfIRBuilder->CreateCall(setCmdLineFunc, args); } BfIRBlock initSkipBlock; - if (targetType == BfTargetType_BeefDynLib) + if (isDllMain) { auto initBlock = bfModule->mBfIRBuilder->CreateBlock("doInit", false); initSkipBlock = bfModule->mBfIRBuilder->CreateBlock("skipInit", false); @@ -1959,18 +2010,19 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) if (!hadRet) retValue = bfModule->GetConstValue32(0); - } - else if (targetType == BfTargetType_BeefDynLib) + } + else { - retValue = bfModule->GetConstValue32(1); + if (mainHasRet) + retValue = bfModule->GetConstValue32(1); } if (targetType == BfTargetType_BeefTest) EmitTestMethod(bfModule, testMethods, retValue); - + BfIRBlock deinitSkipBlock; - if (targetType == BfTargetType_BeefDynLib) - { + if (isDllMain) + { auto deinitBlock = bfModule->mBfIRBuilder->CreateBlock("doDeinit", false); deinitSkipBlock = bfModule->mBfIRBuilder->CreateBlock("skipDeinit", false); auto cmpResult = bfModule->mBfIRBuilder->CreateCmpEQ(bfModule->mBfIRBuilder->GetArgument(1), bfModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, 0)); @@ -1981,6 +2033,14 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) if (mOptions.mPlatformType != BfPlatformType_Wasm) { + auto prevBlock = bfModule->mBfIRBuilder->GetInsertBlock(); + if (shutdownFunc) + { + bfModule->mBfIRBuilder->SetActiveFunction(shutdownFunc); + auto entryBlock = bfModule->mBfIRBuilder->CreateBlock("entry", true); + bfModule->mBfIRBuilder->SetInsertPoint(entryBlock); + } + bfModule->mBfIRBuilder->CreateCall(dtorFunc, SizedArray()); BfModuleMethodInstance shutdownMethod = bfModule->GetInternalMethod("Shutdown"); @@ -1988,6 +2048,13 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) { bfModule->mBfIRBuilder->CreateCall(shutdownMethod.mFunc, SizedArray()); } + + if (shutdownFunc) + { + bfModule->mBfIRBuilder->CreateRetVoid(); + bfModule->mBfIRBuilder->SetActiveFunction(mainFunc); + bfModule->mBfIRBuilder->SetInsertPoint(prevBlock); + } } if (deinitSkipBlock) @@ -2025,9 +2092,6 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) bfModule->mBfIRBuilder->SetInsertPoint(entryBlock); SmallVector startArgs; - startArgs.push_back(bfModule->mBfIRBuilder->CreateConstNull()); - startArgs.push_back(bfModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, 1)); - startArgs.push_back(bfModule->mBfIRBuilder->CreateConstNull()); bfModule->mBfIRBuilder->CreateCall(mainFunc, startArgs); bfModule->mBfIRBuilder->CreateRetVoid(); @@ -2038,9 +2102,8 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule) bfModule->mBfIRBuilder->SetActiveFunction(func); entryBlock = bfModule->mBfIRBuilder->CreateBlock("main", true); bfModule->mBfIRBuilder->SetInsertPoint(entryBlock); - SmallVector stopArgs; - startArgs[1] = bfModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, 0); - bfModule->mBfIRBuilder->CreateCall(mainFunc, startArgs); + SmallVector stopArgs; + bfModule->mBfIRBuilder->CreateCall(shutdownFunc, startArgs); bfModule->mBfIRBuilder->CreateRetVoid(); } } @@ -2873,7 +2936,7 @@ void BfCompiler::UpdateRevisedTypes() auto typeDef = *typeDefItr; auto origTypeDef = typeDef; if (typeDef->mNextRevision != NULL) - typeDef = typeDef->mNextRevision; + typeDef = typeDef->mNextRevision; if (typeDef->mDupDetectedRevision == mRevision) { ++typeDefItr; @@ -4206,7 +4269,7 @@ void BfCompiler::ProcessAutocompleteTempType() #endif SetAndRestoreValue prevType(module->mCurTypeInstance, typeInst); - typeState.mTypeInstance = typeInst; + typeState.mType = typeInst; BfGenericExtensionEntry* genericExEntry = NULL; bool hadTempExtensionInfo = false; @@ -4242,7 +4305,7 @@ void BfCompiler::ProcessAutocompleteTempType() if (autoComplete->mResolveType == BfResolveType_GetResultString) { autoComplete->mResultString = ":"; - autoComplete->mResultString += module->TypeToString(typeInst); + autoComplete->mResultString += module->TypeToString(typeInst, (BfTypeNameFlags)(BfTypeNameFlag_ExtendedInfo | BfTypeNameFlag_ResolveGenericParamNames)); } } } @@ -4293,7 +4356,10 @@ void BfCompiler::ProcessAutocompleteTempType() if (fieldDef->mTypeRef != NULL) { - module->ResolveTypeRef(fieldDef->mTypeRef); + BfResolveTypeRefFlags flags = BfResolveTypeRefFlag_None; + if ((fieldDecl != NULL) && (fieldDecl->mInitializer != NULL)) + flags = (BfResolveTypeRefFlags)(flags | BfResolveTypeRefFlag_AllowInferredSizedArray); + module->ResolveTypeRef(fieldDef->mTypeRef, BfPopulateType_Identity, flags); } mResolvePassData->mAutoComplete->CheckTypeRef(fieldDef->mTypeRef, true); @@ -4514,7 +4580,8 @@ void BfCompiler::ProcessAutocompleteTempType() } } - if ((mResolvePassData->mAutoComplete->mDefType == actualTypeDef) && (mResolvePassData->mAutoComplete->mDefMethod != NULL)) + if ((mResolvePassData->mAutoComplete->mDefType != NULL) && (mResolvePassData->mAutoComplete->mDefType->GetDefinition() == actualTypeDef) && + (mResolvePassData->mAutoComplete->mDefMethod != NULL)) { BfMethodDef* tempDefMethod = NULL; for (auto checkMethod : tempTypeDef->mMethods) @@ -4607,7 +4674,7 @@ void BfCompiler::AddToRebuildTypeList(BfTypeInstance* typeInst, HashSetmParser != NULL) { // Only find references within the current file - if (!typeInst->mTypeDef->HasSource(mResolvePassData->mParser)) + if (!typeInst->mTypeDef->GetDefinition()->HasSource(mResolvePassData->mParser)) return; } @@ -4685,6 +4752,7 @@ void BfCompiler::GetSymbolReferences() if ((typeDef == NULL) || (typeDef->mTypeDeclaration == NULL)) return; + typeDef = typeDef->GetLatest(); mResolvePassData->mSymbolReferenceTypeDef = typeDef; auto replaceType = module->ResolveTypeDef(typeDef, BfPopulateType_IdentityNoRemapAlias); module->PopulateType(replaceType); @@ -4699,7 +4767,7 @@ void BfCompiler::GetSymbolReferences() for (auto type : mContext->mResolvedTypes) { auto typeInst = type->ToTypeInstance(); - if ((typeInst != replaceTypeInst) && (typeInst != NULL) && (typeInst->mTypeDef == typeDef)) + if ((typeInst != replaceTypeInst) && (typeInst != NULL) && (typeInst->mTypeDef->GetLatest() == typeDef)) AddDepsToRebuildTypeList(typeInst, rebuildTypeInstList); } } @@ -5312,9 +5380,14 @@ void BfCompiler::PopulateReified() BfLogSysM("PopulateReified iteration start\n"); - int typeCount = 0; + Array typeList; + typeList.Reserve(context->mResolvedTypes.mCount); for (auto type : context->mResolvedTypes) - { + typeList.Add(type); + + int typeCount = 0; + for (auto type : typeList) + { auto module = type->GetModule(); typeCount++; @@ -6652,7 +6725,11 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mArray2TypeDef = _GetRequiredType("System.Array2", 1); mArray3TypeDef = _GetRequiredType("System.Array3", 1); mArray4TypeDef = _GetRequiredType("System.Array4", 1); - mSpanTypeDef = _GetRequiredType("System.Span", 1); + mSpanTypeDef = _GetRequiredType("System.Span", 1); + mRangeTypeDef = _GetRequiredType("System.Range"); + mClosedRangeTypeDef = _GetRequiredType("System.ClosedRange"); + mIndexTypeDef = _GetRequiredType("System.Index"); + mIndexRangeTypeDef = _GetRequiredType("System.IndexRange"); mAttributeTypeDef = _GetRequiredType("System.Attribute"); mAttributeUsageAttributeTypeDef = _GetRequiredType("System.AttributeUsageAttribute"); mClassVDataTypeDef = _GetRequiredType("System.ClassVData"); @@ -6690,6 +6767,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mCompilerTypeDef = _GetRequiredType("System.Compiler"); mDiagnosticsDebugTypeDef = _GetRequiredType("System.Diagnostics.Debug"); mIDisposableTypeDef = _GetRequiredType("System.IDisposable"); + mIIntegerTypeDef = _GetRequiredType("System.IInteger"); mIPrintableTypeDef = _GetRequiredType("System.IPrintable"); mIHashableTypeDef = _GetRequiredType("System.IHashable"); mIComptimeTypeApply = _GetRequiredType("System.IComptimeTypeApply"); @@ -6765,7 +6843,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) } if ((bfProject->mTargetType != BfTargetType_BeefConsoleApplication) && (bfProject->mTargetType != BfTargetType_BeefWindowsApplication) && - (bfProject->mTargetType != BfTargetType_BeefDynLib) && + (bfProject->mTargetType != BfTargetType_BeefLib_DynamicLib) && (bfProject->mTargetType != BfTargetType_BeefLib_StaticLib) && (bfProject->mTargetType != BfTargetType_C_ConsoleApplication) && (bfProject->mTargetType != BfTargetType_C_WindowsApplication) && (bfProject->mTargetType != BfTargetType_BeefTest) && (bfProject->mTargetType != BfTargetType_BeefApplication_StaticLib) && (bfProject->mTargetType != BfTargetType_BeefApplication_DynamicLib)) @@ -6997,7 +7075,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) { if (!module->mIsSpecialModule) { - if ((module->mIsReified) && (module->mIsModuleMutable)) + if ((module->HasCompiledOutput()) && (module->mIsModuleMutable)) { module->Finish(); } @@ -7040,6 +7118,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mContext->mUnreifiedModule->ResolveTypeDef(typeDef, BfPopulateType_Full); } + Array typeWorkList; + for (auto type : mContext->mResolvedTypes) { auto module = type->GetModule(); @@ -7061,28 +7141,33 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) continue; if (!typeInst->IsSpecializedType()) - { - // Find any remaining methods for unreified processing - for (auto&& methodInstGroup : typeInst->mMethodInstanceGroups) + { + typeWorkList.Add(typeInst); + } + } + + for (auto typeInst : typeWorkList) + { + // Find any remaining methods for unreified processing + for (auto&& methodInstGroup : typeInst->mMethodInstanceGroups) + { + if ((methodInstGroup.mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference) || + (methodInstGroup.mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference)) { - if ((methodInstGroup.mOnDemandKind == BfMethodOnDemandKind_Decl_AwaitingReference) || - (methodInstGroup.mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference)) - { - if ((methodInstGroup.mDefault != NULL) && (methodInstGroup.mDefault->mIsForeignMethodDef)) - { - mContext->mUnreifiedModule->GetMethodInstance(typeInst, methodInstGroup.mDefault->mMethodDef, BfTypeVector(), - (BfGetMethodInstanceFlags)(BfGetMethodInstanceFlag_ForeignMethodDef | BfGetMethodInstanceFlag_UnspecializedPass | BfGetMethodInstanceFlag_ExplicitResolveOnlyPass)); - queuedMoreMethods = true; - } - else - { - auto methodDef = typeInst->mTypeDef->mMethods[methodInstGroup.mMethodIdx]; - if (methodDef->mMethodType == BfMethodType_Init) - continue; - mContext->mUnreifiedModule->GetMethodInstance(typeInst, methodDef, BfTypeVector(), - (BfGetMethodInstanceFlags)(BfGetMethodInstanceFlag_UnspecializedPass | BfGetMethodInstanceFlag_ExplicitResolveOnlyPass)); - queuedMoreMethods = true; - } + if ((methodInstGroup.mDefault != NULL) && (methodInstGroup.mDefault->mIsForeignMethodDef)) + { + mContext->mUnreifiedModule->GetMethodInstance(typeInst, methodInstGroup.mDefault->mMethodDef, BfTypeVector(), + (BfGetMethodInstanceFlags)(BfGetMethodInstanceFlag_ForeignMethodDef | BfGetMethodInstanceFlag_UnspecializedPass | BfGetMethodInstanceFlag_ExplicitResolveOnlyPass)); + queuedMoreMethods = true; + } + else + { + auto methodDef = typeInst->mTypeDef->mMethods[methodInstGroup.mMethodIdx]; + if (methodDef->mMethodType == BfMethodType_Init) + continue; + mContext->mUnreifiedModule->GetMethodInstance(typeInst, methodDef, BfTypeVector(), + (BfGetMethodInstanceFlags)(BfGetMethodInstanceFlag_UnspecializedPass | BfGetMethodInstanceFlag_ExplicitResolveOnlyPass)); + queuedMoreMethods = true; } } } @@ -7875,10 +7960,13 @@ void BfCompiler::GenerateAutocompleteInfo() } else methodText += bfModule->TypeToString(type, BfTypeNameFlag_ResolveGenericParamNames, genericMethodNameOverridesPtr); - methodInstance->GetParamName(paramIdx, paramName); + int namePrefixCount = 0; + methodInstance->GetParamName(paramIdx, paramName, namePrefixCount); if (!paramName.IsEmpty()) { methodText += " "; + for (int i = 0; i < namePrefixCount; i++) + methodText += "@"; methodText += paramName; } @@ -7929,16 +8017,18 @@ void BfCompiler::GenerateAutocompleteInfo() continue; autoCompleteResultString += String(entry->mEntryType); - autoCompleteResultString += "\t"; + autoCompleteResultString += '\t'; + for (int i = 0; i < entry->mNamePrefixCount; i++) + autoCompleteResultString += '@'; autoCompleteResultString += String(entry->mDisplay); if (entry->mDocumentation != NULL) - { + { autoCompleteResultString += '\x03'; autoCompleteResultString += entry->mDocumentation; } - autoCompleteResultString += "\n"; + autoCompleteResultString += '\n'; } } } @@ -7959,9 +8049,9 @@ String BfCompiler::GetTypeDefList() if (projectIds.TryAdd(curProject, NULL, &projectIdPtr)) { *projectIdPtr = (int)projectIds.size() - 1; - result += "+"; + result += '+'; result += curProject->mName; - result += "\n"; + result += '\n'; } else { @@ -7975,21 +8065,21 @@ String BfCompiler::GetTypeDefList() { if (typeDef->IsGlobalsContainer()) { - result += "g"; + result += 'g'; if (!typeDef->mNamespace.IsEmpty()) { typeDef->mNamespace.ToString(result); - result += "."; + result += '.'; } result += ":static\n"; continue; } else if (typeDef->mTypeCode == BfTypeCode_Interface) - result += "i"; + result += 'i'; else if (typeDef->mTypeCode == BfTypeCode_Object) - result += "c"; + result += 'c'; else - result += "v"; + result += 'v'; result += BfTypeUtils::TypeToString(typeDef, BfTypeNameFlag_InternalName) + "\n"; } } @@ -8629,11 +8719,13 @@ int BfCompiler::GetEmitSource(const StringImpl& fileName, StringImpl* outBuffer) return -1; auto typeDef = typeInst->mTypeDef; - - if (typeDef->mEmitParser == NULL) + if (typeDef->mEmitParent == NULL) + return -1; + auto emitParser = typeDef->mSource->ToParser(); + if (emitParser == NULL) return -1; if (outBuffer != NULL) - outBuffer->Append(typeDef->mEmitParser->mSrc, typeDef->mEmitParser->mSrcLength); + outBuffer->Append(emitParser->mSrc, emitParser->mSrcLength); return typeInst->mRevision; } @@ -9058,8 +9150,9 @@ BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetUsedOutputFileNames(BfCompiler* canReference = false; if (bfProject != project) { - if (project->mTargetType == BfTargetType_BeefDynLib) - canReference = false; + //TODO: What was this for? +// if (project->mTargetType == BfTargetType_BeefDynLib) +// canReference = false; } } if (!canReference) @@ -9142,7 +9235,8 @@ BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetSymbolReferences(BfCompiler* bfC std::map sortedParserMap; for (auto& parserDataPair : resolvePassData->mFoundSymbolReferencesParserData) { - sortedParserMap.insert(std::make_pair(parserDataPair.mKey->mFileName, &parserDataPair.mValue)); + if (!parserDataPair.mKey->mFileName.Contains('|')) + sortedParserMap.insert(std::make_pair(parserDataPair.mKey->mFileName, &parserDataPair.mValue)); } for (auto& parserData : sortedParserMap) @@ -9268,10 +9362,9 @@ BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(BfCompiler* bfCompiler, BfProje options->mCLongSize = 4; if ((options->mMachineType == BfMachineType_AArch64) || (options->mMachineType == BfMachineType_x64)) { - if ((options->mPlatformType == BfPlatformType_macOS) || (options->mPlatformType == BfPlatformType_iOS) || - (options->mPlatformType == BfPlatformType_Linux) || (options->mPlatformType == BfPlatformType_Android)) + if (options->mPlatformType != BfPlatformType_Windows) options->mCLongSize = 8; - } + } bfCompiler->mCodeGen.SetMaxThreads(maxWorkerThreads); diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index 03bcaec0..d859da0f 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -347,6 +347,10 @@ public: BfTypeDef* mArray3TypeDef; BfTypeDef* mArray4TypeDef; BfTypeDef* mSpanTypeDef; + BfTypeDef* mRangeTypeDef; + BfTypeDef* mClosedRangeTypeDef; + BfTypeDef* mIndexTypeDef; + BfTypeDef* mIndexRangeTypeDef; BfTypeDef* mClassVDataTypeDef; @@ -372,6 +376,7 @@ public: BfTypeDef* mCompilerTypeDef; BfTypeDef* mDiagnosticsDebugTypeDef; BfTypeDef* mIDisposableTypeDef; + BfTypeDef* mIIntegerTypeDef; BfTypeDef* mIPrintableTypeDef; BfTypeDef* mIHashableTypeDef; BfTypeDef* mIComptimeTypeApply; diff --git a/IDEHelper/Compiler/BfConstResolver.cpp b/IDEHelper/Compiler/BfConstResolver.cpp index 64e24e7d..6bb58881 100644 --- a/IDEHelper/Compiler/BfConstResolver.cpp +++ b/IDEHelper/Compiler/BfConstResolver.cpp @@ -116,7 +116,7 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo if ((mResult) && (wantType != NULL)) { auto typeInst = mResult.mType->ToTypeInstance(); - if ((typeInst != NULL) && (typeInst->mTypeDef == mModule->mCompiler->mStringTypeDef)) + if ((typeInst != NULL) && (typeInst->IsInstanceOf(mModule->mCompiler->mStringTypeDef))) { BfType* toType = wantType; if (toType == NULL) @@ -234,7 +234,10 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo mModule->FixIntUnknown(mResult); if ((flags & BfConstResolveFlag_NoActualizeValues) == 0) + { + prevIgnoreWrites.Restore(); mModule->FixValueActualization(mResult, !prevIgnoreWrites.mPrevVal || ((flags & BfConstResolveFlag_ActualizeValues) != 0)); + } return mResult; } diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index 412c29e1..2de7a71a 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/IDEHelper/Compiler/BfContext.cpp @@ -61,12 +61,14 @@ BfContext::BfContext(BfCompiler* compiler) : mScratchModule->mIsSpecialModule = true; mScratchModule->mIsScratchModule = true; mScratchModule->mIsReified = true; + mScratchModule->mGeneratesCode = false; mScratchModule->Init(); mUnreifiedModule = new BfModule(this, ""); mUnreifiedModule->mIsSpecialModule = true; mUnreifiedModule->mIsScratchModule = true; mUnreifiedModule->mIsReified = false; + mUnreifiedModule->mGeneratesCode = false; mUnreifiedModule->Init(); mValueTypeDeinitSentinel = (BfMethodInstance*)1; @@ -158,7 +160,7 @@ void BfContext::AssignModule(BfType* type) // We used to have this "IsReified" check, but we DO want to create modules for unreified types even if they remain unused. // What was that IsReified check catching? // It screwed up the reification of generic types- they just got switched to mScratchModule from mUnreifiedModule, but didn't ever generate code. - if (/*(!type->IsReified()) ||*/ (type->IsUnspecializedType()) || (type->IsInterface()) || (type->IsVar()) || (type->IsTypeAlias()) || (type->IsFunction())) + if (/*(!type->IsReified()) ||*/ (type->IsUnspecializedType()) || (type->IsVar()) || (type->IsTypeAlias()) || (type->IsFunction())) { if (typeInst->mIsReified) module = mScratchModule; @@ -203,7 +205,7 @@ void BfContext::AssignModule(BfType* type) { String moduleName = GenerateModuleName(typeInst); module = new BfModule(this, moduleName); - module->mIsReified = typeInst->mIsReified; + module->mIsReified = typeInst->mIsReified; module->mProject = project; typeInst->mModule = module; BF_ASSERT(!mLockModules); @@ -221,6 +223,8 @@ void BfContext::AssignModule(BfType* type) module->mOwnedTypeInstances.push_back(localTypeInst); } + module->CalcGeneratesCode(); + if (needsModuleInit) module->Init(); } @@ -463,6 +467,8 @@ bool BfContext::ProcessWorkList(bool onlyReifiedTypes, bool onlyReifiedMethods) else { module->PopulateType(typeInst); + if (methodSpecializationRequest.mMethodIdx >= typeInst->mTypeDef->mMethods.mSize) + continue; methodDef = typeInst->mTypeDef->mMethods[methodSpecializationRequest.mMethodIdx]; } @@ -513,7 +519,7 @@ bool BfContext::ProcessWorkList(bool onlyReifiedTypes, bool onlyReifiedMethods) auto owner = methodInstance->mMethodInstanceGroup->mOwner; BF_ASSERT(!module->mAwaitingFinish); - if ((resolveParser != NULL) && (methodInstance->mMethodDef->mDeclaringType != NULL) && (methodInstance->mMethodDef->mDeclaringType->mSource != resolveParser)) + if ((resolveParser != NULL) && (methodInstance->mMethodDef->mDeclaringType != NULL) && (methodInstance->mMethodDef->mDeclaringType->GetDefinition()->mSource != resolveParser)) { continue; } @@ -722,6 +728,8 @@ bool BfContext::ProcessWorkList(bool onlyReifiedTypes, bool onlyReifiedMethods) void BfContext::HandleChangedTypeDef(BfTypeDef* typeDef, bool isAutoCompleteTempType) { + BF_ASSERT(typeDef->mEmitParent == NULL); + if ((mCompiler->mResolvePassData == NULL) || (!typeDef->HasSource(mCompiler->mResolvePassData->mParser))) return; @@ -834,7 +842,7 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild { return; } - + type->mDirty = true; bool wantDeleteType = (type->IsOnDemand()) && (deleteOnDemandTypes); @@ -866,6 +874,8 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild return; } + BF_ASSERT_REL(typeInst->mDefineState != BfTypeDefineState_DefinedAndMethodsSlotting); + // We need to verify lookups before we rebuild the type, because a type lookup change needs to count as a TypeDataChanged VerifyTypeLookups(typeInst); @@ -893,7 +903,7 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild RebuildDependentTypes(typeInst); } - if (typeInst->mTypeDef->mDefState == BfTypeDef::DefState_Deleted) + if (typeInst->mTypeDef->GetDefinition()->mDefState == BfTypeDef::DefState_Deleted) return; if (typeInst->mDefineState == BfTypeDefineState_Undefined) @@ -1047,7 +1057,14 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild delete typeInst->mTypeInfoEx; typeInst->mTypeInfoEx = NULL; - typeInst->mTypeDef->ClearEmitted(); + if (typeInst->mTypeDef->mEmitParent != NULL) + { + auto emitTypeDef = typeInst->mTypeDef; + typeInst->mTypeDef = emitTypeDef->mEmitParent; + delete emitTypeDef; + } + + //typeInst->mTypeDef->ClearEmitted(); for (auto localMethod : typeInst->mOwnedLocalMethods) delete localMethod; typeInst->mOwnedLocalMethods.Clear(); @@ -1123,7 +1140,7 @@ void BfContext::RebuildDependentTypes(BfDependedType* dType) // (obviously) doesn't change the data layout of ClassC // Calls: non-cascading dependency, since it's independent of data layout ConstValue: non-cascading data change void BfContext::TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChange) -{ +{ BfLogSysM("TypeDataChanged %p\n", dType); auto rebuildFlag = isNonStaticDataChange ? BfTypeRebuildFlag_NonStaticChange : BfTypeRebuildFlag_StaticChange; @@ -1136,12 +1153,7 @@ void BfContext::TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChang { auto dependentType = depItr.mKey; auto dependencyFlags = depItr.mValue.mFlags; - - if (dependentType->mRevision == mCompiler->mRevision) - { - continue; - } - + auto dependentDType = dependentType->ToDependedType(); if (dependentDType != NULL) { @@ -1150,9 +1162,11 @@ void BfContext::TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChang { bool hadChange = false; - if ((dependencyFlags & BfDependencyMap::DependencyFlag_DerivedFrom) || - (dependencyFlags & BfDependencyMap::DependencyFlag_ValueTypeMemberData) || - (dependencyFlags & BfDependencyMap::DependencyFlag_NameReference)) + if ((dependencyFlags & + (BfDependencyMap::DependencyFlag_DerivedFrom | + BfDependencyMap::DependencyFlag_ValueTypeMemberData | + BfDependencyMap::DependencyFlag_NameReference | + BfDependencyMap::DependencyFlag_ValueTypeSizeDep)) != 0) { hadChange = true; } @@ -1181,32 +1195,38 @@ void BfContext::TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChang if (dependencyFlags & BfDependencyMap::DependencyFlag_ConstValue) { TypeDataChanged(dependentDType, false); - - + // The ConstValue dependency may be that dependentType used one of our consts as // a default value to a method param, so assume callsites need rebuilding if (dependentTypeInstance != NULL) TypeMethodSignaturesChanged(dependentTypeInstance); } - // We need to include DependencyFlag_ParamOrReturnValue because it could be a struct that changes its splatting ability - // We can't ONLY check against structs, though, because a type could change from a class to a struct - if (dependencyFlags & - (BfDependencyMap::DependencyFlag_ReadFields | BfDependencyMap::DependencyFlag_ParamOrReturnValue | - BfDependencyMap::DependencyFlag_LocalUsage | BfDependencyMap::DependencyFlag_MethodGenericArg | - BfDependencyMap::DependencyFlag_Allocates)) + if (dependentType->mRevision != mCompiler->mRevision) { - RebuildType(dependentType); + // We need to include DependencyFlag_ParamOrReturnValue because it could be a struct that changes its splatting ability + // We can't ONLY check against structs, though, because a type could change from a class to a struct + if (dependencyFlags & + (BfDependencyMap::DependencyFlag_ReadFields | BfDependencyMap::DependencyFlag_ParamOrReturnValue | + BfDependencyMap::DependencyFlag_LocalUsage | BfDependencyMap::DependencyFlag_MethodGenericArg | + BfDependencyMap::DependencyFlag_Allocates)) + { + RebuildType(dependentType); + } } } else { - // Not a type instance, probably something like a sized array - RebuildType(dependentType); + if (dependentType->mRevision != mCompiler->mRevision) + { + // Not a type instance, probably something like a sized array + RebuildType(dependentType); + } } } - - RebuildType(dType); + + if (dType->mRevision != mCompiler->mRevision) + RebuildType(dType); } void BfContext::TypeMethodSignaturesChanged(BfTypeInstance* typeInst) @@ -1223,21 +1243,19 @@ void BfContext::TypeMethodSignaturesChanged(BfTypeInstance* typeInst) auto dependentType = depItr.mKey; auto dependencyFlags = depItr.mValue.mFlags; - if (dependentType->mRevision == mCompiler->mRevision) + if (dependentType->mRevision != mCompiler->mRevision) { - continue; - } - - // We don't need to cascade rebuilding for method-based usage - just rebuild the type directly (unlike TypeDataChanged, which cascades) - if ((dependencyFlags & BfDependencyMap::DependencyFlag_Calls) || - (dependencyFlags & BfDependencyMap::DependencyFlag_VirtualCall) || - (dependencyFlags & BfDependencyMap::DependencyFlag_InlinedCall) || - (dependencyFlags & BfDependencyMap::DependencyFlag_MethodGenericArg) || - (dependencyFlags & BfDependencyMap::DependencyFlag_CustomAttribute) || - (dependencyFlags & BfDependencyMap::DependencyFlag_DerivedFrom) || - (dependencyFlags & BfDependencyMap::DependencyFlag_ImplementsInterface)) - { - RebuildType(dependentType); + // We don't need to cascade rebuilding for method-based usage - just rebuild the type directly (unlike TypeDataChanged, which cascades) + if ((dependencyFlags & BfDependencyMap::DependencyFlag_Calls) || + (dependencyFlags & BfDependencyMap::DependencyFlag_VirtualCall) || + (dependencyFlags & BfDependencyMap::DependencyFlag_InlinedCall) || + (dependencyFlags & BfDependencyMap::DependencyFlag_MethodGenericArg) || + (dependencyFlags & BfDependencyMap::DependencyFlag_CustomAttribute) || + (dependencyFlags & BfDependencyMap::DependencyFlag_DerivedFrom) || + (dependencyFlags & BfDependencyMap::DependencyFlag_ImplementsInterface)) + { + RebuildType(dependentType); + } } } } @@ -1252,12 +1270,16 @@ void BfContext::TypeInlineMethodInternalsChanged(BfTypeInstance* typeInst) for (auto& depItr : typeInst->mDependencyMap) { auto dependentType = depItr.mKey; + auto dependencyFlags = depItr.mValue.mFlags; - // We don't need to cascade rebuilding for method-based usage - just rebuild the type directly (unlike TypeDataChanged, which cascades) - if ((dependencyFlags & BfDependencyMap::DependencyFlag_InlinedCall) != 0) + if (dependentType->mRevision != mCompiler->mRevision) { - RebuildType(dependentType); + // We don't need to cascade rebuilding for method-based usage - just rebuild the type directly (unlike TypeDataChanged, which cascades) + if ((dependencyFlags & BfDependencyMap::DependencyFlag_InlinedCall) != 0) + { + RebuildType(dependentType); + } } } } @@ -1280,14 +1302,16 @@ void BfContext::TypeConstEvalChanged(BfTypeInstance* typeInst) auto depTypeInst = dependentType->ToTypeInstance(); if (depTypeInst != NULL) TypeConstEvalChanged(depTypeInst); - RebuildType(dependentType); + if (dependentType->mRevision != mCompiler->mRevision) + RebuildType(dependentType); } else if ((dependencyFlags & BfDependencyMap::DependencyFlag_ConstEvalConstField) != 0) { auto depTypeInst = dependentType->ToTypeInstance(); if (depTypeInst != NULL) TypeConstEvalFieldChanged(depTypeInst); - RebuildType(dependentType); + if (dependentType->mRevision != mCompiler->mRevision) + RebuildType(dependentType); } } } @@ -1309,7 +1333,8 @@ void BfContext::TypeConstEvalFieldChanged(BfTypeInstance* typeInst) auto depTypeInst = dependentType->ToTypeInstance(); if (depTypeInst != NULL) TypeConstEvalFieldChanged(depTypeInst); - RebuildType(dependentType); + if (dependentType->mRevision != mCompiler->mRevision) + RebuildType(dependentType); } } } @@ -1884,12 +1909,21 @@ void BfContext::UpdateRevisedTypes() continue; auto typeDef = typeInst->mTypeDef; + + if (typeDef->mEmitParent != NULL) + { + auto emitTypeDef = typeDef; + typeDef = typeDef->mEmitParent; + if (typeDef->mNextRevision != NULL) + emitTypeDef->mDefState = BfTypeDef::DefState_EmittedDirty; + } + if (typeDef->mProject->mDisabled) { DeleteType(type); continue; } - + typeInst->mRebuildFlags = BfTypeRebuildFlag_None; if (typeDef->mIsPartial) @@ -1938,14 +1972,19 @@ void BfContext::UpdateRevisedTypes() auto typeDef = typeInst->mTypeDef; bool isTypeDefinedInContext = true; - + + if (typeDef->mEmitParent != NULL) + { + typeDef = typeDef->mEmitParent; + } + if (typeDef->mDefState == BfTypeDef::DefState_Deleted) { HandleChangedTypeDef(typeDef); DeleteType(typeInst); continue; } - + if (typeDef->mDefState == BfTypeDef::DefState_InlinedInternals_Changed) { TypeInlineMethodInternalsChanged(typeInst); @@ -2561,7 +2600,7 @@ void BfContext::QueueMethodSpecializations(BfTypeInstance* typeInst, bool checkS } bool allowMismatch = false; - if ((methodRef.mTypeInstance->mTypeDef == mCompiler->mInternalTypeDef) || (methodRef.mTypeInstance->mTypeDef == mCompiler->mGCTypeDef)) + if ((methodRef.mTypeInstance->IsInstanceOf(mCompiler->mInternalTypeDef)) || (methodRef.mTypeInstance->IsInstanceOf(mCompiler->mGCTypeDef))) allowMismatch = true; // The signature hash better not have changed, because if it did then we should have rebuilding 'module' @@ -2569,7 +2608,9 @@ void BfContext::QueueMethodSpecializations(BfTypeInstance* typeInst, bool checkS int newSignatureHash = (int)methodRef.mTypeInstance->mTypeDef->mSignatureHash; BF_ASSERT((newSignatureHash == methodRef.mSignatureHash) || (allowMismatch)); - auto methodDef = methodRef.mTypeInstance->mTypeDef->mMethods[methodRef.mMethodNum]; + BfMethodDef* methodDef = NULL; + if (methodRef.mMethodNum < methodRef.mTypeInstance->mTypeDef->mMethods.mSize) + methodDef = methodRef.mTypeInstance->mTypeDef->mMethods[methodRef.mMethodNum]; auto targetContext = methodRef.mTypeInstance->mContext; BfMethodSpecializationRequest* specializationRequest = targetContext->mMethodSpecializationWorkList.Alloc(); @@ -2881,13 +2922,14 @@ void BfContext::Finish() void BfContext::Cleanup() { + BfLogSysM("BfContext::Cleanup() MethodWorkList: %d LocalMethodGraveyard: %d\n", mMethodWorkList.size(), mLocalMethodGraveyard.size()); + // Can't clean up LLVM types, they are allocated with a bump allocator RemoveInvalidFailTypes(); mCompiler->mCompileState = BfCompiler::CompileState_Cleanup; - // We can't remove the local methods if they still may be referenced by a BfMethodRefType used to specialize a method - if (mMethodWorkList.empty()) + /// { Array survivingLocalMethods; @@ -2912,6 +2954,14 @@ void BfContext::Cleanup() } else if ((localMethod->mMethodInstanceGroup != NULL) && (localMethod->mMethodInstanceGroup->mRefCount > 0)) { + BfLogSysM("BfContext::Cleanup surviving local method with refs %p\n", localMethod); + localMethod->Dispose(); + survivingLocalMethods.push_back(localMethod); + } + else if (!mMethodWorkList.empty()) + { + // We can't remove the local methods if they still may be referenced by a BfMethodRefType used to specialize a method + BfLogSysM("BfContext::Cleanup surviving local method %p\n", localMethod); localMethod->Dispose(); survivingLocalMethods.push_back(localMethod); } diff --git a/IDEHelper/Compiler/BfContext.h b/IDEHelper/Compiler/BfContext.h index c9ab4227..a85a308d 100644 --- a/IDEHelper/Compiler/BfContext.h +++ b/IDEHelper/Compiler/BfContext.h @@ -131,13 +131,14 @@ public: ResolveKind_UnionInnerType, ResolveKind_LocalVariable, ResolveKind_Attributes, + ResolveKind_FieldType, ResolveKind_ConstField }; public: BfTypeState* mPrevState; - BfTypeInstance* mTypeInstance; + BfType* mType; BfTypeDef* mGlobalContainerCurUserTypeDef; Array mGlobalContainers; // All global containers that are visible @@ -156,7 +157,7 @@ public: BfTypeState() { mPrevState = NULL; - mTypeInstance = NULL; + mType = NULL; mGlobalContainerCurUserTypeDef = NULL; mPopulateType = BfPopulateType_Identity; @@ -171,14 +172,15 @@ public: mResolveKind = ResolveKind_None; } - BfTypeState(BfTypeInstance* typeInstance, BfTypeState* prevState = NULL) + BfTypeState(BfType* type, BfTypeState* prevState = NULL) { mPrevState = prevState; - mTypeInstance = typeInstance; + mType = type; mGlobalContainerCurUserTypeDef = NULL; mPopulateType = BfPopulateType_Declaration; mCurBaseTypeRef = NULL; + mCurBaseType = NULL; mCurFieldDef = NULL; mCurAttributeTypeRef = NULL; mCurTypeDef = NULL; diff --git a/IDEHelper/Compiler/BfDefBuilder.cpp b/IDEHelper/Compiler/BfDefBuilder.cpp index 3e34d58d..65995cc1 100644 --- a/IDEHelper/Compiler/BfDefBuilder.cpp +++ b/IDEHelper/Compiler/BfDefBuilder.cpp @@ -59,6 +59,7 @@ BfDefBuilder::BfDefBuilder(BfSystem* bfSystem) mPassInstance = NULL; mSystem = bfSystem; mCurTypeDef = NULL; + mCurDeclaringTypeDef = NULL; mCurActualTypeDef = NULL; mFullRefresh = false; mIsComptime = false; @@ -391,7 +392,7 @@ void BfDefBuilder::Visit(BfConstructorDeclaration* ctorDeclaration) } BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaration, BfMethodDef* outerMethodDef) -{ +{ BfMethodDef* methodDef; if (auto operatorDecl = BfNodeDynCast(methodDeclaration)) @@ -403,8 +404,9 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio } else methodDef = new BfMethodDef(); - - methodDef->mDeclaringType = mCurTypeDef; + + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mMethodDeclaration = methodDeclaration; methodDef->mExplicitInterface = methodDeclaration->mExplicitInterface; methodDef->mReturnTypeRef = methodDeclaration->mReturnType; @@ -447,8 +449,6 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio if (methodDef->mIsAbstract) { - if ((!mCurTypeDef->mIsAbstract) && (mCurTypeDef->mTypeCode != BfTypeCode_Interface)) - Fail("Method is abstract but it is contained in non-abstract class", methodDeclaration); if (methodDeclaration->mBody != NULL) Fail("Abstract method cannot declare a body", methodDeclaration); } @@ -555,7 +555,7 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio { if (attributes->mAttributeTypeRef != NULL) { - auto typeRefName = attributes->mAttributeTypeRef->ToString(); + auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); if (typeRefName == "StdCall") methodDef->mCallingConvention = BfCallingConvention_Stdcall; @@ -566,13 +566,13 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio else if (methodDeclaration->mMixinSpecifier != NULL) { if (methodDeclaration->mNameNode != NULL) - methodDef->mName = methodDeclaration->mNameNode->ToString(); + methodDef->SetName(methodDeclaration->mNameNode); methodDef->mMethodType = BfMethodType_Mixin; } else { if (methodDeclaration->mNameNode != NULL) - methodDef->mName = methodDeclaration->mNameNode->ToString(); + methodDef->SetName(methodDeclaration->mNameNode); methodDef->mMethodType = BfMethodType_Normal; if ((methodDeclaration->mThisToken != NULL) && (!methodDeclaration->mParams.IsEmpty())) @@ -622,11 +622,11 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio auto paramDef = new BfParameterDef(); paramDef->mParamDeclaration = paramDecl; if (paramDecl->mNameNode != NULL) - paramDef->mName = paramDecl->mNameNode->ToString(); + paramDef->SetName(paramDecl->mNameNode); paramDef->mTypeRef = paramDecl->mTypeRef; paramDef->mMethodGenericParamIdx = mSystem->GetGenericParamIdx(methodDef->mGenericParams, paramDef->mTypeRef); if (paramDecl->mModToken == NULL) - paramDef->mParamKind = BfParamKind_Normal; + paramDef->mParamKind = BfParamKind_Normal; else if (paramDecl->mModToken->mToken == BfToken_Params) paramDef->mParamKind = BfParamKind_Params; else if ((paramDecl->mModToken->mToken == BfToken_ReadOnly) && (isAutoCtor)) @@ -703,9 +703,15 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio { auto fieldDef = new BfFieldDef(); fieldDef->mName = paramDef->mName; + while (fieldDef->mName.StartsWith("@")) + { + fieldDef->mNamePrefixCount++; + fieldDef->mName.Remove(0); + } fieldDef->mTypeRef = paramDef->mTypeRef; fieldDef->mProtection = BfProtection_Public; - fieldDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + fieldDef->mDeclaringType = mCurDeclaringTypeDef; fieldDef->mIdx = mCurTypeDef->mFields.mSize; if ((paramDef->mParamDeclaration->mModToken != NULL) && (paramDef->mParamDeclaration->mModToken->mToken == BfToken_ReadOnly)) @@ -788,7 +794,7 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef { if (attributes->mAttributeTypeRef != NULL) { - auto typeRefName = attributes->mAttributeTypeRef->ToString(); + auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); if (typeRefName == "CLink") methodDef->mCLink = true; @@ -874,7 +880,7 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfTypeDef* { if (attributes->mAttributeTypeRef != NULL) { - auto typeRefName = attributes->mAttributeTypeRef->ToString(); + auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); if (typeRefName == "AlwaysInclude") typeDef->mIsAlwaysInclude = true; @@ -917,7 +923,7 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) propertyDef->mIsStatic = propertyDeclaration->mStaticSpecifier != NULL; propertyDef->mIsReadOnly = propertyDeclaration->mReadOnlySpecifier != NULL; if (propertyDeclaration->mNameNode != NULL) - propertyDef->mName = propertyDeclaration->mNameNode->ToString(); + propertyDef->SetName(propertyDeclaration->mNameNode); else if (propertyDeclaration->IsA()) { propertyDef->mName = "[]"; @@ -925,7 +931,8 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) propertyDef->mTypeRef = propertyDeclaration->mTypeRef; propertyDef->mInitializer = NULL; propertyDef->mFieldDeclaration = propertyDeclaration; - propertyDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + propertyDef->mDeclaringType = mCurDeclaringTypeDef; if (auto varType = BfNodeDynCast(propertyDef->mTypeRef)) propertyDef->mIsReadOnly = true; @@ -950,7 +957,8 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) if (needsAutoProperty) { BfFieldDef* fieldDef = new BfFieldDef(); - fieldDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + fieldDef->mDeclaringType = mCurDeclaringTypeDef; fieldDef->mFieldDeclaration = propertyDeclaration; fieldDef->mProtection = BfProtection_Hidden; fieldDef->mIsStatic = propertyDef->mIsStatic; @@ -959,9 +967,28 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) fieldDef->mTypeRef = refTypeRef->mElementType; fieldDef->mName = mCurTypeDef->GetAutoPropertyName(propertyDeclaration); fieldDef->mIdx = (int)mCurTypeDef->mFields.size(); + fieldDef->mInitializer = propertyDeclaration->mInitializer; mCurTypeDef->mFields.push_back(fieldDef); - mCurTypeDef->mSignatureHash = HashString(fieldDef->mName, mCurTypeDef->mSignatureHash); + mCurTypeDef->mSignatureHash = HashString(fieldDef->mName, mCurTypeDef->mSignatureHash + fieldDef->mNamePrefixCount); + } + else + { + if (propertyDeclaration->mInitializer != NULL) + { + if (mCurTypeDef->mTypeCode == BfTypeCode_Interface) + Fail("Interface properties cannot have initializers", propertyDeclaration->mInitializer); + else + Fail("Properties with method bodies cannot have initializers", propertyDeclaration->mInitializer); + } + + if (propertyDeclaration->mFieldDtor != NULL) + { + if (mCurTypeDef->mTypeCode == BfTypeCode_Interface) + Fail("Interface properties cannot have field destructors", propertyDeclaration->mFieldDtor); + else + Fail("Properties with method bodies cannot have field destructors", propertyDeclaration->mFieldDtor); + } } for (auto methodDeclaration : propertyDeclaration->mMethods) @@ -981,7 +1008,8 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mMethodDeclaration = methodDeclaration; methodDef->mProtection = propertyDef->mProtection; methodDef->mWantsBody = (methodDeclaration->mBody != NULL) && (WantsNode(methodDeclaration->mBody)); @@ -1023,9 +1051,13 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) auto paramDef = new BfParameterDef(); BfParameterDeclaration* paramDecl = indexerDeclaration->mParams[paramIdx]; paramDef->mParamDeclaration = paramDecl; - paramDef->mName = paramDecl->mNameNode->ToString(); + paramDef->SetName(paramDecl->mNameNode); paramDef->mTypeRef = paramDecl->mTypeRef; paramDef->mMethodGenericParamIdx = mSystem->GetGenericParamIdx(methodDef->mGenericParams, paramDef->mTypeRef); + if (paramDecl->mModToken == NULL) + paramDef->mParamKind = BfParamKind_Normal; + else if (paramDecl->mModToken->mToken == BfToken_Params) + paramDef->mParamKind = BfParamKind_Params; methodDef->mParams.push_back(paramDef); } } @@ -1060,13 +1092,20 @@ void BfDefBuilder::Visit(BfPropertyDeclaration* propertyDeclaration) if (BfNodeDynCast(methodDeclaration->mBody) != NULL) methodDef->mIsMutating = true; // Don't require "set mut;", just "set;" - auto paramDef = new BfParameterDef(); + auto paramDef = new BfParameterDef(); paramDef->mName = "value"; - if (auto refTypeRef = BfNodeDynCast(propertyDeclaration->mTypeRef)) - paramDef->mTypeRef = refTypeRef->mElementType; + paramDef->mTypeRef = propertyDeclaration->mTypeRef; + if (auto refTypeRef = BfNodeDynCast(propertyDeclaration->mTypeRef)) + { + if (methodDeclaration->mSetRefSpecifier == NULL) + paramDef->mTypeRef = refTypeRef->mElementType; + } else - paramDef->mTypeRef = propertyDeclaration->mTypeRef; - methodDef->mParams.push_back(paramDef); + { + if (methodDeclaration->mSetRefSpecifier != NULL) + Fail("Property setter 'ref' can only be used with a 'ref' property type", methodDeclaration->mSetRefSpecifier); + } + methodDef->mParams.Insert(0, paramDef); propertyDef->mMethods.Add(methodDef); } else @@ -1096,9 +1135,10 @@ void BfDefBuilder::Visit(BfFieldDeclaration* fieldDeclaration) auto fieldDef = new BfFieldDef(); mCurTypeDef->mFields.push_back(fieldDef); fieldDef->mFieldDeclaration = fieldDeclaration; - fieldDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + fieldDef->mDeclaringType = mCurDeclaringTypeDef; if (fieldDeclaration->mNameNode != NULL) - fieldDef->mName = fieldDeclaration->mNameNode->ToString(); + fieldDef->SetName(fieldDeclaration->mNameNode); fieldDef->mProtection = GetProtection(fieldDeclaration->mProtectionSpecifier); if (isEnumEntryDecl) fieldDef->mProtection = BfProtection_Public; @@ -1148,7 +1188,7 @@ void BfDefBuilder::Visit(BfEnumCaseDeclaration* enumCaseDeclaration) BfFieldDef* BfDefBuilder::AddField(BfTypeDef* typeDef, BfTypeReference* fieldType, const StringImpl& fieldName) { - BfFieldDef* fieldDef = new BfFieldDef(); + BfFieldDef* fieldDef = new BfFieldDef(); fieldDef->mDeclaringType = typeDef; fieldDef->mTypeRef = fieldType; fieldDef->mName = fieldName; @@ -1397,6 +1437,7 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) actualOuterTypeDef = actualOuterTypeDef->mOuterType; SetAndRestoreValue prevTypeDef(mCurTypeDef, new BfTypeDef()); + SetAndRestoreValue prevDeclaringTypeDef(mCurDeclaringTypeDef, mCurTypeDef); SetAndRestoreValue prevActualTypeDef(mCurActualTypeDef, mCurTypeDef); mCurTypeDef->mSystem = mSystem; @@ -1429,7 +1470,9 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) mCurTypeDef->mProtection = (outerTypeDef == NULL) ? BfProtection_Public : BfProtection_Private; if (typeDeclaration->mProtectionSpecifier != NULL) { - if ((outerTypeDef == NULL) && (typeDeclaration->mProtectionSpecifier->GetToken() != BfToken_Public)) + if ((outerTypeDef == NULL) && + (typeDeclaration->mProtectionSpecifier->GetToken() != BfToken_Public) && + (typeDeclaration->mProtectionSpecifier->GetToken() != BfToken_Internal)) { //CS1527 Fail("Elements defined in a namespace cannot be explicitly declared as private or protected", typeDeclaration->mProtectionSpecifier); @@ -1807,7 +1850,7 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) outerTypeDef->mNestedTypes.push_back(mCurActualTypeDef); } - BfLogSysM("Creating TypeDef %p Hash:%d from TypeDecl: %p Source: %p ResolvePass: %d AutoComplete:%d PrevRevision:%d\n", mCurTypeDef, mSystem->mTypeDefs.GetHash(mCurTypeDef), typeDeclaration, + BfLogSysM("Creating TypeDef %p Hash:%d from TypeDecl: %p Source: %p ResolvePass: %d AutoComplete:%d PrevRevision:%d\n", mCurTypeDef, mCurTypeDef->mHash, typeDeclaration, typeDeclaration->GetSourceData(), mResolvePassData != NULL, isAutoCompleteTempType, prevRevisionTypeDef); BF_ASSERT(mCurTypeDef->mNameEx == NULL); @@ -1904,6 +1947,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) BfMethodDef* staticMarkMethod = NULL; BfMethodDef* dynamicCastMethod = NULL; BfMethodDef* toStringMethod = NULL; + BfMethodDef* getUnderlyingMethod = NULL; bool needsEqualsMethod = ((mCurTypeDef->mTypeCode == BfTypeCode_Struct) || (mCurTypeDef->mTypeCode == BfTypeCode_Enum)) && (!mCurTypeDef->mIsStatic); BfMethodDef* equalsOpMethod = NULL; BfMethodDef* equalsMethod = NULL; @@ -1955,7 +1999,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.Insert(methodIdx + 1, methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mName = BF_METHODNAME_CALCAPPEND; methodDef->mProtection = BfProtection_Public; methodDef->mMethodType = BfMethodType_CtorCalcAppend; @@ -1972,6 +2017,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { BfParameterDef* newParam = new BfParameterDef(); newParam->mName = param->mName; + newParam->mNamePrefixCount = param->mNamePrefixCount; newParam->mTypeRef = param->mTypeRef; newParam->mMethodGenericParamIdx = param->mMethodGenericParamIdx; methodDef->mParams.push_back(newParam); @@ -2038,9 +2084,14 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if (method->mName == BF_METHODNAME_DYNAMICCAST) _SetMethod(dynamicCastMethod, method); if (method->mName == BF_METHODNAME_TO_STRING) - _SetMethod(toStringMethod, method); + _SetMethod(toStringMethod, method); } } + else if (method->mMethodType == BfMethodType_PropertyGetter) + { + if (method->mName == BF_METHODNAME_ENUM_GETUNDERLYING) + _SetMethod(getUnderlyingMethod, method); + } else if ((method->mMethodType == BfMethodType_Operator) && (method->mIsStatic) && (method->mParams.size() == 2)) @@ -2098,7 +2149,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { if (attributes->mAttributeTypeRef != NULL) { - auto typeRefName = attributes->mAttributeTypeRef->ToString(); + auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); if (typeRefName == "ThreadStatic") hasThreadStatics = true; @@ -2224,11 +2275,12 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if (toStringMethod != NULL) wantsToString = false; - if ((mCurTypeDef->mTypeCode == BfTypeCode_Enum) && (!isPayloadEnum)) + if ((mCurTypeDef->mTypeCode == BfTypeCode_Enum) && (!isPayloadEnum) && (getUnderlyingMethod == NULL)) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mName = BF_METHODNAME_ENUM_HASFLAG; methodDef->mReturnTypeRef = mSystem->mDirectBoolTypeRef; methodDef->mProtection = BfProtection_Public; @@ -2239,7 +2291,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mName = BF_METHODNAME_ENUM_GETUNDERLYING; methodDef->mReturnTypeRef = mSystem->mDirectSelfBaseTypeRef; methodDef->mMethodType = BfMethodType_PropertyGetter; @@ -2249,7 +2302,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) auto propDef = new BfPropertyDef(); mCurTypeDef->mProperties.Add(propDef); propDef->mTypeRef = mSystem->mDirectSelfBaseTypeRef; - propDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + propDef->mDeclaringType = mCurDeclaringTypeDef; propDef->mName = "Underlying"; propDef->mMethods.Add(methodDef); propDef->mProtection = BfProtection_Public; @@ -2259,7 +2313,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mIsMutating = true; methodDef->mName = BF_METHODNAME_ENUM_GETUNDERLYINGREF; methodDef->mReturnTypeRef = mSystem->mDirectRefSelfBaseTypeRef; @@ -2270,7 +2325,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) auto propDef = new BfPropertyDef(); mCurTypeDef->mProperties.Add(propDef); propDef->mTypeRef = mSystem->mDirectRefSelfBaseTypeRef; - propDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + propDef->mDeclaringType = mCurDeclaringTypeDef; propDef->mName = "UnderlyingRef"; propDef->mMethods.Add(methodDef); propDef->mProtection = BfProtection_Public; @@ -2281,7 +2337,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mName = BF_METHODNAME_TO_STRING; methodDef->mReturnTypeRef = mSystem->mDirectVoidTypeRef; methodDef->mProtection = BfProtection_Public; @@ -2296,7 +2353,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mName = BF_METHODNAME_DEFAULT_EQUALS; methodDef->mReturnTypeRef = mSystem->mDirectBoolTypeRef; methodDef->mProtection = BfProtection_Private; @@ -2310,7 +2368,8 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); - methodDef->mDeclaringType = mCurTypeDef; + BF_ASSERT(mCurDeclaringTypeDef != NULL); + methodDef->mDeclaringType = mCurDeclaringTypeDef; methodDef->mName = BF_METHODNAME_DEFAULT_STRICT_EQUALS; methodDef->mReturnTypeRef = mSystem->mDirectBoolTypeRef; methodDef->mProtection = BfProtection_Private; diff --git a/IDEHelper/Compiler/BfDefBuilder.h b/IDEHelper/Compiler/BfDefBuilder.h index 3c61f4aa..feeea107 100644 --- a/IDEHelper/Compiler/BfDefBuilder.h +++ b/IDEHelper/Compiler/BfDefBuilder.h @@ -16,6 +16,7 @@ public: BfSystem* mSystem; BfPassInstance* mPassInstance; BfTypeDef* mCurTypeDef; + BfTypeDef* mCurDeclaringTypeDef; BfTypeDef* mCurActualTypeDef; bool mFullRefresh; bool mIsComptime; diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index bd0da9e9..0805ee67 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -492,6 +492,16 @@ void BfElementVisitor::Visit(BfTypeAttrExpression* typeAttrExpr) VisitChild(typeAttrExpr->mCloseParen); } +void BfElementVisitor::Visit(BfOffsetOfExpression* offsetOfExpr) +{ + VisitChild(offsetOfExpr->mToken); + VisitChild(offsetOfExpr->mOpenParen); + VisitChild(offsetOfExpr->mTypeRef); + VisitChild(offsetOfExpr->mCommaToken); + VisitChild(offsetOfExpr->mMemberName); + VisitChild(offsetOfExpr->mCloseParen); +} + void BfElementVisitor::Visit(BfDefaultExpression* defaultExpr) { Visit(defaultExpr->ToBase()); @@ -1040,8 +1050,9 @@ void BfElementVisitor::Visit(BfPropertyMethodDeclaration* propertyDeclaration) VisitChild(propertyDeclaration->mAttributes); VisitChild(propertyDeclaration->mProtectionSpecifier); VisitChild(propertyDeclaration->mNameNode); + VisitChild(propertyDeclaration->mSetRefSpecifier); VisitChild(propertyDeclaration->mMutSpecifier); - VisitChild(propertyDeclaration->mFatArrowToken); + VisitChild(propertyDeclaration->mFatArrowToken); VisitChild(propertyDeclaration->mBody); VisitChild(propertyDeclaration->mEndSemicolon); } diff --git a/IDEHelper/Compiler/BfElementVisitor.h b/IDEHelper/Compiler/BfElementVisitor.h index a72f3cd7..60e07b09 100644 --- a/IDEHelper/Compiler/BfElementVisitor.h +++ b/IDEHelper/Compiler/BfElementVisitor.h @@ -69,6 +69,7 @@ public: virtual void Visit(BfLocalMethodDeclaration* methodDecl); virtual void Visit(BfParameterDeclaration* paramDecl); virtual void Visit(BfTypeAttrExpression* typeAttrExpr); + virtual void Visit(BfOffsetOfExpression* offsetOfExpr); virtual void Visit(BfDefaultExpression* defaultExpr); virtual void Visit(BfUninitializedExpression* uninitializedExpr); virtual void Visit(BfCheckTypeExpression* checkTypeExpr); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index b00fdf65..e6d3ba2b 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -242,9 +242,9 @@ bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstanc { if (argType == NULL) return false; - + if (!wantType->IsUnspecializedType()) - return true; + return true; bool alreadyChecked = false; auto _AddToCheckedSet = [](BfType* type, HashSet& checkedTypeSet, bool& alreadyChecked) @@ -285,7 +285,7 @@ bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstanc if (checkArgType->IsGenericTypeInstance()) { auto argGenericType = (BfTypeInstance*)checkArgType; - if (argGenericType->mTypeDef == wantGenericType->mTypeDef) + if (argGenericType->mTypeDef->GetLatest() == wantGenericType->mTypeDef->GetLatest()) { for (int genericArgIdx = 0; genericArgIdx < (int)argGenericType->mGenericTypeInfo->mTypeGenericArguments.size(); genericArgIdx++) InferGenericArgument(methodInstance, argGenericType->mGenericTypeInfo->mTypeGenericArguments[genericArgIdx], wantGenericType->mGenericTypeInfo->mTypeGenericArguments[genericArgIdx], BfIRValue()); @@ -294,7 +294,7 @@ bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstanc else if (checkArgType->IsSizedArray()) { auto sizedArrayType = (BfSizedArrayType*)checkArgType; - if (wantGenericType->mTypeDef == mModule->mCompiler->mSizedArrayTypeDef) + if (wantGenericType->IsInstanceOf(mModule->mCompiler->mSizedArrayTypeDef)) { InferGenericArgument(methodInstance, sizedArrayType->mElementType, wantGenericType->mGenericTypeInfo->mTypeGenericArguments[0], BfIRValue()); auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); @@ -305,7 +305,7 @@ bool BfGenericInferContext::InferGenericArgument(BfMethodInstance* methodInstanc else if (checkArgType->IsPointer()) { auto pointerType = (BfPointerType*)checkArgType; - if (wantGenericType->mTypeDef == mModule->mCompiler->mPointerTTypeDef) + if (wantGenericType->IsInstanceOf(mModule->mCompiler->mPointerTTypeDef)) { InferGenericArgument(methodInstance, pointerType->mElementType, wantGenericType->mGenericTypeInfo->mTypeGenericArguments[0], BfIRValue()); } @@ -619,10 +619,10 @@ bool BfGenericInferContext::InferGenericArguments(BfMethodInstance* methodInstan BfGenericParamInstance* genericParam = NULL; if (genericParamType->mGenericParamKind == BfGenericParamKind_Method) - genericParam = methodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx]; + genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx]; else genericParam = mModule->GetGenericParamInstance(genericParamType); - + if (genericParam->mTypeConstraint != NULL) InferGenericArgument(methodInstance, genericParam->mTypeConstraint, ifaceConstraint, BfIRValue()); for (auto argIfaceConstraint : genericParam->mInterfaceConstraints) @@ -1071,6 +1071,17 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp RETURN_RESULTS; } + // For operators, prefer explicit comparison over '<=>' comparison operator + if ((!isBetter) && (!isWorse)) + { + if ((prevMethodDef->mIsOperator) && (newMethodDef->mIsOperator)) + { + bool newIsComparison = ((BfOperatorDeclaration*)newMethodDef->mMethodDeclaration)->mBinOp == BfBinaryOp_Compare; + bool prevIsComparison = ((BfOperatorDeclaration*)prevMethodDef->mMethodDeclaration)->mBinOp == BfBinaryOp_Compare; + RETURN_BETTER_OR_WORSE(!newIsComparison, !prevIsComparison); + } + } + if ((newMethodInstance->mMethodDef->mExternalConstraints.size() != 0) || (prevMethodInstance->mMethodDef->mExternalConstraints.size() != 0)) { struct GenericParamPair @@ -1098,7 +1109,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp }; _GetParams(0, newMethodInstance); - _GetParams(0, prevMethodInstance); + _GetParams(1, prevMethodInstance); for (auto kv : externConstraints) { @@ -1119,18 +1130,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp // If both are empty partials then just bind to either isWorse = true; RETURN_RESULTS; - } - - // For operators, prefer explicit comparison over '<=>' comparison operator - if ((!isBetter) && (!isWorse)) - { - if ((prevMethodDef->mIsOperator) && (newMethodDef->mIsOperator)) - { - bool newIsComparison = ((BfOperatorDeclaration*)newMethodDef->mMethodDeclaration)->mBinOp == BfBinaryOp_Compare; - bool prevIsComparison = ((BfOperatorDeclaration*)prevMethodDef->mMethodDeclaration)->mBinOp == BfBinaryOp_Compare; - RETURN_BETTER_OR_WORSE(!newIsComparison, !prevIsComparison); - } - } + } // For extensions, select the version in the most-specific project (only applicable for ctors) if ((!isBetter) && (!isWorse)) @@ -1547,7 +1547,7 @@ bool BfMethodMatcher::InferFromGenericConstraints(BfMethodInstance* methodInstan exprEvaluator.PerformUnaryOperation(NULL, checkOpConstraint.mUnaryOp, NULL, BfUnaryOpFlag_IsConstraintCheck); if (exprEvaluator.mResult) - checkArgType = exprEvaluator.mResult.mType; + checkArgType = exprEvaluator.mResult.mType; } } } @@ -1557,21 +1557,9 @@ bool BfMethodMatcher::InferFromGenericConstraints(BfMethodInstance* methodInstan { for (auto comptypeConstraint : genericParamInst->mComptypeConstraint) { - BfConstraintState constraintSet; - constraintSet.mPrevState = mModule->mContext->mCurConstraintState; - constraintSet.mGenericParamInstance = genericParamInst; - constraintSet.mMethodInstance = methodInstance; - constraintSet.mMethodGenericArgsOverride = methodGenericArgs; - - SetAndRestoreValue prevConstraintSet(mModule->mContext->mCurConstraintState, &constraintSet); - if (!mModule->CheckConstraintState(NULL)) - return false; - - SetAndRestoreValue prevMethodInstance(mModule->mCurMethodInstance, methodInstance); - SetAndRestoreValue prevTypeInstance(mModule->mCurTypeInstance, methodInstance->GetOwner()); - SetAndRestoreValue prevIgnoreErrors(mModule->mIgnoreErrors, true); - - checkArgType = mModule->ResolveTypeRef(comptypeConstraint); + checkArgType = mModule->ResolveGenericMethodTypeRef(comptypeConstraint, methodInstance, genericParamInst, methodGenericArgs); + if (checkArgType == NULL) + return false; } } @@ -1617,7 +1605,7 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst return false; } - if ((checkMethod->mIsVirtual) && (!checkMethod->mIsOverride) && (!mBypassVirtual) && + if ((checkMethod->mIsVirtual) && (!checkMethod->mIsOverride) && (!mBypassVirtual) && (targetTypeInstance != NULL) && (targetTypeInstance->IsObject())) { mModule->PopulateType(targetTypeInstance, BfPopulateType_DataAndMethods); @@ -2138,7 +2126,21 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst { auto genericParam = methodInstance->mMethodInfoEx->mGenericParams[checkMethod->mGenericParams.size() + externConstraintIdx]; BF_ASSERT(genericParam->mExternType != NULL); - if (!mModule->CheckGenericConstraints(BfGenericParamSource(methodInstance), genericParam->mExternType, NULL, genericParam, genericArgumentsSubstitute, NULL)) + auto externType = genericParam->mExternType; + BfTypeVector* externGenericArgumentsSubstitute = genericArgumentsSubstitute; + + if (externType->IsVar()) + { + auto& externConstraint = checkMethod->mExternalConstraints[externConstraintIdx]; + if (externConstraint.mTypeRef != NULL) + { + externType = mModule->ResolveGenericMethodTypeRef(externConstraint.mTypeRef, methodInstance, genericParam, genericArgumentsSubstitute); + if (externType == NULL) + goto NoMatch; + } + } + + if (!mModule->CheckGenericConstraints(BfGenericParamSource(methodInstance), externType, NULL, genericParam, externGenericArgumentsSubstitute, NULL)) goto NoMatch; } @@ -2176,6 +2178,29 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst // And if neither better nor worse then they are equally good, which is not allowed either if (((!isBetter) && (!isWorse)) || ((isBetter) && (isWorse))) { + if (!mHasVarArguments) + { + // If we are ambiguous based on a subset of an extern 'var' constraint then don't throw an error + auto _CheckMethodInfo = [&](BfMethodInstance* checkMethodInstance) + { + if (checkMethodInstance->mMethodInfoEx == NULL) + return; + for (auto genericParam : checkMethodInstance->mMethodInfoEx->mGenericParams) + { + if ((genericParam->mExternType == NULL) || (!genericParam->mExternType->IsGenericParam())) + continue; + auto genericParamType = (BfGenericParamType*)genericParam->mExternType; + if (genericParamType->mGenericParamKind != BfGenericParamKind_Type) + continue; + auto externGenericParam = mModule->GetGenericParamInstance(genericParamType); + if ((externGenericParam->mGenericParamFlags & BfGenericParamFlag_Var) != 0) + mHasVarArguments = true; + } + }; + _CheckMethodInfo(methodInstance); + _CheckMethodInfo(prevMethodInstance); + } + if (mHasVarArguments) { if (methodInstance->mReturnType != prevMethodInstance->mReturnType) @@ -2460,7 +2485,7 @@ bool BfMethodMatcher::CheckType(BfTypeInstance* typeInstance, BfTypedValue targe while (checkTypeState != NULL) { if ((checkTypeState->mResolveKind == BfTypeState::ResolveKind_ResolvingVarType) && - (checkTypeState->mTypeInstance == typeInstance)) + (checkTypeState->mType == typeInstance)) isResolvingVarField = true; checkTypeState = checkTypeState->mPrevState; } @@ -2644,7 +2669,7 @@ void BfMethodMatcher::TryDevirtualizeCall(BfTypedValue target, BfTypedValue* ori BfTypeInterfaceEntry* bestIFaceEntry = NULL; auto checkTypeInst = checkType->ToTypeInstance(); - if (mBestMethodTypeInstance->mTypeDef == mModule->mCompiler->mIHashableTypeDef) + if (mBestMethodTypeInstance->IsInstanceOf(mModule->mCompiler->mIHashableTypeDef)) { if ((origTarget != NULL) && (origTarget->mType->IsPointer()) && (staticResult != NULL)) { @@ -3020,16 +3045,24 @@ void BfExprEvaluator::Evaluate(BfAstNode* astNode, bool propogateNullConditional // ParenthesizedExpression breaks null conditional chain if (astNode->IsExact()) propogateNullConditional = false; + + bool scopeWasConditional = false; BfPendingNullConditional* pendingNullCond = NULL; + mInsidePendingNullable = false; if (mModule->mCurMethodState != NULL) { + scopeWasConditional = mModule->mCurMethodState->mCurScope->mIsConditional; pendingNullCond = mModule->mCurMethodState->mPendingNullConditional; if (!propogateNullConditional) mModule->mCurMethodState->mPendingNullConditional = NULL; - } - mInsidePendingNullable = pendingNullCond != NULL; - + if (pendingNullCond != NULL) + { + mInsidePendingNullable = true; + mModule->mCurMethodState->mCurScope->mIsConditional = true; + } + } + astNode->Accept(this); GetResult(); @@ -3042,11 +3075,30 @@ void BfExprEvaluator::Evaluate(BfAstNode* astNode, bool propogateNullConditional if ((mBfEvalExprFlags & BfEvalExprFlags_AllowIntUnknown) == 0) mModule->FixIntUnknown(mResult); - if ((!propogateNullConditional) && (mModule->mCurMethodState != NULL)) + if (!mModule->mBfIRBuilder->mIgnoreWrites) { - if (mModule->mCurMethodState->mPendingNullConditional != NULL) - mResult = mModule->FlushNullConditional(mResult, ignoreNullConditional); - mModule->mCurMethodState->mPendingNullConditional = pendingNullCond; + if (mResult.mValue.IsConst()) + { + auto constant = mModule->mBfIRBuilder->GetConstant(mResult.mValue); + if (constant->mConstType == BfConstType_TypeOf) + { + auto typeOfConst = (BfTypeOf_Const*)constant; + mResult.mValue = mModule->CreateTypeDataRef(typeOfConst->mType); + } + } + } + + if (mModule->mCurMethodState != NULL) + { + if (mInsidePendingNullable) + mModule->mCurMethodState->mCurScope->mIsConditional = scopeWasConditional; + + if (!propogateNullConditional) + { + if (mModule->mCurMethodState->mPendingNullConditional != NULL) + mResult = mModule->FlushNullConditional(mResult, ignoreNullConditional); + mModule->mCurMethodState->mPendingNullConditional = pendingNullCond; + } } } @@ -3800,6 +3852,9 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI { auto varDecl = entry->mLocalVar; + if (varDecl != NULL) + varSkipCount -= varDecl->mNamePrefixCount; + while ((varSkipCount > 0) && (varDecl != NULL)) { varDecl = varDecl->mShadowedLocal; @@ -3929,17 +3984,24 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI { if (checkTypeState->mCurFieldDef != NULL) { - if (checkTypeState->mTypeInstance == mModule->mCurTypeInstance) + if (checkTypeState->mType == mModule->mCurTypeInstance) resolvingFieldDef = checkTypeState->mCurFieldDef; } checkTypeState = checkTypeState->mPrevState; } - - if ((resolvingFieldDef != NULL) && (resolvingFieldDef->mIdx > 0)) + + if ((resolvingFieldDef != NULL) && + (mModule->mCompiler->mResolvePassData != NULL) && + (mModule->mCompiler->mResolvePassData->mParser == resolvingFieldDef->mFieldDeclaration->GetParser()) && + (GetAutoComplete() != NULL)) + { + return mModule->GetDefaultTypedValue(mModule->mCurTypeInstance); + } + else if ((resolvingFieldDef != NULL) && (resolvingFieldDef->mIdx > 0)) { auto enumType = mModule->mCurTypeInstance; if (!enumType->mFieldInstances.IsEmpty()) - { + { auto fieldInstance = &mModule->mCurTypeInstance->mFieldInstances[resolvingFieldDef->mIdx - 1]; if ((fieldInstance->mConstIdx != -1) && (fieldInstance->mResolvedType == mModule->mCurTypeInstance)) @@ -3989,7 +4051,7 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI if (!thisValue.HasType()) { - if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mTypeInstance == mModule->mCurTypeInstance) && + if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mType == mModule->mCurTypeInstance) && (mModule->mContext->mCurTypeState->mResolveKind == BfTypeState::ResolveKind_Attributes)) { // Can't do static lookups yet @@ -4179,7 +4241,7 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar } } - if (mModule->mCurMethodInstance->mIsUnspecialized) + if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized)) { if (genericParamInst->mTypeConstraint != NULL) target.mType = genericParamInst->mTypeConstraint; @@ -4254,7 +4316,7 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar { // Don't allow lookups yet if ((mModule->mContext->mCurTypeState->mResolveKind == BfTypeState::ResolveKind_Attributes) && - (startCheckType == mModule->mContext->mCurTypeState->mTypeInstance)) + (startCheckType == mModule->mContext->mCurTypeState->mType)) return BfTypedValue(); } @@ -4288,9 +4350,13 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar if (curCheckType->mTypeDef->mFieldSet.TryGetWith(findName, &entry)) nextField = (BfFieldDef*)entry->mMemberDef; + if (nextField != NULL) + varSkipCount -= nextField->mNamePrefixCount; + while ((varSkipCount > 0) && (nextField != NULL)) { nextField = nextField->mNextWithSameName; + varSkipCount--; } BfProtectionCheckFlags protectionCheckFlags = BfProtectionCheckFlag_None; @@ -4415,14 +4481,14 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar } mModule->PopulateType(resolvedFieldType, BfPopulateType_Data); - mModule->AddDependency(curCheckType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields); + mModule->AddDependency(curCheckType, mModule->mCurTypeInstance, field->mIsConst ? BfDependencyMap::DependencyFlag_ConstValue : BfDependencyMap::DependencyFlag_ReadFields); if (fieldInstance->mHadConstEval) { mModule->AddDependency(curCheckType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ConstEvalConstField); if ((mModule->mContext->mCurTypeState != NULL) && (mModule->mContext->mCurTypeState->mCurFieldDef != NULL)) { // If we're initializing another const field then also set it as having const eval - auto resolvingFieldInstance = &mModule->mContext->mCurTypeState->mTypeInstance->mFieldInstances[mModule->mContext->mCurTypeState->mCurFieldDef->mIdx]; + auto resolvingFieldInstance = &mModule->mContext->mCurTypeState->mType->ToTypeInstance()->mFieldInstances[mModule->mContext->mCurTypeState->mCurFieldDef->mIdx]; if (resolvingFieldInstance->GetFieldDef()->mIsConst) resolvingFieldInstance->mHadConstEval = true; } @@ -4507,19 +4573,18 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar } mModule->PopulateType(fieldInstance->mOwner, BfPopulateType_Data); - // OLD: -// auto agg = mModule->CreateAlloca(fieldInstance->mOwner); -// auto gep = mModule->mBfIRBuilder->CreateInBoundsGEP(agg, 0, 2); -// mModule->mBfIRBuilder->CreateStore(mModule->mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, tagIdx), gep); -// -// if (fieldInstance->mResolvedType->mSize != 0) -// { -// mModule->FailAfter("Enum case parameters expected", targetSrc); -// } -// -// return BfTypedValue(agg, fieldInstance->mOwner, true); - - //NEW + if (auto fieldTypeInstance = fieldInstance->mResolvedType->ToTypeInstance()) + { + bool hasFields = false; + for (auto& fieldInstance : fieldTypeInstance->mFieldInstances) + { + if (!fieldInstance.mResolvedType->IsVoid()) + hasFields = true; + } + if (hasFields) + mModule->FailAfter("Enum payload arguments expected", targetSrc); + } + SizedArray values; values.Add(mModule->mBfIRBuilder->CreateConstAggZero(mModule->mBfIRBuilder->MapType(curCheckType->mBaseType))); values.Add(mModule->mBfIRBuilder->CreateConstAggZero(mModule->mBfIRBuilder->MapType(curCheckType->GetUnionInnerType()))); @@ -4550,7 +4615,8 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar { mModule->CheckStaticAccess(curCheckType); auto retVal = mModule->ReferenceStaticField(fieldInstance); - bool isStaticCtor = (mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor) && + bool isStaticCtor = (mModule->mCurMethodInstance != NULL) && + (mModule->mCurMethodInstance->mMethodDef->IsCtorOrInit()) && (mModule->mCurMethodInstance->mMethodDef->mIsStatic); if ((field->mIsReadOnly) && (!isStaticCtor)) { @@ -4594,7 +4660,7 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar bool isTemporary = target.IsTempAddr(); bool wantsLoadValue = false; bool wantsReadOnly = false; - if ((field->mIsReadOnly) && (mModule->mCurMethodInstance != NULL) && ((mModule->mCurMethodInstance->mMethodDef->mMethodType != BfMethodType_Ctor) || (!target.IsThis()))) + if ((field->mIsReadOnly) && (mModule->mCurMethodInstance != NULL) && ((!mModule->mCurMethodInstance->mMethodDef->IsCtorOrInit()) || (!target.IsThis()))) wantsReadOnly = true; bool isComposite = target.mType->IsComposite(); @@ -4820,8 +4886,15 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar mPropTarget = BfTypedValue(curCheckType); else if (isBaseLookup) { - mPropTarget = mModule->Cast(targetSrc, target, curCheckType); - BF_ASSERT(mPropTarget); + if (target.mValue.IsFake()) + { + mPropTarget = BfTypedValue(target.mValue, curCheckType, target.mKind); + } + else + { + mPropTarget = mModule->Cast(targetSrc, target, curCheckType); + BF_ASSERT(mPropTarget); + } } else mPropTarget = target; @@ -4871,34 +4944,45 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar { if ((curCheckType->mTypeDef->HasAutoProperty(propertyDeclaration)) && (propertyDeclaration->mVirtualSpecifier == NULL)) { - bool hasSetter = GetPropertyMethodDef(mPropDef, BfMethodType_PropertySetter, BfCheckedKind_NotSet, mPropTarget) != NULL; - auto autoFieldName = curCheckType->mTypeDef->GetAutoPropertyName(propertyDeclaration); - auto result = LookupField(targetSrc, target, autoFieldName, (BfLookupFieldFlags)(BfLookupFieldFlag_IgnoreProtection | BfLookupFieldFlag_IsImplicitThis)); - if (result) + BfMethodDef* getter = GetPropertyMethodDef(mPropDef, BfMethodType_PropertyGetter, BfCheckedKind_NotSet, mPropTarget); + BfMethodDef* setter = GetPropertyMethodDef(mPropDef, BfMethodType_PropertySetter, BfCheckedKind_NotSet, mPropTarget); + + bool optAllowed = true; + if ((getter != NULL) && (getter->mBody != NULL)) + optAllowed = false; + if ((setter != NULL) && (setter->mBody != NULL)) + optAllowed = false; + + if (optAllowed) { - bool needsCopy = true; - - if (!hasSetter) + auto autoFieldName = curCheckType->mTypeDef->GetAutoPropertyName(propertyDeclaration); + auto result = LookupField(targetSrc, target, autoFieldName, (BfLookupFieldFlags)(BfLookupFieldFlag_IgnoreProtection | BfLookupFieldFlag_IsImplicitThis)); + if (result) { - if (((mModule->mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor)) && - (startCheckType == mModule->mCurTypeInstance)) + bool needsCopy = true; + + if (setter == NULL) { - // Allow writing inside ctor - } - else - { - result.MakeReadOnly(); - needsCopy = false; + if (((mModule->mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor)) && + (startCheckType == mModule->mCurTypeInstance)) + { + // Allow writing inside ctor + } + else + { + result.MakeReadOnly(); + needsCopy = false; + } } + + if (result.mKind == BfTypedValueKind_Addr) + result.mKind = BfTypedValueKind_CopyOnMutateAddr; + + mPropDef = NULL; + mPropSrc = NULL; + mOrigPropTarget = NULL; + return result; } - - if (result.mKind == BfTypedValueKind_Addr) - result.mKind = BfTypedValueKind_CopyOnMutateAddr; - - mPropDef = NULL; - mPropSrc = NULL; - mOrigPropTarget = NULL; - return result; } } } @@ -5022,8 +5106,11 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr if (auto unaryOpExpr = BfNodeDynCastExact(argExpr)) { - if (unaryOpExpr->mOp == BfUnaryOp_Cascade) + if ((unaryOpExpr->mOp == BfUnaryOp_Cascade) && ((flags & BfResolveArgsFlag_FromIndexer) == 0)) { + if ((mBfEvalExprFlags & BfEvalExprFlags_InCascade) != 0) + mModule->Fail("Cascade already specified on call target", unaryOpExpr->mOpToken); + resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_Cascade); argExpr = unaryOpExpr->mExpression; } @@ -5194,7 +5281,7 @@ void BfExprEvaluator::PerformCallChecks(BfMethodInstance* methodInstance, BfAstN mModule->CheckErrorAttributes(methodInstance->GetOwner(), methodInstance, customAttributes, targetSrc); } -BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl& irArgs, BfTypedValue* sret, bool isTailCall) +BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl& irArgs, BfTypedValue* sret, BfCreateCallFlags callFlags) { // static int sCallIdx = 0; // if (!mModule->mCompiler->mIsResolveOnly) @@ -5252,7 +5339,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* } } - if ((methodInstance->GetOwner()->mTypeDef == mModule->mCompiler->mDeferredCallTypeDef) && + if ((methodInstance->GetOwner()->IsInstanceOf(mModule->mCompiler->mDeferredCallTypeDef)) && (methodInstance->mMethodDef->mName == "Cancel")) { if (mModule->mCurMethodState != NULL) @@ -5437,12 +5524,22 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* { doConstReturn = true; } + else if (((callFlags & BfCreateCallFlags_GenericParamThis) != 0) && (methodInstance->GetOwner()->IsInterface())) + { + mModule->Warn(0, "Concrete method may fail to comptime during specialization", targetSrc); + doConstReturn = true; + } + else if (methodDef->mIsVirtual) + { + // This could only really be the case for a Type, since no other 'this' could qualify as const + } else { CeEvalFlags evalFlags = CeEvalFlags_None; auto constRet = mModule->mCompiler->mCEMachine->Call(targetSrc, mModule, methodInstance, irArgs, evalFlags, mExpectingType); if (constRet) { + auto constant = mModule->mBfIRBuilder->GetConstant(constRet.mValue); BF_ASSERT(!constRet.mType->IsVar()); return constRet; } @@ -5482,13 +5579,13 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* { if (returnType->IsInstanceOf(mModule->mCompiler->mSpanTypeDef)) { - if (mExpectingType->IsUndefSizedArray()) + if ((mExpectingType != NULL) && (mExpectingType->IsUndefSizedArray())) { if (returnType->GetUnderlyingType() == mExpectingType->GetUnderlyingType()) return mModule->GetDefaultTypedValue(mExpectingType, true, BfDefaultValueKind_Undef); } } - + return mModule->GetDefaultTypedValue(returnType, true, BfDefaultValueKind_Undef); } @@ -5819,7 +5916,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* mModule->mBfIRBuilder->Call_AddAttribute(callInst, argIdx + 1, BfIRAttribute_NoCapture); addDeref = paramType->mSize; } - else if ((methodInstance->WantsStructsAttribByVal()) && (!paramType->IsSizedArray())) + else if (methodInstance->WantsStructsAttribByVal(paramType)) { mModule->mBfIRBuilder->Call_AddAttribute(callInst, argIdx + 1, BfIRAttribute_ByVal, mModule->mSystem->mPtrSize); } @@ -5902,7 +5999,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* //argIdx++; } - if (isTailCall) + if ((callFlags & BfCreateCallFlags_TailCall) != 0) mModule->mBfIRBuilder->SetTailCall(callInst); if (methodDef->mIsNoReturn) @@ -5981,9 +6078,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfMethodMatcher* methodMatcher, BfTyped PerformCallChecks(moduleMethodInstance.mMethodInstance, methodMatcher->mTargetSrc); - BfCreateFallFlags callFlags = BfCreateFallFlags_None; + BfCreateCallFlags callFlags = BfCreateCallFlags_None; if (methodMatcher->mAllowImplicitRef) - callFlags = (BfCreateFallFlags)(callFlags | BfCreateFallFlags_AllowImplicitRef); + callFlags = (BfCreateCallFlags)(callFlags | BfCreateCallFlags_AllowImplicitRef); return CreateCall(methodMatcher->mTargetSrc, target, BfTypedValue(), methodMatcher->mBestMethodDef, moduleMethodInstance, callFlags, methodMatcher->mArguments); } @@ -6315,10 +6412,10 @@ void BfExprEvaluator::AddCallDependencies(BfMethodInstance* methodInstance) } //TODO: delete argumentsZ -BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValue& inTarget, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance moduleMethodInstance, BfCreateFallFlags callFlags, SizedArrayImpl& argValues, BfTypedValue* argCascade) +BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValue& inTarget, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance moduleMethodInstance, BfCreateCallFlags callFlags, SizedArrayImpl& argValues, BfTypedValue* argCascade) { - bool bypassVirtual = (callFlags & BfCreateFallFlags_BypassVirtual) != 0; - bool skipThis = (callFlags & BfCreateFallFlags_SkipThis) != 0;; + bool bypassVirtual = (callFlags & BfCreateCallFlags_BypassVirtual) != 0; + bool skipThis = (callFlags & BfCreateCallFlags_SkipThis) != 0;; static int sCallIdx = 0; if (!mModule->mCompiler->mIsResolveOnly) @@ -6379,6 +6476,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu if (delegateInfo->mHasExplicitThis) { target = mModule->GetDefaultTypedValue(delegateInfo->mParams[0], false, BfDefaultValueKind_Addr); + bypassVirtual = true; } } else if (bindResult->mBindType->IsFunction()) @@ -6510,7 +6608,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu if (argValue.mType == mModule->GetPrimitiveType(BfTypeCode_Float)) argValue = mModule->Cast(argValues[argExprIdx].mExpression, argValue, mModule->GetPrimitiveType(BfTypeCode_Double)); - if ((typeInst != NULL) && (typeInst->mTypeDef == mModule->mCompiler->mStringTypeDef)) + if ((typeInst != NULL) && (typeInst->IsInstanceOf(mModule->mCompiler->mStringTypeDef))) { BfType* charType = mModule->GetPrimitiveType(BfTypeCode_Char8); BfType* charPtrType = mModule->CreatePointerType(charType); @@ -7029,7 +7127,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu refNode = targetSrc; if ((wantType->IsRef()) && (!argValue.mType->IsRef()) && - (((callFlags & BfCreateFallFlags_AllowImplicitRef) != 0) || (wantType->IsIn()))) + (((callFlags & BfCreateCallFlags_AllowImplicitRef) != 0) || (wantType->IsIn()))) argValue = mModule->ToRef(argValue, (BfRefType*)wantType); if (mModule->mCurMethodState != NULL) @@ -7256,8 +7354,12 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu return BfTypedValue(); } + BfCreateCallFlags physCallFlags = BfCreateCallFlags_None; + if ((origTarget.mType != NULL) && (origTarget.mType->IsGenericParam())) + physCallFlags = (BfCreateCallFlags)(physCallFlags | BfCreateCallFlags_GenericParamThis); + auto func = moduleMethodInstance.mFunc; - BfTypedValue callResult = CreateCall(targetSrc, methodInstance, func, bypassVirtual, irArgs); + BfTypedValue callResult = CreateCall(targetSrc, methodInstance, func, bypassVirtual, irArgs, NULL, physCallFlags); // This gets triggered for non-sret (ie: comptime) composite returns so they aren't considered readonly if ((callResult.mKind == BfTypedValueKind_Value) && (!callResult.mValue.IsConst()) && @@ -7384,7 +7486,7 @@ BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBou auto methodDef = methodMatcher.mBestMethodDef; if (mModule->mCompiler->mResolvePassData != NULL) - mModule->mCompiler->mResolvePassData->HandleMethodReference(targetSrc, curTypeInst->mTypeDef, methodDef); + mModule->mCompiler->mResolvePassData->HandleMethodReference(targetSrc, curTypeInst->mTypeDef->GetDefinition(), methodDef); // There should always be a constructor BF_ASSERT(methodMatcher.mBestMethodDef != NULL); @@ -7459,7 +7561,7 @@ BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBou if (isFailurePass) mModule->Fail(StrFormat("'%s' is inaccessible due to its protection level", mModule->MethodToString(moduleMethodInstance.mMethodInstance).c_str()), targetSrc); prevBindResult.Restore(); - return CreateCall(methodMatcher.mTargetSrc, target, BfTypedValue(), methodMatcher.mBestMethodDef, moduleMethodInstance, BfCreateFallFlags_None, methodMatcher.mArguments); + return CreateCall(methodMatcher.mTargetSrc, target, BfTypedValue(), methodMatcher.mBestMethodDef, moduleMethodInstance, BfCreateCallFlags_None, methodMatcher.mArguments); } static int sInvocationIdx = 0; @@ -7851,27 +7953,30 @@ BfTypedValue BfExprEvaluator::CheckEnumCreation(BfAstNode* targetSrc, BfTypeInst bool BfExprEvaluator::CheckGenericCtor(BfGenericParamType* genericParamType, BfResolvedArgs& argValues, BfAstNode* targetSrc) { - auto genericConstraint = mModule->GetGenericParamInstance(genericParamType); + BfGenericParamFlags genericParamFlags = BfGenericParamFlag_None; + BfType* typeConstraint = NULL; + auto genericParam = mModule->GetMergedGenericParamData((BfGenericParamType*)genericParamType, genericParamFlags, typeConstraint); + bool success = true; if ((argValues.mArguments != NULL) && (argValues.mArguments->size() != 0)) { - mModule->Fail(StrFormat("Only default parameterless constructors can be called on generic argument '%s'", genericConstraint->GetGenericParamDef()->mName.c_str()), targetSrc); + mModule->Fail(StrFormat("Only default parameterless constructors can be called on generic argument '%s'", genericParam->GetGenericParamDef()->mName.c_str()), targetSrc); success = false; } - else if ((genericConstraint->mGenericParamFlags & (BfGenericParamFlag_New | BfGenericParamFlag_Struct)) == 0) + else if ((genericParamFlags & (BfGenericParamFlag_New | BfGenericParamFlag_Struct | BfGenericParamFlag_Var)) == 0) { - mModule->Fail(StrFormat("Must add 'where %s : new, struct' constraint to generic parameter to instantiate type", genericConstraint->GetGenericParamDef()->mName.c_str()), targetSrc); + mModule->Fail(StrFormat("Must add 'where %s : new, struct' constraint to generic parameter to instantiate type", genericParam->GetGenericParamDef()->mName.c_str()), targetSrc); success = false; } - else if ((genericConstraint->mGenericParamFlags & BfGenericParamFlag_New) == 0) + else if ((genericParamFlags & (BfGenericParamFlag_New | BfGenericParamFlag_Var)) == 0) { - mModule->Fail(StrFormat("Must add 'where %s : new' constraint to generic parameter to instantiate type", genericConstraint->GetGenericParamDef()->mName.c_str()), targetSrc); + mModule->Fail(StrFormat("Must add 'where %s : new' constraint to generic parameter to instantiate type", genericParam->GetGenericParamDef()->mName.c_str()), targetSrc); success = false; } - else if ((genericConstraint->mGenericParamFlags & BfGenericParamFlag_Struct) == 0) + else if ((genericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_Var)) == 0) { - mModule->Fail(StrFormat("Must add 'where %s : struct' constraint to generic parameter to instantiate type without allocator", genericConstraint->GetGenericParamDef()->mName.c_str()), targetSrc); + mModule->Fail(StrFormat("Must add 'where %s : struct' constraint to generic parameter to instantiate type without allocator", genericParam->GetGenericParamDef()->mName.c_str()), targetSrc); success = false; } @@ -8058,6 +8163,30 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp { if (targetType->IsWrappableType()) { + if ((targetType->IsPrimitiveType()) && (methodName.IsEmpty())) + { + if (argValues.mArguments->IsEmpty()) + { + return mModule->GetDefaultTypedValue(targetType); + } + else if (argValues.mArguments->mSize == 1) + { + FinishDeferredEvals(argValues); + + // This is just a primitive cast + auto& resolvedArg = argValues.mResolvedArgs[0]; + BfTypedValue castedValue; + BfTypedValue castTarget = resolvedArg.mTypedValue; + if (resolvedArg.mTypedValue) + { + castTarget = mModule->LoadValue(castTarget); + castedValue = mModule->Cast(targetSrc, castTarget, targetType, BfCastFlags_Explicit); + } + if (!castedValue) + castedValue = mModule->GetDefaultTypedValue(targetType); + return castedValue; + } + } targetTypeInst = mModule->GetWrappedStructType(targetType); } else if (targetType->IsGenericParam()) @@ -8143,7 +8272,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp methodMatcher.mAllowImplicitThis = allowImplicitThis; methodMatcher.mAllowStatic = !target.mValue; methodMatcher.mAllowNonStatic = !methodMatcher.mAllowStatic; - methodMatcher.mAutoFlushAmbiguityErrors = !wantsExtensionCheck; + methodMatcher.mAutoFlushAmbiguityErrors = !wantsExtensionCheck; if (allowImplicitThis) { if (mModule->mCurMethodState == NULL) @@ -8449,6 +8578,9 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp { FinishDeferredEvals(argValues); + if (argValues.mResolvedArgs.IsEmpty()) + return mModule->GetDefaultTypedValue(refType); + if (argValues.mResolvedArgs.size() != 1) { mModule->Fail("Cast requires one parameter", targetSrc); @@ -8494,7 +8626,10 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if (auto identifier = BfNodeDynCastExact(targetSrc)) mModule->SetElementType(identifier, resolvedTypeInstance->IsEnum() ? BfSourceElementType_Type : BfSourceElementType_Struct); if (mModule->mCompiler->mResolvePassData != NULL) - mModule->mCompiler->mResolvePassData->HandleTypeReference(targetSrc, resolvedTypeInstance->mTypeDef); + { + if (!BfNodeIsA(targetSrc)) + mModule->mCompiler->mResolvePassData->HandleTypeReference(targetSrc, resolvedTypeInstance->mTypeDef); + } BfTypedValue structInst; mModule->PopulateType(resolvedTypeInstance); @@ -8607,7 +8742,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if (genericParamInstance->mTypeConstraint != NULL) typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance(); if ((typeInstConstraint != NULL) && - ((typeInstConstraint->mTypeDef == mModule->mCompiler->mDelegateTypeDef) || (typeInstConstraint->mTypeDef == mModule->mCompiler->mFunctionTypeDef))) + ((typeInstConstraint->IsInstanceOf(mModule->mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mModule->mCompiler->mFunctionTypeDef)))) { MarkResultUsed(); @@ -8644,6 +8779,10 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp prevBindResult.Restore(); auto fieldTypeInst = fieldVal.mType->ToTypeInstance(); MarkResultUsed(); + if (mFunctionBindResult != NULL) + { + mFunctionBindResult->mOrigTarget = BfTypedValue(); + } return MatchMethod(targetSrc, NULL, fieldVal, false, false, "Invoke", argValues, methodGenericArguments, checkedKind); } if (fieldVal.mType->IsVar()) @@ -8809,13 +8948,14 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp { BfType* retType = mModule->GetPrimitiveType(BfTypeCode_Var); - if ((!methodMatcher.mHadVarConflictingReturnType) && (methodMatcher.mBestRawMethodInstance != NULL) && (!methodMatcher.mBestRawMethodInstance->mReturnType->IsUnspecializedTypeVariation())) + if ((!methodMatcher.mHadVarConflictingReturnType) && (methodMatcher.mBestRawMethodInstance != NULL) && (!methodMatcher.mBestRawMethodInstance->mReturnType->IsUnspecializedTypeVariation()) && + (prevBindResult.mPrevVal == NULL)) { if ((!methodMatcher.mBestRawMethodInstance->mReturnType->IsGenericParam()) || (((BfGenericParamType*)methodMatcher.mBestRawMethodInstance->mReturnType)->mGenericParamKind != BfGenericParamKind_Method)) retType = methodMatcher.mBestRawMethodInstance->mReturnType; } - + return mModule->GetDefaultTypedValue(retType, true, BfDefaultValueKind_Addr); } } @@ -8827,6 +8967,8 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if (!moduleMethodInstance) { FinishDeferredEvals(argValues); + if (mModule->IsInUnspecializedGeneric()) + return mModule->GetDefaultTypedValue(mModule->GetPrimitiveType(BfTypeCode_Var)); return BfTypedValue(); } @@ -8987,7 +9129,8 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp auto identifierNode = BfNodeDynCast(targetSrc); while (auto qualifiedNameNode = BfNodeDynCast(identifierNode)) identifierNode = qualifiedNameNode->mRight; - if ((identifierNode != NULL) && (methodDef->mIdx >= 0) && (!isIndirectMethodCall)) + if ((identifierNode != NULL) && (methodDef->mIdx >= 0) && (!isIndirectMethodCall) && + ((targetTypeInst == NULL) || (!targetTypeInst->IsDelegateOrFunction()))) { mModule->mCompiler->mResolvePassData->HandleMethodReference(identifierNode, moduleMethodInstance.mMethodInstance->GetOwner()->mTypeDef, methodDef); auto autoComplete = GetAutoComplete(); @@ -9130,11 +9273,11 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp mModule->Fail(StrFormat("Method '%s' can only be invoked at comptime. Consider adding [Comptime] to the current method.", mModule->MethodToString(moduleMethodInstance.mMethodInstance).c_str()), targetSrc); } - BfCreateFallFlags subCallFlags = BfCreateFallFlags_None; + BfCreateCallFlags subCallFlags = BfCreateCallFlags_None; if (bypassVirtual) - subCallFlags = (BfCreateFallFlags)(subCallFlags | BfCreateFallFlags_BypassVirtual); + subCallFlags = (BfCreateCallFlags)(subCallFlags | BfCreateCallFlags_BypassVirtual); if (skipThis) - subCallFlags = (BfCreateFallFlags)(subCallFlags | BfCreateFallFlags_SkipThis); + subCallFlags = (BfCreateCallFlags)(subCallFlags | BfCreateCallFlags_SkipThis); result = CreateCall(targetSrc, callTarget, origTarget, methodDef, moduleMethodInstance, subCallFlags, argValues.mResolvedArgs, &argCascade); } @@ -9678,7 +9821,7 @@ void BfExprEvaluator::LookupQualifiedStaticField(BfAstNode* nameNode, BfIdentifi { // Lookup left side as a type { - BfType* type = mModule->ResolveTypeRef(nameLeft, NULL, BfPopulateType_Declaration, BfResolveTypeRefFlag_IgnoreLookupError); + BfType* type = mModule->ResolveTypeRef(nameLeft, NULL, BfPopulateType_Declaration, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_IgnoreLookupError | BfResolveTypeRefFlag_AllowGlobalContainer)); if (type != NULL) { BfTypedValue lookupType; @@ -9695,13 +9838,19 @@ void BfExprEvaluator::LookupQualifiedStaticField(BfAstNode* nameNode, BfIdentifi { mResult = LookupField(nameRight, BfTypedValue(genericParamInstance->mTypeConstraint), findName); if ((mResult) || (mPropDef != NULL)) + { + mOrigPropTarget = lookupType; return; + } } for (auto constraint : genericParamInstance->mInterfaceConstraints) { mResult = LookupField(nameRight, BfTypedValue(constraint), findName); if ((mResult) || (mPropDef != NULL)) + { + mOrigPropTarget = lookupType; return; + } } } @@ -9832,6 +9981,9 @@ void BfExprEvaluator::Visit(BfBaseExpression* baseExpr) return; } + if ((mBfEvalExprFlags & BfEvalExprFlags_AllowBase) == 0) + mModule->Fail("Use of keyword 'base' is not valid in this context", baseExpr); + auto baseType = mModule->mCurTypeInstance->mBaseType; if (baseType == NULL) baseType = mModule->mContext->mBfObjectType; @@ -9842,6 +9994,12 @@ void BfExprEvaluator::Visit(BfBaseExpression* baseExpr) mModule->PopulateType(baseType, BfPopulateType_Data); mResult = mModule->Cast(baseExpr, mResult, baseType, BfCastFlags_Explicit); + if (mResult.IsSplat()) + mResult.mKind = BfTypedValueKind_BaseSplatHead; + else if (mResult.IsAddr()) + mResult.mKind = BfTypedValueKind_BaseAddr; + else if (mResult) + mResult.mKind = BfTypedValueKind_BaseValue; } void BfExprEvaluator::Visit(BfMixinExpression* mixinExpr) @@ -10045,8 +10203,8 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) SizedArray argExprs; argExprs.push_back(elementExpr); BfSizedArray sizedArgExprs(argExprs); - BfResolvedArgs argValues(&sizedArgExprs); - exprEvaluator.ResolveArgValues(argValues); + BfResolvedArgs argValues(&sizedArgExprs); + exprEvaluator.ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); exprEvaluator.MatchMethod(elementExpr, NULL, initValue, false, false, "Add", argValues, NULL); if (addFunctionBindResult.mMethodInstance != NULL) @@ -10115,7 +10273,7 @@ void BfExprEvaluator::Visit(BfTypeOfExpression* typeOfExpr) } else { - type = ResolveTypeRef(typeOfExpr->mTypeRef, BfPopulateType_Identity); + type = ResolveTypeRef(typeOfExpr->mTypeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowGlobalsSelf); } if (type == NULL) @@ -10124,8 +10282,15 @@ void BfExprEvaluator::Visit(BfTypeOfExpression* typeOfExpr) return; } - mModule->AddDependency(type, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); - mResult = BfTypedValue(mModule->CreateTypeDataRef(type), typeType); + if (type->IsGenericParam()) + { + mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->MapType(typeType)), typeType); + } + else + { + mModule->AddDependency(type, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); + mResult = BfTypedValue(mModule->CreateTypeDataRef(type), typeType); + } } bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifierNode* propName) @@ -10247,7 +10412,16 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)checkType); if (((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Enum) != 0) || ((genericParamInstance->mTypeConstraint != NULL) && (genericParamInstance->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mEnumTypeDef)))) - foundMatch = true; + foundMatch = true; + + else + { + for (auto constraint : genericParamInstance->mInterfaceConstraints) + { + if (constraint->IsInstanceOf(mModule->mCompiler->mIIntegerTypeDef)) + foundMatch = true; + } + } if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL)) { @@ -10275,7 +10449,7 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie { auto primType = (BfPrimitiveType*)checkType; - if (typeInstance->IsEnum()) + if ((typeInstance != NULL) && (typeInstance->IsEnum())) { if (typeInstance->mTypeInfoEx != NULL) { @@ -10288,31 +10462,43 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie switch (primType->mTypeDef->mTypeCode) { case BfTypeCode_Int8: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x80 : 0x7F), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x80 : 0x7F), primType); return true; case BfTypeCode_Int16: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x8000 : 0x7FFF), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x8000 : 0x7FFF), primType); return true; case BfTypeCode_Int32: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), primType); return true; case BfTypeCode_Int64: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), primType); return true; case BfTypeCode_UInt8: case BfTypeCode_Char8: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFF), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFF), primType); return true; case BfTypeCode_UInt16: case BfTypeCode_Char16: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFFFF), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFFFF), primType); return true; case BfTypeCode_UInt32: case BfTypeCode_Char32: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), primType); return true; case BfTypeCode_UInt64: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), primType); + return true; + case BfTypeCode_IntPtr: + if (mModule->mSystem->mPtrSize == 8) + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), primType); + else + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), primType); + return true; + case BfTypeCode_UIntPtr: + if (mModule->mSystem->mPtrSize == 8) + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), primType); + else + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), primType); return true; default: break; } @@ -10330,6 +10516,12 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie } else return false; + + if (type->IsGenericParam()) + { + if (mResult.mType != NULL) + mResult = mModule->GetDefaultTypedValue(mResult.mType, false, Beefy::BfDefaultValueKind_Undef); + } auto autoComplete = GetAutoComplete(); if ((autoComplete != NULL) && (typeOfExpr->mTypeRef != NULL)) @@ -10340,8 +10532,11 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie return true; } -void BfExprEvaluator::DoTypeIntAttr(BfTypeReference* typeRef, BfToken token) +void BfExprEvaluator::DoTypeIntAttr(BfTypeReference* typeRef, BfTokenNode* commaToken, BfIdentifierNode* memberName, BfToken token) { + auto autoComplete = GetAutoComplete(); + + auto type = mModule->ResolveTypeRef(typeRef, BfPopulateType_Data, BfResolveTypeRefFlag_AutoComplete); if (type == NULL) return; @@ -10364,6 +10559,72 @@ void BfExprEvaluator::DoTypeIntAttr(BfTypeReference* typeRef, BfToken token) default: break; } + if (token == BfToken_OffsetOf) + { + bool found = false; + String findName; + if (memberName != NULL) + findName = memberName->ToString(); + + BfAstNode* refNode = typeRef; + if (memberName != NULL) + refNode = memberName; + + auto checkTypeInst = typeInst; + while (checkTypeInst != NULL) + { + checkTypeInst->mTypeDef->PopulateMemberSets(); + + String filter; + if ((autoComplete != NULL) && (autoComplete->InitAutocomplete(commaToken, memberName, filter))) + { + auto activeTypeDef = mModule->GetActiveTypeDef(); + mModule->PopulateType(checkTypeInst); + + BfProtectionCheckFlags protectionCheckFlags = BfProtectionCheckFlag_None; + for (auto fieldDef : checkTypeInst->mTypeDef->mFields) + { + if (fieldDef->mIsStatic) + continue; + + if (!mModule->CheckProtection(protectionCheckFlags, typeInst, fieldDef->mDeclaringType->mProject, fieldDef->mProtection, typeInst)) + continue; + + if ((!typeInst->IsTypeMemberIncluded(fieldDef->mDeclaringType, activeTypeDef, mModule)) || + (!typeInst->IsTypeMemberAccessible(fieldDef->mDeclaringType, activeTypeDef))) + continue; + + auto& fieldInst = checkTypeInst->mFieldInstances[fieldDef->mIdx]; + autoComplete->AddField(checkTypeInst, fieldDef, &fieldInst, filter); + } + } + + BfMemberSetEntry* memberSetEntry = NULL; + if (checkTypeInst->mTypeDef->mFieldSet.TryGetWith(findName, &memberSetEntry)) + { + auto fieldDef = (BfFieldDef*)memberSetEntry->mMemberDef; + if (fieldDef != NULL) + { + if (fieldDef->mIsStatic) + mModule->Fail(StrFormat("Cannot generate an offset from static field '%s.%s'", mModule->TypeToString(type).c_str(), fieldDef->mName.c_str()), refNode); + + mModule->PopulateType(checkTypeInst); + auto& fieldInst = checkTypeInst->mFieldInstances[fieldDef->mIdx]; + attrVal = fieldInst.mDataOffset; + found = true; + break; + } + } + + checkTypeInst = checkTypeInst->mBaseType; + } + + if (!found) + { + mModule->Fail(StrFormat("Unable to locate field '%s.%s'", mModule->TypeToString(type).c_str(), findName.c_str()), refNode); + } + } + bool isUndefVal = false; if (type->IsGenericParam()) @@ -10389,17 +10650,22 @@ void BfExprEvaluator::DoTypeIntAttr(BfTypeReference* typeRef, BfToken token) void BfExprEvaluator::Visit(BfSizeOfExpression* sizeOfExpr) { - DoTypeIntAttr(sizeOfExpr->mTypeRef, BfToken_SizeOf); + DoTypeIntAttr(sizeOfExpr->mTypeRef, NULL, NULL, BfToken_SizeOf); } void BfExprEvaluator::Visit(BfAlignOfExpression* alignOfExpr) { - DoTypeIntAttr(alignOfExpr->mTypeRef, BfToken_AlignOf); + DoTypeIntAttr(alignOfExpr->mTypeRef, NULL, NULL, BfToken_AlignOf); } void BfExprEvaluator::Visit(BfStrideOfExpression* strideOfExpr) { - DoTypeIntAttr(strideOfExpr->mTypeRef, BfToken_StrideOf); + DoTypeIntAttr(strideOfExpr->mTypeRef, NULL, NULL, BfToken_StrideOf); +} + +void BfExprEvaluator::Visit(BfOffsetOfExpression* offsetOfExpr) +{ + DoTypeIntAttr(offsetOfExpr->mTypeRef, offsetOfExpr->mCommaToken, offsetOfExpr->mMemberName, BfToken_OffsetOf); } void BfExprEvaluator::Visit(BfDefaultExpression* defaultExpr) @@ -11601,7 +11867,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) return; } } - result = mModule->CastToFunction(delegateBindExpr->mTarget, bindResult.mOrigTarget, bindResult.mMethodInstance, mExpectingType); + result = mModule->CastToFunction(delegateBindExpr->mTarget, bindResult.mOrigTarget, bindResult.mMethodInstance, mExpectingType, BfCastFlags_None, bindResult.mFunc); } if (result) mResult = BfTypedValue(result, mExpectingType); @@ -12000,6 +12266,9 @@ void BfExprEvaluator::VisitLambdaBodies(BfAstNode* body, BfFieldDtorDeclaration* BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lambdaBindExpr, BfAllocTarget& allocTarget) { + if (mModule->mCurMethodState == NULL) + return NULL; + auto rootMethodState = mModule->mCurMethodState->GetRootMethodState(); BfAstNodeList cacheNodeList; cacheNodeList.mList.Add(lambdaBindExpr); @@ -12160,7 +12429,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam ( { if (autoComplete != NULL) - autoComplete->mIsCapturingMethodMatchInfo = (wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo); + autoComplete->mIsCapturingMethodMatchInfo = (wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo) && (autoComplete->mMethodMatchInfo != NULL); } ); @@ -12232,6 +12501,8 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam Val128 val128(delegateTypeInstance->mTypeId); + bool isConstEval = ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0); + BfMethodState methodState; methodState.mPrevMethodState = mModule->mCurMethodState; BfIRFunctionType funcType; @@ -12240,7 +12511,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam funcType = mModule->mBfIRBuilder->CreateFunctionType(mModule->mBfIRBuilder->MapType(voidType), paramTypes, false); auto prevInsertBlock = mModule->mBfIRBuilder->GetInsertBlock(); - BF_ASSERT(prevInsertBlock); + BF_ASSERT(prevInsertBlock || isConstEval); auto prevActiveFunction = mModule->mBfIRBuilder->GetActiveFunction(); mModule->mBfIRBuilder->SaveDebugLocation(); @@ -12329,6 +12600,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam methodDef->mParams.push_back(paramDef); localVar->mResolvedType = invokeMethodInstance->GetParamType(paramIdx); + mModule->PopulateType(localVar->mResolvedType); localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional; localVar->mReadFromId = 0; @@ -12818,7 +13090,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam prevMethodState.Restore(); mModule->mBfIRBuilder->SetActiveFunction(prevActiveFunction); - if (!prevInsertBlock.IsFake()) + if ((prevInsertBlock) && (!prevInsertBlock.IsFake())) mModule->mBfIRBuilder->SetInsertPoint(prevInsertBlock); // Just a check @@ -13000,7 +13272,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam } prevClosureState.Restore(); - if (!prevInsertBlock.IsFake()) + if ((prevInsertBlock) && (!prevInsertBlock.IsFake())) mModule->mBfIRBuilder->SetInsertPoint(prevInsertBlock); for (auto& capturedEntry : capturedEntries) @@ -13623,8 +13895,17 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs if ((constant != NULL) && (mModule->mBfIRBuilder->IsInt(constant->mTypeCode))) { int64 dimLength = constant->mInt64; + if (dimLength < 0) + { + mModule->Fail(StrFormat("Invalid array dimension '%lld'", dimLength), dimLengthRefs[dim]); + dimLength = -1; + } dimLengths.push_back(dimLength); } + else if ((constant != NULL) && (constant->mConstType == BfConstType_Undef)) + { + dimLengths.push_back(-1); + } else { mModule->Fail("A constant length is required when using an initializer", dimLengthRefs[dim]); @@ -14004,16 +14285,19 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs bool isGenericParam = unresolvedTypeRef->IsGenericParam(); if (resolvedTypeRef->IsGenericParam()) { - auto genericParam = mModule->GetGenericParamInstance((BfGenericParamType*)resolvedTypeRef); - if (genericParam->mTypeConstraint == NULL) + BfGenericParamFlags genericParamFlags = BfGenericParamFlag_None; + BfType* typeConstraint = NULL; + auto genericParam = mModule->GetMergedGenericParamData((BfGenericParamType*)resolvedTypeRef, genericParamFlags, typeConstraint); + + if (typeConstraint == NULL) { - if ((genericParam->mGenericParamFlags & BfGenericParamFlag_Var) != 0) + if ((genericParamFlags & BfGenericParamFlag_Var) != 0) { // Allow it } else { - if ((genericParam->mGenericParamFlags & BfGenericParamFlag_New) == 0) + if ((genericParamFlags & BfGenericParamFlag_New) == 0) { mModule->Fail(StrFormat("Must add 'where %s : new' constraint to generic parameter to instantiate type", genericParam->GetName().c_str()), objCreateExpr->mTypeRef); } @@ -14024,13 +14308,13 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs } } - if (((genericParam->mTypeConstraint != NULL) && (genericParam->mTypeConstraint->IsValueType())) || - ((genericParam->mGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr)) != 0)) + if (((typeConstraint != NULL) && (typeConstraint->IsValueType())) || + ((genericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr)) != 0)) { resultType = mModule->CreatePointerType(resolvedTypeRef); } - else if (((genericParam->mTypeConstraint != NULL) && (!genericParam->mTypeConstraint->IsValueType())) || - ((genericParam->mGenericParamFlags & (BfGenericParamFlag_Class)) != 0)) + else if (((typeConstraint != NULL) && (!typeConstraint->IsValueType())) || + ((genericParamFlags & (BfGenericParamFlag_Class)) != 0)) { // Leave as 'T' resultType = resolvedTypeRef; @@ -14450,7 +14734,7 @@ BfAllocTarget BfExprEvaluator::ResolveAllocTarget(BfAstNode* allocNode, BfTokenN { for (auto& attrib : customAttrs->mAttributes) { - if (attrib.mType->mTypeDef == mModule->mCompiler->mAlignAttributeTypeDef) + if (attrib.mType->IsInstanceOf(mModule->mCompiler->mAlignAttributeTypeDef)) { allocTarget.mAlignOverride = 16; // System conservative default @@ -14468,7 +14752,7 @@ BfAllocTarget BfExprEvaluator::ResolveAllocTarget(BfAstNode* allocNode, BfTokenN } } } - else if (attrib.mType->mTypeDef == mModule->mCompiler->mFriendAttributeTypeDef) + else if (attrib.mType->IsInstanceOf(mModule->mCompiler->mFriendAttributeTypeDef)) allocTarget.mIsFriend = true; } @@ -14747,6 +15031,7 @@ BfModuleMethodInstance BfExprEvaluator::GetSelectedMethod(BfAstNode* targetSrc, if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL) && (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mInlineAttributeTypeDef))) { flags = (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_ForceInline); + mModule->mAttributeState->mUsed = true; } if ((!mModule->mCurTypeInstance->IsInterface()) && (methodDef->mBody != NULL)) @@ -15057,7 +15342,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } auto autoComplete = GetAutoComplete(); - if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (autoComplete->mMethodMatchInfo->mInstanceList.size() != 0)) + if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (autoComplete->mMethodMatchInfo != NULL) && (autoComplete->mMethodMatchInfo->mInstanceList.size() != 0)) autoComplete->mIsCapturingMethodMatchInfo = false; BfMethodMatcher methodMatcher(targetSrc, mModule, name, args, methodGenericArgs); @@ -15086,7 +15371,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo if ((methodMatcher.mBestMethodDef == NULL) && (target.mType == NULL) && (mModule->mContext->mCurTypeState != NULL)) { - BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mTypeInstance); + BF_ASSERT(mModule->mCurTypeInstance == mModule->mContext->mCurTypeState->mType); BfGlobalLookup globalLookup; globalLookup.mKind = BfGlobalLookup::Kind_Method; globalLookup.mName = name; @@ -15126,38 +15411,40 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo auto curMethodState = mModule->mCurMethodState; // - { - bool hasCircularRef = false; - auto checkMethodState = curMethodState; - while (checkMethodState != NULL) - { - auto curMixinState = checkMethodState->mMixinState; - while (curMixinState != NULL) - { - if (curMixinState->mSource == targetSrc) - hasCircularRef = true; - curMixinState = curMixinState->mPrevMixinState; - } - - if ((checkMethodState->mClosureState != NULL) && (checkMethodState->mClosureState->mActiveDeferredLocalMethod != NULL)) - { - for (auto& mixinRecord : checkMethodState->mClosureState->mActiveDeferredLocalMethod->mMixinStateRecords) - { - if (mixinRecord.mSource == targetSrc) - hasCircularRef = true; - } - } - - checkMethodState = checkMethodState->mPrevMethodState; - } - - if (hasCircularRef) - { - mModule->Fail("Circular reference detected between mixins", targetSrc); - return; - } - } + // Why was this required? It doesn't check for matching generic args (we only want to throw an error if we call back into a mixin with the same generic args as before) +// { +// bool hasCircularRef = false; +// +// auto checkMethodState = curMethodState; +// while (checkMethodState != NULL) +// { +// auto curMixinState = checkMethodState->mMixinState; +// while (curMixinState != NULL) +// { +// if (curMixinState->mSource == targetSrc) +// hasCircularRef = true; +// curMixinState = curMixinState->mPrevMixinState; +// } +// +// if ((checkMethodState->mClosureState != NULL) && (checkMethodState->mClosureState->mActiveDeferredLocalMethod != NULL)) +// { +// for (auto& mixinRecord : checkMethodState->mClosureState->mActiveDeferredLocalMethod->mMixinStateRecords) +// { +// if (mixinRecord.mSource == targetSrc) +// hasCircularRef = true; +// } +// } +// +// checkMethodState = checkMethodState->mPrevMethodState; +// } +// +// if (hasCircularRef) +// { +// mModule->Fail("Circular reference detected between mixins", targetSrc); +// return; +// } +// } auto moduleMethodInstance = GetSelectedMethod(targetSrc, methodMatcher.mBestMethodTypeInstance, methodMatcher.mBestMethodDef, methodMatcher); if (!moduleMethodInstance) @@ -15709,25 +15996,31 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } if (auto blockBody = BfNodeDynCast(methodDef->mBody)) + { mModule->VisitCodeBlock(blockBody); - if (mixinState->mResultExpr != NULL) - { - if (auto exprNode = BfNodeDynCast(mixinState->mResultExpr)) + if (mixinState->mResultExpr != NULL) { - if (!exprNode->IsA()) + if (auto exprNode = BfNodeDynCast(mixinState->mResultExpr)) { - // Mixin expression result - mModule->UpdateSrcPos(exprNode); - VisitChild(exprNode); - FinishExpressionResult(); - ResolveGenericType(); - //mResult = mModule->LoadValue(mResult); + if (!exprNode->IsA()) + { + // Mixin expression result + mModule->UpdateSrcPos(exprNode); + VisitChild(exprNode); + FinishExpressionResult(); + ResolveGenericType(); + } } } - } - GetResult(); + GetResult(); + } + else if (auto expr = BfNodeDynCast(methodDef->mBody)) + { + mModule->UpdateSrcPos(expr); + mResult = mModule->CreateValueFromExpression(expr); + } if (!mResult) { @@ -15756,8 +16049,15 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } if (auto blockBody = BfNodeDynCast(methodDef->mBody)) + { if (blockBody->mCloseBrace != NULL) mModule->UpdateSrcPos(blockBody->mCloseBrace); + } + else if (auto methodDeclaration = BfNodeDynCast(methodDef->mMethodDeclaration)) + { + if (methodDeclaration->mFatArrowToken != NULL) + mModule->UpdateSrcPos(methodDeclaration->mFatArrowToken); + } mModule->RestoreScopeState(); @@ -15972,6 +16272,10 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m // Silently allow gaveUnqualifiedDotError = true; } + else if (expectingType->IsPrimitiveType()) + { + // Allow + } else { gaveUnqualifiedDotError = true; @@ -16201,14 +16505,14 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m BfType* type; { //SetAndRestoreValue prevIgnoreErrors(mModule->mIgnoreErrors, true); - type = mModule->ResolveTypeRef(qualifiedName->mLeft, NULL, BfPopulateType_DataAndMethods, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_IgnoreLookupError)); + type = mModule->ResolveTypeRef(qualifiedName->mLeft, NULL, BfPopulateType_DataAndMethods, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowGlobalContainer | BfResolveTypeRefFlag_IgnoreLookupError)); } if (type == NULL) { //SetAndRestoreValue prevIgnoreErrors(mModule->mIgnoreErrors, true); - type = mModule->ResolveTypeRef(qualifiedName, methodGenericArguments, BfPopulateType_DataAndMethods, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_IgnoreLookupError)); + type = mModule->ResolveTypeRef(qualifiedName, methodGenericArguments, BfPopulateType_DataAndMethods, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowGlobalContainer | BfResolveTypeRefFlag_IgnoreLookupError)); if (type != NULL) { // This is a CTOR call, treat it as such @@ -16307,7 +16611,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m if (!value) return; auto typeInst = value.mType->ToTypeInstance(); - if ((typeInst != NULL) && (typeInst->mTypeDef == mModule->mCompiler->mStringTypeDef)) + if ((typeInst != NULL) && (typeInst->IsInstanceOf(mModule->mCompiler->mStringTypeDef))) value = mModule->Cast(arg, value, charPtrType); if ((value.mType->IsFloat()) && (value.mType->mSize != 8)) // Always cast float to double value = mModule->Cast(arg, value, mModule->GetPrimitiveType(BfTypeCode_Double)); @@ -16863,7 +17167,7 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp if (mPropTarget.mType->IsGenericTypeInstance()) { auto genericTypeInst = (BfTypeInstance*)mPropTarget.mType; - if (genericTypeInst->mTypeDef == mModule->mCompiler->mSizedArrayTypeDef) + if (genericTypeInst->IsInstanceOf(mModule->mCompiler->mSizedArrayTypeDef)) { if (mPropDef->mName == "Count") { @@ -16927,7 +17231,7 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp mResult = mModule->GetDefaultTypedValue(methodInstance.mMethodInstance->mReturnType); } else - { + { SizedArray args; if (!matchedMethod->mIsStatic) { @@ -16952,63 +17256,11 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp } if ((mPropGetMethodFlags & BfGetMethodInstanceFlag_DisableObjectAccessChecks) == 0) - mModule->EmitObjectAccessCheck(mPropTarget); - PushThis(mPropSrc, mPropTarget, methodInstance.mMethodInstance, args); - } - - bool failed = false; - for (int paramIdx = 0; paramIdx < (int)mIndexerValues.size(); paramIdx++) - { - auto refNode = mIndexerValues[paramIdx].mExpression; - auto wantType = methodInstance.mMethodInstance->GetParamType(paramIdx); - auto argValue = ResolveArgValue(mIndexerValues[paramIdx], wantType); - if (refNode == NULL) - refNode = mPropSrc; - BfTypedValue val; - if (argValue) - val = mModule->Cast(refNode, argValue, wantType); - if (!val) - failed = true; - else - PushArg(val, args); - } - - if (mPropDefBypassVirtual) - { - auto methodDef = methodInstance.mMethodInstance->mMethodDef; - if ((methodDef->mIsAbstract) && (mPropDefBypassVirtual)) - { - mModule->Fail(StrFormat("Abstract base property method '%s' cannot be invoked", mModule->MethodToString(methodInstance.mMethodInstance).c_str()), mPropSrc); - } - } - - if (failed) - { - auto returnType = methodInstance.mMethodInstance->mReturnType; - auto methodDef = methodInstance.mMethodInstance->mMethodDef; - if (returnType->IsRef()) - { - auto result = mModule->GetDefaultTypedValue(returnType->GetUnderlyingType(), true, BfDefaultValueKind_Addr); - if (methodDef->mIsReadOnly) - result.mKind = BfTypedValueKind_ReadOnlyAddr; - return result; - } - else - { - auto val = mModule->GetDefaultTypedValue(returnType, true, (GetStructRetIdx(methodInstance.mMethodInstance) != -1) ? BfDefaultValueKind_Addr : BfDefaultValueKind_Value); - if (val.mKind == BfTypedValueKind_Addr) - val.mKind = BfTypedValueKind_TempAddr; - return val; - } - } - else - mResult = CreateCall(mPropSrc, methodInstance.mMethodInstance, methodInstance.mFunc, mPropDefBypassVirtual, args); - if (mResult.mType != NULL) - { - if ((mResult.mType->IsVar()) && (mModule->mCompiler->mIsResolveOnly)) - mModule->Fail("Property type reference failed to resolve", mPropSrc); - BF_ASSERT(!mResult.mType->IsRef()); - } + mModule->EmitObjectAccessCheck(mPropTarget); + } + + auto callFlags = mPropDefBypassVirtual ? BfCreateCallFlags_BypassVirtual : BfCreateCallFlags_None; + mResult = CreateCall(mPropSrc, mPropTarget, mOrigPropTarget, matchedMethod, methodInstance, callFlags, mIndexerValues, NULL); } } @@ -17696,6 +17948,7 @@ void BfExprEvaluator::AssignDeferrredTupleAssignData(BfAssignmentExpression* ass BfTypedValue elementValue; if (fieldInstance->mDataIdx >= 0) { + rightValue = mModule->LoadOrAggregateValue(rightValue); auto extractedValue = mModule->mBfIRBuilder->CreateExtractValue(rightValue.mValue, fieldInstance->mDataIdx); elementValue = BfTypedValue(extractedValue, fieldInstance->GetResolvedType()); if (child.mInnerTuple != NULL) @@ -17872,7 +18125,7 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool } else { - auto wantType = methodInstance.mMethodInstance->GetParamType(methodInstance.mMethodInstance->GetParamCount() - 1); + auto wantType = methodInstance.mMethodInstance->GetParamType(0); if (rightValue) { convVal = mModule->Cast(assignExpr->mRight, rightValue, wantType); @@ -17884,7 +18137,10 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool mModule->AssertErrorState(); return; } - convVal = mModule->CreateValueFromExpression(assignExpr->mRight, wantType, (BfEvalExprFlags)(BfEvalExprFlags_AllowSplat | BfEvalExprFlags_PendingPropSet)); + BfEvalExprFlags exprFlags = (BfEvalExprFlags)(BfEvalExprFlags_AllowSplat | BfEvalExprFlags_PendingPropSet); + if (wantType->IsRef()) + exprFlags = (BfEvalExprFlags)(exprFlags | BfEvalExprFlags_AllowRefExpr); + convVal = mModule->CreateValueFromExpression(assignExpr->mRight, wantType, exprFlags); } if (!convVal) { @@ -17896,7 +18152,10 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool if (mPropSrc != NULL) mModule->UpdateExprSrcPos(mPropSrc); - SizedArray args; + BfResolvedArg valueArg; + valueArg.mTypedValue = convVal; + mIndexerValues.Insert(0, valueArg); + if (!setMethod->mIsStatic) { auto owner = methodInstance.mMethodInstance->GetOwner(); @@ -17913,34 +18172,13 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool } } } - - mModule->EmitObjectAccessCheck(mPropTarget); - PushThis(mPropSrc, mPropTarget, methodInstance.mMethodInstance, args); } - - for (int paramIdx = 0; paramIdx < (int)mIndexerValues.size(); paramIdx++) - { - auto refNode = mIndexerValues[paramIdx].mExpression; - auto wantType = methodInstance.mMethodInstance->GetParamType(paramIdx); - auto argValue = ResolveArgValue(mIndexerValues[paramIdx], wantType); - if (refNode == NULL) - refNode = mPropSrc; - auto val = mModule->Cast(refNode, argValue, wantType); - if (!val) - { - mPropDef = NULL; - return; - } - PushArg(val, args); - } - - PushArg(convVal, args); - if (methodInstance) - CreateCall(assignExpr, methodInstance.mMethodInstance, methodInstance.mFunc, mPropDefBypassVirtual, args); - + auto callFlags = mPropDefBypassVirtual ? BfCreateCallFlags_BypassVirtual : BfCreateCallFlags_None; + mResult = CreateCall(mPropSrc, mPropTarget, mOrigPropTarget, setMethod, methodInstance, callFlags, mIndexerValues, NULL); mPropDef = NULL; mResult = convVal; + mIndexerValues.Clear(); return; } } @@ -18145,6 +18383,7 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool if (ptr.mType->IsVar()) { mResult = ptr; + MarkResultAssigned(); return; } if (convVal.mValue) @@ -18863,8 +19102,27 @@ BfTypedValue BfExprEvaluator::SetupNullConditional(BfTypedValue thisValue, BfTok auto opResult = PerformUnaryOperation_TryOperator(thisValue, NULL, BfUnaryOp_NullConditional, dotToken, BfUnaryOpFlag_None); if (opResult) thisValue = opResult; + + if (thisValue.mType->IsGenericParam()) + { + bool isValid = false; + + auto genericParams = mModule->GetGenericParamInstance((BfGenericParamType*)thisValue.mType); + if (genericParams->mTypeConstraint != NULL) + { + if ((genericParams->mTypeConstraint->IsNullable()) || + (genericParams->mTypeConstraint->IsPointer()) || + (genericParams->mTypeConstraint->IsObjectOrInterface())) + isValid = true; + } + + if ((genericParams->mGenericParamFlags & (BfGenericParamFlag_Var | BfGenericParamFlag_StructPtr | BfGenericParamFlag_Class)) != 0) + isValid = true; + + if (isValid) + return thisValue; + } - //TODO: But make null conditional work for Nullable types if (thisValue.mType->IsNullable()) { // Success @@ -19179,7 +19437,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) { /// { - SetAndRestoreValue prevFlags(mBfEvalExprFlags, (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_NoLookupError), pass == 0); + SetAndRestoreValue prevFlags(mBfEvalExprFlags, (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_NoLookupError | BfEvalExprFlags_AllowBase), pass == 0); VisitChild(indexerExpr->mTarget); } ResolveGenericType(); @@ -19242,7 +19500,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) SizedArray argExprs; BfSizedArray sizedArgExprs(indexerExpr->mArguments); BfResolvedArgs argValues(&sizedArgExprs); - ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); + ResolveArgValues(argValues, (BfResolveArgsFlags)(BfResolveArgsFlag_DeferParamEval | BfResolveArgsFlag_FromIndexer)); //exprEvaluator.MatchMethod(elementExpr, NULL, initValue, false, false, "Add", argValues, NULL); mIndexerValues = argValues.mResolvedArgs; @@ -19337,36 +19595,28 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) if (foundProp != NULL) { - int indexDiff = matchedIndexCount - (int)mIndexerValues.size(); - if (indexDiff > 0) + + mPropSrc = indexerExpr->mOpenBracket; + mPropDef = foundProp; + if (foundProp->mIsStatic) { - mModule->Fail(StrFormat("Expected %d more indices", indexDiff), indexerExpr->mTarget); - //mModule->mCompiler->mPassInstance->MoreInfo("See method declaration", methodInstance->mMethodDef->mMethodDeclaration); - } - else if (indexDiff < 0) - { - mModule->Fail(StrFormat("Expected %d fewer indices", -indexDiff), indexerExpr->mTarget); + mPropTarget = BfTypedValue(curCheckType); } else - { - mPropSrc = indexerExpr->mOpenBracket; - mPropDef = foundProp; - if (foundProp->mIsStatic) - { - mPropTarget = BfTypedValue(curCheckType); - } + { + if (target.mType != foundPropTypeInst) + mPropTarget = mModule->Cast(indexerExpr->mTarget, target, foundPropTypeInst); else - { - if (target.mType != foundPropTypeInst) - mPropTarget = mModule->Cast(indexerExpr->mTarget, target, foundPropTypeInst); - else - mPropTarget = target; - } - mOrigPropTarget = mPropTarget; - if (isInlined) - mPropGetMethodFlags = (BfGetMethodInstanceFlags)(mPropGetMethodFlags | BfGetMethodInstanceFlag_ForceInline); - mPropCheckedKind = checkedKind; + mPropTarget = target; } + mOrigPropTarget = mPropTarget; + if (isInlined) + mPropGetMethodFlags = (BfGetMethodInstanceFlags)(mPropGetMethodFlags | BfGetMethodInstanceFlag_ForceInline); + mPropCheckedKind = checkedKind; + + if ((target.IsBase()) && (mPropDef->IsVirtual())) + mPropDefBypassVirtual = true; + return; } @@ -19416,7 +19666,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) bool isUndefIndex = false; - auto indexArgument = mModule->CreateValueFromExpression(indexerExpr->mArguments[0]); + auto indexArgument = mModule->CreateValueFromExpression(indexerExpr->mArguments[0], mModule->GetPrimitiveType(BfTypeCode_IntPtr), BfEvalExprFlags_NoCast); if (!indexArgument) return; if (!indexArgument.mType->IsIntegral()) @@ -19604,6 +19854,13 @@ void BfExprEvaluator::Visit(BfUnaryOperatorExpression* unaryOpExpr) void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags) { + if ((unaryOpExpr == NULL) && (unaryOp == BfUnaryOp_PartialRangeThrough)) + { + PerformBinaryOperation(NULL, NULL, BfBinaryOp_ClosedRange, opToken, BfBinOpFlag_None); + return; + } + + /// { // If this is a cast, we don't want the value to be coerced before the unary operator is applied. // WAIT: Why not? @@ -20233,7 +20490,7 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, { auto isValid = false; auto genericTypeInst = mResult.mType->ToGenericTypeInstance(); - if ((genericTypeInst != NULL) && (genericTypeInst->mTypeDef == mModule->mCompiler->mSpanTypeDef)) + if ((genericTypeInst != NULL) && (genericTypeInst->IsInstanceOf(mModule->mCompiler->mSpanTypeDef))) isValid = true; else if (mResult.mType->IsArray()) isValid = true; @@ -20263,8 +20520,32 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, mModule->Fail("Illegal use of argument cascade expression", opToken); } break; + case BfUnaryOp_FromEnd: + { + CheckResultForReading(mResult); + auto value = mModule->Cast(unaryOpExpr, mResult, mModule->GetPrimitiveType(BfTypeCode_IntPtr)); + value = mModule->LoadValue(value); + if (value) + { + auto indexType = mModule->ResolveTypeDef(mModule->mCompiler->mIndexTypeDef); + auto alloca = mModule->CreateAlloca(indexType); + mModule->mBfIRBuilder->CreateStore(value.mValue, mModule->mBfIRBuilder->CreateInBoundsGEP(mModule->mBfIRBuilder->CreateInBoundsGEP(alloca, 0, 1), 0, 1)); + mModule->mBfIRBuilder->CreateStore(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int8, 1), mModule->mBfIRBuilder->CreateInBoundsGEP(alloca, 0, 2)); + mResult = BfTypedValue(alloca, indexType, BfTypedValueKind_Addr); + } + } + break; + case BfUnaryOp_PartialRangeUpTo: + PerformBinaryOperation(NULL, unaryOpExpr, BfBinaryOp_Range, opToken, BfBinOpFlag_None); + break; + case BfUnaryOp_PartialRangeThrough: + PerformBinaryOperation(NULL, unaryOpExpr, BfBinaryOp_ClosedRange, opToken, BfBinOpFlag_None); + break; + case BfUnaryOp_PartialRangeFrom: + PerformBinaryOperation(unaryOpExpr, NULL, BfBinaryOp_ClosedRange, opToken, BfBinOpFlag_None); + break; default: - mModule->Fail("INTERNAL ERROR: Unhandled unary operator", unaryOpExpr); + mModule->Fail(StrFormat("Illegal use of '%s' unary operator", BfGetOpName(unaryOp)), unaryOpExpr); break; } @@ -20339,7 +20620,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp } if (leftValue.mType->IsRef()) leftValue.mType = leftValue.mType->GetUnderlyingType(); - + if ((binaryOp == BfBinaryOp_ConditionalAnd) || (binaryOp == BfBinaryOp_ConditionalOr)) { if (mModule->mCurMethodState->mDeferredLocalAssignData != NULL) @@ -20502,10 +20783,14 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, { if ((leftValue) && ((leftValue.mType->IsPointer()) || (leftValue.mType->IsFunction()) || (leftValue.mType->IsObject()))) { - auto prevBB = mModule->mBfIRBuilder->GetInsertBlock(); + leftValue = mModule->LoadValue(leftValue); + auto prevBB = mModule->mBfIRBuilder->GetInsertBlock(); auto rhsBB = mModule->mBfIRBuilder->CreateBlock("nullc.rhs"); auto endBB = mModule->mBfIRBuilder->CreateBlock("nullc.end"); + auto lhsBB = endBB; + + auto endLhsBB = prevBB; BfIRValue isNull; if (leftValue.mType->IsFunction()) @@ -20513,21 +20798,23 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, mModule->mBfIRBuilder->CreateIntToPtr(leftValue.mValue, mModule->mBfIRBuilder->MapType(mModule->GetPrimitiveType(BfTypeCode_NullPtr)))); else isNull = mModule->mBfIRBuilder->CreateIsNull(leftValue.mValue); - mModule->mBfIRBuilder->CreateCondBr(isNull, rhsBB, endBB); - + mModule->AddBasicBlock(rhsBB); BfTypedValue rightValue; if (assignTo != NULL) rightValue = mModule->CreateValueFromExpression(rightExpression, wantType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags))); else rightValue = mModule->CreateValueFromExpression(rightExpression, wantType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags) | BfEvalExprFlags_NoCast)); + if (!rightValue) { mModule->AssertErrorState(); return true; } - else if (assignTo == NULL) - { + rightValue = mModule->LoadValue(rightValue); + + if (assignTo == NULL) + { auto rightToLeftValue = mModule->CastToValue(rightExpression, rightValue, leftValue.mType, BfCastFlags_SilentFail); if (rightToLeftValue) { @@ -20535,6 +20822,9 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, } else { + lhsBB = mModule->mBfIRBuilder->CreateBlock("nullc.lhs", true); + mModule->mBfIRBuilder->SetInsertPoint(lhsBB); + auto leftToRightValue = mModule->CastToValue(leftExpression, leftValue, rightValue.mType, BfCastFlags_SilentFail); if (leftToRightValue) { @@ -20547,6 +20837,10 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, mModule->TypeToString(leftValue.mType).c_str(), mModule->TypeToString(rightValue.mType).c_str()), opToken); leftValue = mModule->GetDefaultTypedValue(rightValue.mType); } + + mModule->mBfIRBuilder->CreateBr(endBB); + endLhsBB = mModule->mBfIRBuilder->GetInsertBlock(); + mModule->mBfIRBuilder->SetInsertPoint(rhsBB); } } @@ -20554,18 +20848,22 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, mModule->mBfIRBuilder->CreateStore(rightValue.mValue, assignTo->mValue); mModule->mBfIRBuilder->CreateBr(endBB); - auto endRhsBB = mModule->mBfIRBuilder->GetInsertBlock(); + + // Actually add CondBr at start + mModule->mBfIRBuilder->SetInsertPoint(prevBB); + mModule->mBfIRBuilder->CreateCondBr(isNull, rhsBB, lhsBB); + mModule->AddBasicBlock(endBB); if (assignTo != NULL) - { + { mResult = *assignTo; } else { auto phi = mModule->mBfIRBuilder->CreatePhi(mModule->mBfIRBuilder->MapType(leftValue.mType), 2); - mModule->mBfIRBuilder->AddPhiIncoming(phi, leftValue.mValue, prevBB); + mModule->mBfIRBuilder->AddPhiIncoming(phi, leftValue.mValue, endLhsBB); mModule->mBfIRBuilder->AddPhiIncoming(phi, rightValue.mValue, endRhsBB); mResult = BfTypedValue(phi, leftValue.mType); } @@ -20578,12 +20876,100 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExpression* rightExpression, BfBinaryOp binaryOp, BfTokenNode* opToken, BfBinOpFlags flags) { + if ((binaryOp == BfBinaryOp_Range) || (binaryOp == BfBinaryOp_ClosedRange)) + { + auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); + + bool isIndexExpr = false; + BfTypeDef* typeDef = NULL; + if (auto unaryOpExpr = BfNodeDynCast(leftExpression)) + if (unaryOpExpr->mOp == BfUnaryOp_FromEnd) + isIndexExpr = true; + if (rightExpression == NULL) + isIndexExpr = true; + if (auto unaryOpExpr = BfNodeDynCast(rightExpression)) + if (unaryOpExpr->mOp == BfUnaryOp_FromEnd) + isIndexExpr = true; + + if (isIndexExpr) + typeDef = mModule->mCompiler->mIndexRangeTypeDef; + else + typeDef = (binaryOp == BfBinaryOp_Range) ? mModule->mCompiler->mRangeTypeDef : mModule->mCompiler->mClosedRangeTypeDef; + + auto allocType = mModule->ResolveTypeDef(typeDef)->ToTypeInstance(); + auto alloca = mModule->CreateAlloca(allocType); + + BfTypedValueExpression leftTypedValueExpr; + BfTypedValueExpression rightTypedValueExpr; + BfTypedValueExpression isClosedTypedValueExpr; + + SizedArray argExprs; + if (leftExpression != NULL) + { + argExprs.Add(leftExpression); + } + else + { + leftTypedValueExpr.mRefNode = opToken; + leftTypedValueExpr.mTypedValue = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0), mModule->GetPrimitiveType(BfTypeCode_IntPtr)); + argExprs.Add(&leftTypedValueExpr); + } + + if (rightExpression != NULL) + { + argExprs.Add(rightExpression); + } + else + { + // Add as a `^1` + auto indexType = mModule->ResolveTypeDef(mModule->mCompiler->mIndexTypeDef)->ToTypeInstance(); + rightTypedValueExpr.mRefNode = opToken; + + auto valueTypeEmpty = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType->mBaseType), {}); + SizedArray enumMembers; + enumMembers.Add(valueTypeEmpty); + auto enumValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType), enumMembers); + + SizedArray tupleMembers; + tupleMembers.Add(valueTypeEmpty); + tupleMembers.Add(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1)); + auto tupleValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mFieldInstances[0].mResolvedType), tupleMembers); + + SizedArray indexMembers; + indexMembers.Add(enumValue); + indexMembers.Add(tupleValue); + indexMembers.Add(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int8, 1)); + auto indexValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType), indexMembers); + + rightTypedValueExpr.mTypedValue = BfTypedValue(indexValue, indexType); + argExprs.Add(&rightTypedValueExpr); + } + + if (isIndexExpr) + { + isClosedTypedValueExpr.mRefNode = opToken; + isClosedTypedValueExpr.mTypedValue = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Boolean, (binaryOp == BfBinaryOp_ClosedRange) ? 1 : 0), mModule->GetPrimitiveType(BfTypeCode_Boolean)); + argExprs.Add(&isClosedTypedValueExpr); + } + + BfSizedArray args = argExprs; + + BfResolvedArgs argValues; + argValues.Init(&args); + ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); + + mResult = BfTypedValue(alloca, allocType, true); + MatchConstructor(opToken, NULL, mResult, allocType, argValues, true, false); + + return; + } + BfTypedValue leftValue; if (leftExpression != NULL) { leftValue = mModule->CreateValueFromExpression(leftExpression, mExpectingType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags) | BfEvalExprFlags_NoCast | BfEvalExprFlags_AllowIntUnknown)); } - return PerformBinaryOperation(leftExpression, rightExpression, binaryOp, opToken, flags, leftValue); + PerformBinaryOperation(leftExpression, rightExpression, binaryOp, opToken, flags, leftValue); } bool BfExprEvaluator::CheckConstCompare(BfBinaryOp binaryOp, BfAstNode* opToken, const BfTypedValue& leftValue, const BfTypedValue& rightValue) @@ -21042,19 +21428,6 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod return; } } - - // Valueless types always compare as 'equal' - if (leftValue.mType == rightValue.mType) - { - mModule->PopulateType(leftValue.mType); - if (leftValue.mType->IsValuelessType()) - { - auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean); - bool isEqual = (binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality); - mResult = BfTypedValue(mModule->GetConstValue(isEqual ? 1 : 0, boolType), boolType); - return; - } - } } if ((leftValue.mType->IsTypeInstance()) || (leftValue.mType->IsGenericParam()) || @@ -21093,11 +21466,20 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod BfBinaryOp findBinaryOp = binaryOp; bool isComparison = (binaryOp >= BfBinaryOp_Equality) && (binaryOp <= BfBinaryOp_LessThanOrEqual); - + for (int pass = 0; pass < 2; pass++) { BfBinaryOp oppositeBinaryOp = BfGetOppositeBinaryOp(findBinaryOp); - bool foundOp = false; + BfBinaryOp overflowBinaryOp = BfBinaryOp_None; + + if (findBinaryOp == BfBinaryOp_OverflowAdd) + overflowBinaryOp = BfBinaryOp_Add; + else if (findBinaryOp == BfBinaryOp_OverflowSubtract) + overflowBinaryOp = BfBinaryOp_Subtract; + else if (findBinaryOp == BfBinaryOp_OverflowMultiply) + overflowBinaryOp = BfBinaryOp_Multiply; + + bool foundOp = false; BfResolvedArg leftArg; leftArg.mExpression = leftExpression; @@ -21141,7 +21523,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod bool invertResult = false; BfType* operatorConstraintReturnType = NULL; - bool wasTransformedUsage = pass == 1; + bool wasTransformedUsage = (pass == 1); while (true) { @@ -21204,7 +21586,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } } } - else if (operatorDef->mOperatorDeclaration->mBinOp == oppositeBinaryOp) + else if ((operatorDef->mOperatorDeclaration->mBinOp == oppositeBinaryOp) || (operatorDef->mOperatorDeclaration->mBinOp == overflowBinaryOp)) oppositeOperatorDefs.Add(operatorDef); } @@ -21225,7 +21607,8 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod methodMatcher.mBestMethodDef = oppositeOperatorDef; methodMatcher.mBestMethodTypeInstance = checkType; methodMatcher.mSelfType = entry.mSrcType; - wasTransformedUsage = true; + if (oppositeBinaryOp != BfBinaryOp_None) + wasTransformedUsage = true; } } else @@ -21238,7 +21621,8 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod { operatorConstraintReturnType = returnType; methodMatcher.mSelfType = entry.mSrcType; - wasTransformedUsage = true; + if (oppositeBinaryOp != BfBinaryOp_None) + wasTransformedUsage = true; } } } @@ -21248,7 +21632,8 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod if (methodMatcher.CheckMethod(NULL, checkType, oppositeOperatorDef, false)) { methodMatcher.mSelfType = entry.mSrcType; - wasTransformedUsage = true; + if (oppositeBinaryOp != BfBinaryOp_None) + wasTransformedUsage = true; } } } @@ -21442,12 +21827,12 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod if (pass == 1) break; - - auto flippedBinaryOp = BfGetFlippedBinaryOp(findBinaryOp); + + auto flippedBinaryOp = BfGetFlippedBinaryOp(findBinaryOp); if (flippedBinaryOp != BfBinaryOp_None) - findBinaryOp = flippedBinaryOp; + findBinaryOp = flippedBinaryOp; } - + auto prevResultType = resultType; if ((leftValue.mType->IsPrimitiveType()) && (!rightValue.mType->IsTypedPrimitive())) resultType = leftValue.mType; @@ -21483,7 +21868,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } //TODO: Allow all pointer comparisons, but only allow SUBTRACTION between equal pointer types - if (binaryOp == BfBinaryOp_Subtract) + if ((binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowSubtract)) { if (!mModule->CanCast(*otherTypedValue, resultType)) { @@ -21538,7 +21923,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod // One pointer if ((!otherType->IsIntegral()) || - ((binaryOp != BfBinaryOp_Add) && (binaryOp != BfBinaryOp_Subtract))) + ((binaryOp != BfBinaryOp_Add) && (binaryOp != BfBinaryOp_Subtract) && (binaryOp != BfBinaryOp_OverflowAdd) && (binaryOp != BfBinaryOp_OverflowSubtract))) { _OpFail(); return; @@ -21548,7 +21933,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod BfIRValue addValue = otherTypedValue->mValue; if ((!otherTypedValue->mType->IsSigned()) && (otherTypedValue->mType->mSize < mModule->mSystem->mPtrSize)) addValue = mModule->mBfIRBuilder->CreateNumericCast(addValue, false, BfTypeCode_UIntPtr); - if (binaryOp == BfBinaryOp_Subtract) + if ((binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowSubtract)) { if (resultTypeSrc == rightExpression) mModule->Fail("Cannot subtract a pointer from an integer", resultTypeSrc); @@ -21647,7 +22032,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } // Allow integer offsetting - if ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract)) + if ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowAdd) || (binaryOp == BfBinaryOp_OverflowSubtract)) { if (otherType->IsIntegral()) needsOtherCast = false; @@ -21712,7 +22097,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod argValues.push_back(resolvedArg); resolvedArg.mTypedValue = rightValue; argValues.push_back(resolvedArg); - mResult = CreateCall(opToken, BfTypedValue(), BfTypedValue(), moduleMethodInstance.mMethodInstance->mMethodDef, moduleMethodInstance, BfCreateFallFlags_None, argValues); + mResult = CreateCall(opToken, BfTypedValue(), BfTypedValue(), moduleMethodInstance.mMethodInstance->mMethodDef, moduleMethodInstance, BfCreateCallFlags_None, argValues); if ((mResult) && ((binaryOp == BfBinaryOp_InEquality) || (binaryOp == BfBinaryOp_StrictInEquality))) mResult.mValue = mModule->mBfIRBuilder->CreateNot(mResult.mValue); @@ -21758,6 +22143,47 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod return; } + // Valueless types always compare as 'equal' if we can ensure no members could have an equality operator overload + if (leftValue.mType->IsComposite()) + { + mModule->PopulateType(leftValue.mType); + if (leftValue.mType->IsValuelessType()) + { + bool mayHaveEqualOverload = false; + auto leftTypeInst = leftValue.mType->ToTypeInstance(); + if (leftTypeInst != NULL) + { + std::function _HasTypeInstance = [&](BfType* type) + { + if (type == NULL) + return false; + + if (type->IsTypeInstance()) + return true; + + if (type->IsSizedArray()) + return _HasTypeInstance(((BfSizedArrayType*)type)->mElementType); + + return false; + }; + + for (auto& fieldInstance : leftTypeInst->mFieldInstances) + { + if (_HasTypeInstance(fieldInstance.mResolvedType)) + mayHaveEqualOverload = true; + } + } + + if (!mayHaveEqualOverload) + { + auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean); + bool isEqual = (binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality); + mResult = BfTypedValue(mModule->GetConstValue(isEqual ? 1 : 0, boolType), boolType); + return; + } + } + } + if (_CallValueTypeEquals()) return; } @@ -21870,7 +22296,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod if (rightValue.mType->IsIntegral()) explicitCast = true; } - else if (((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract)) && (resultType->IsChar()) && (otherType->IsInteger())) + else if (((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowAdd) || (binaryOp == BfBinaryOp_OverflowSubtract)) && (resultType->IsChar()) && (otherType->IsInteger())) { // charVal += intVal; explicitCast = true; @@ -21896,14 +22322,15 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } else { - if ((binaryOp == BfBinaryOp_Subtract) && (resultType->IsChar()) && (otherType->IsChar())) + if (((binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowSubtract)) && + (resultType->IsChar()) && (otherType->IsChar())) { // "wchar - char" subtraction will always fit into int32, because of unicode range resultType = mModule->GetPrimitiveType(BfTypeCode_Int32); explicitCast = true; } else if ((otherType->IsChar()) && - ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract))) + ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowAdd) || (binaryOp == BfBinaryOp_OverflowSubtract))) { mModule->Fail(StrFormat("Cannot perform operation between types '%s' and '%s'", mModule->TypeToString(leftValue.mType).c_str(), @@ -22058,6 +22485,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfType* resultType, BfIRValue convL if ((resultType->IsChar()) && ((binaryOp == BfBinaryOp_Multiply) || + (binaryOp == BfBinaryOp_OverflowMultiply) || (binaryOp == BfBinaryOp_Divide) || (binaryOp == BfBinaryOp_Modulus))) { @@ -22067,15 +22495,18 @@ void BfExprEvaluator::PerformBinaryOperation(BfType* resultType, BfIRValue convL switch (binaryOp) { - case BfBinaryOp_Add: + case BfBinaryOp_Add: + case BfBinaryOp_OverflowAdd: mResult = BfTypedValue(mModule->mBfIRBuilder->CreateAdd(convLeftValue, convRightValue), resultType); mModule->CheckRangeError(resultType, opToken); break; - case BfBinaryOp_Subtract: + case BfBinaryOp_Subtract: + case BfBinaryOp_OverflowSubtract: mResult = BfTypedValue(mModule->mBfIRBuilder->CreateSub(convLeftValue, convRightValue), resultType); mModule->CheckRangeError(resultType, opToken); break; - case BfBinaryOp_Multiply: + case BfBinaryOp_Multiply: + case BfBinaryOp_OverflowMultiply: mResult = BfTypedValue(mModule->mBfIRBuilder->CreateMul(convLeftValue, convRightValue), resultType); mModule->CheckRangeError(resultType, opToken); break; diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 6eb98707..e45afd29 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -31,7 +31,8 @@ enum BfResolveArgsFlags BfResolveArgsFlag_DeferParamValues = 2, // We still evaluate but don't generate code until the method is selected (for SkipCall support) BfResolveArgsFlag_DeferParamEval = 4, BfResolveArgsFlag_AllowUnresolvedTypes = 8, - BfResolveArgsFlag_InsideStringInterpolationAlloc = 0x10 + BfResolveArgsFlag_InsideStringInterpolationAlloc = 0x10, + BfResolveArgsFlag_FromIndexer = 0x20 }; enum BfResolveArgFlags @@ -41,12 +42,14 @@ enum BfResolveArgFlags BfResolveArgFlag_FromGenericParam = 2 }; -enum BfCreateFallFlags +enum BfCreateCallFlags { - BfCreateFallFlags_None, - BfCreateFallFlags_BypassVirtual = 1, - BfCreateFallFlags_SkipThis = 2, - BfCreateFallFlags_AllowImplicitRef = 4 + BfCreateCallFlags_None, + BfCreateCallFlags_BypassVirtual = 1, + BfCreateCallFlags_SkipThis = 2, + BfCreateCallFlags_AllowImplicitRef = 4, + BfCreateCallFlags_TailCall = 8, + BfCreateCallFlags_GenericParamThis = 0x10 }; class BfResolvedArg @@ -137,13 +140,13 @@ public: BfModule* mModule; BfTypeVector* mCheckMethodGenericArguments; SizedArray mPrevArgValues; - int mInferredCount; + int mInferredCount; public: BfGenericInferContext() { mModule = NULL; - mInferredCount = 0; + mInferredCount = 0; } bool InferGenericArgument(BfMethodInstance* methodInstance, BfType* argType, BfType* wantType, BfIRValue argValue); int GetUnresolvedCount() @@ -198,7 +201,7 @@ public: bool mAllowStatic; bool mAllowNonStatic; bool mSkipImplicitParams; - bool mAutoFlushAmbiguityErrors; + bool mAutoFlushAmbiguityErrors; BfEvalExprFlags mBfEvalExprFlags; int mMethodCheckCount; BfType* mExplicitInterfaceCheck; @@ -444,8 +447,8 @@ public: BfTypedValue LookupIdentifier(BfIdentifierNode* identifierNode, bool ignoreInitialError = false, bool* hadError = NULL); void AddCallDependencies(BfMethodInstance* methodInstance); void PerformCallChecks(BfMethodInstance* methodInstance, BfAstNode* targetSrc); - BfTypedValue CreateCall(BfAstNode* targetSrc, BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl& irArgs, BfTypedValue* sret = NULL, bool isTailCall = false); - BfTypedValue CreateCall(BfAstNode* targetSrc, const BfTypedValue& target, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance methodInstance, BfCreateFallFlags callFlags, SizedArrayImpl& argValues, BfTypedValue* argCascade = NULL); + BfTypedValue CreateCall(BfAstNode* targetSrc, BfMethodInstance* methodInstance, BfIRValue func, bool bypassVirtual, SizedArrayImpl& irArgs, BfTypedValue* sret = NULL, BfCreateCallFlags callFlags = BfCreateCallFlags_None); + BfTypedValue CreateCall(BfAstNode* targetSrc, const BfTypedValue& target, const BfTypedValue& origTarget, BfMethodDef* methodDef, BfModuleMethodInstance methodInstance, BfCreateCallFlags callFlags, SizedArrayImpl& argValues, BfTypedValue* argCascade = NULL); BfTypedValue CreateCall(BfMethodMatcher* methodMatcher, BfTypedValue target); void MakeBaseConcrete(BfTypedValue& typedValue); void SplatArgs(BfTypedValue value, SizedArrayImpl& irArgs); @@ -485,7 +488,7 @@ public: void FinishDeferredEvals(SizedArrayImpl& argValues); void FinishDeferredEvals(BfResolvedArgs& argValues); bool LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifierNode* propName); - void DoTypeIntAttr(BfTypeReference* typeRef, BfToken token); + void DoTypeIntAttr(BfTypeReference* typeRef, BfTokenNode* commaToken, BfIdentifierNode* memberName, BfToken token); //void InitializedSizedArray(BfTupleExpression* createExpr, BfSizedArrayType* arrayType); void InitializedSizedArray(BfSizedArrayType* sizedArrayType, BfTokenNode* openToken, const BfSizedArray& values, const BfSizedArray& commas, BfTokenNode* closeToken, BfTypedValue* receivingValue = NULL); void CheckDotToken(BfTokenNode* tokenNode); @@ -516,6 +519,7 @@ public: virtual void Visit(BfSizeOfExpression* sizeOfExpr) override; virtual void Visit(BfAlignOfExpression* alignOfExpr) override; virtual void Visit(BfStrideOfExpression* strideOfExpr) override; + virtual void Visit(BfOffsetOfExpression* offsetOfExpr) override; virtual void Visit(BfDefaultExpression* defaultExpr) override; virtual void Visit(BfUninitializedExpression* uninitialziedExpr) override; virtual void Visit(BfCheckTypeExpression* checkTypeExpr) override; diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index 674ea7d4..cc43725c 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -48,127 +48,141 @@ USING_NS_BF; auto constRHS = GetConstantById(rhs.mId); \ if (constLHS->mConstType == BfConstType_Undef) return lhs; \ if (constRHS->mConstType == BfConstType_Undef) return rhs; \ - BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ - uint64 val = 0; \ - switch (constLHS->mTypeCode) \ + if ((constLHS->mTypeCode < BfTypeCode_Length) && (constRHS->mTypeCode < BfTypeCode_Length)) \ { \ - case BfTypeCode_Boolean: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ - case BfTypeCode_Int8: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ - case BfTypeCode_Char8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ - case BfTypeCode_UInt8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ - case BfTypeCode_Int16: val = OP(constLHS->mInt16, constRHS->mInt16); break; \ - case BfTypeCode_UInt16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ - case BfTypeCode_Char16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ - case BfTypeCode_Int32: val = OP(constLHS->mInt32, constRHS->mInt32); break; \ - case BfTypeCode_UInt32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ - case BfTypeCode_Char32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ - case BfTypeCode_Int64: val = OP(constLHS->mInt64, constRHS->mInt64); break; \ - case BfTypeCode_UInt64: val = OP(constLHS->mUInt64, constRHS->mUInt64); break; \ - case BfTypeCode_Float: \ - case BfTypeCode_Double: \ - return CreateConst(constLHS->mTypeCode, OP(constLHS->mDouble, constRHS->mDouble)); break; \ - default: break; \ - } \ - return CreateConst(constLHS->mTypeCode, val); + BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ + uint64 val = 0; \ + switch (constLHS->mTypeCode) \ + { \ + case BfTypeCode_Boolean: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ + case BfTypeCode_Int8: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ + case BfTypeCode_Char8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ + case BfTypeCode_UInt8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ + case BfTypeCode_Int16: val = OP(constLHS->mInt16, constRHS->mInt16); break; \ + case BfTypeCode_UInt16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ + case BfTypeCode_Char16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ + case BfTypeCode_Int32: val = OP(constLHS->mInt32, constRHS->mInt32); break; \ + case BfTypeCode_UInt32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ + case BfTypeCode_Char32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ + case BfTypeCode_Int64: val = OP(constLHS->mInt64, constRHS->mInt64); break; \ + case BfTypeCode_UInt64: val = OP(constLHS->mUInt64, constRHS->mUInt64); break; \ + case BfTypeCode_Float: \ + case BfTypeCode_Double: \ + return CreateConst(constLHS->mTypeCode, OP(constLHS->mDouble, constRHS->mDouble)); break; \ + default: break; \ + } \ + return CreateConst(constLHS->mTypeCode, val); \ + } #define INT_BINOPFUNC_APPLY(lhs, rhs, OP) \ auto constLHS = GetConstantById(lhs.mId); \ auto constRHS = GetConstantById(rhs.mId); \ if (constLHS->mConstType == BfConstType_Undef) return lhs; \ if (constRHS->mConstType == BfConstType_Undef) return rhs; \ - BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ - uint64 val = 0; \ - switch (constLHS->mTypeCode) \ + if ((constLHS->mTypeCode < BfTypeCode_Length) && (constRHS->mTypeCode < BfTypeCode_Length)) \ { \ - case BfTypeCode_Boolean: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ - case BfTypeCode_Int8: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ - case BfTypeCode_Char8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ - case BfTypeCode_UInt8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ - case BfTypeCode_Int16: val = OP(constLHS->mInt16, constRHS->mInt16); break; \ - case BfTypeCode_UInt16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ - case BfTypeCode_Char16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ - case BfTypeCode_Int32: val = OP(constLHS->mInt32, constRHS->mInt32); break; \ - case BfTypeCode_UInt32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ - case BfTypeCode_Char32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ - case BfTypeCode_Int64: val = OP(constLHS->mInt64, constRHS->mInt64); break; \ - case BfTypeCode_UInt64: val = OP(constLHS->mUInt64, constRHS->mUInt64); break; \ - default: break; \ - } \ - return CreateConst(constLHS->mTypeCode, val); - + BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ + uint64 val = 0; \ + switch (constLHS->mTypeCode) \ + { \ + case BfTypeCode_Boolean: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ + case BfTypeCode_Int8: val = OP(constLHS->mInt8, constRHS->mInt8); break; \ + case BfTypeCode_Char8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ + case BfTypeCode_UInt8: val = OP(constLHS->mUInt8, constRHS->mUInt8); break; \ + case BfTypeCode_Int16: val = OP(constLHS->mInt16, constRHS->mInt16); break; \ + case BfTypeCode_UInt16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ + case BfTypeCode_Char16: val = OP(constLHS->mUInt16, constRHS->mUInt16); break; \ + case BfTypeCode_Int32: val = OP(constLHS->mInt32, constRHS->mInt32); break; \ + case BfTypeCode_UInt32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ + case BfTypeCode_Char32: val = OP(constLHS->mUInt32, constRHS->mUInt32); break; \ + case BfTypeCode_Int64: val = OP(constLHS->mInt64, constRHS->mInt64); break; \ + case BfTypeCode_UInt64: val = OP(constLHS->mUInt64, constRHS->mUInt64); break; \ + default: break; \ + } \ + return CreateConst(constLHS->mTypeCode, val); \ + } #define BINOP_APPLY(lhs, rhs, OP) \ auto constLHS = GetConstantById(lhs.mId); \ auto constRHS = GetConstantById(rhs.mId); \ if (constLHS->mConstType == BfConstType_Undef) return lhs; \ if (constRHS->mConstType == BfConstType_Undef) return rhs; \ - BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ - uint64 val = 0; \ - switch (constLHS->mTypeCode) \ + if ((constLHS->mTypeCode < BfTypeCode_Length) && (constRHS->mTypeCode < BfTypeCode_Length)) \ { \ - case BfTypeCode_Boolean: val = constLHS->mInt8 OP constRHS->mInt8; break; \ - case BfTypeCode_Int8: val = constLHS->mInt8 OP constRHS->mInt8; break; \ - case BfTypeCode_Char8: val = constLHS->mUInt8 OP constRHS->mUInt8; break; \ - case BfTypeCode_UInt8: val = constLHS->mUInt8 OP constRHS->mUInt8; break; \ - case BfTypeCode_Int16: val = constLHS->mInt16 OP constRHS->mInt16; break; \ - case BfTypeCode_UInt16: val = constLHS->mUInt16 OP constRHS->mUInt16; break; \ - case BfTypeCode_Char16: val = constLHS->mUInt16 OP constRHS->mUInt16; break; \ - case BfTypeCode_Int32: val = constLHS->mInt32 OP constRHS->mInt32; break; \ - case BfTypeCode_UInt32: val = constLHS->mUInt32 OP constRHS->mUInt32; break; \ - case BfTypeCode_Char32: val = constLHS->mUInt32 OP constRHS->mUInt32; break; \ - case BfTypeCode_Int64: val = constLHS->mInt64 OP constRHS->mInt64; break; \ - case BfTypeCode_UInt64: val = constLHS->mUInt64 OP constRHS->mUInt64; break; \ - case BfTypeCode_Float: \ - case BfTypeCode_Double: \ - return CreateConst(constLHS->mTypeCode, constLHS->mDouble OP constRHS->mDouble); break; \ - default: break; \ - } \ - return CreateConst(constLHS->mTypeCode, val); + BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ + uint64 val = 0; \ + switch (constLHS->mTypeCode) \ + { \ + case BfTypeCode_Boolean: val = constLHS->mInt8 OP constRHS->mInt8; break; \ + case BfTypeCode_Int8: val = constLHS->mInt8 OP constRHS->mInt8; break; \ + case BfTypeCode_Char8: val = constLHS->mUInt8 OP constRHS->mUInt8; break; \ + case BfTypeCode_UInt8: val = constLHS->mUInt8 OP constRHS->mUInt8; break; \ + case BfTypeCode_Int16: val = constLHS->mInt16 OP constRHS->mInt16; break; \ + case BfTypeCode_UInt16: val = constLHS->mUInt16 OP constRHS->mUInt16; break; \ + case BfTypeCode_Char16: val = constLHS->mUInt16 OP constRHS->mUInt16; break; \ + case BfTypeCode_Int32: val = constLHS->mInt32 OP constRHS->mInt32; break; \ + case BfTypeCode_UInt32: val = constLHS->mUInt32 OP constRHS->mUInt32; break; \ + case BfTypeCode_Char32: val = constLHS->mUInt32 OP constRHS->mUInt32; break; \ + case BfTypeCode_Int64: val = constLHS->mInt64 OP constRHS->mInt64; break; \ + case BfTypeCode_UInt64: val = constLHS->mUInt64 OP constRHS->mUInt64; break; \ + case BfTypeCode_Float: \ + case BfTypeCode_Double: \ + return CreateConst(constLHS->mTypeCode, constLHS->mDouble OP constRHS->mDouble); break; \ + default: break; \ + } \ + return CreateConst(constLHS->mTypeCode, val); \ + } #define INT_BINOP_APPLY(constLHS, constRHS, OP) \ if (constLHS->mConstType == BfConstType_Undef) return lhs; \ if (constRHS->mConstType == BfConstType_Undef) return rhs; \ - BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ - uint64 val = 0; \ - switch (constLHS->mTypeCode) \ + if ((constLHS->mTypeCode < BfTypeCode_Length) && (constRHS->mTypeCode < BfTypeCode_Length)) \ { \ - case BfTypeCode_Int8: return CreateConst(constLHS->mTypeCode, constLHS->mInt8 OP constRHS->mInt8); \ - case BfTypeCode_Char8: return CreateConst(constLHS->mTypeCode, constLHS->mUInt8 OP constRHS->mUInt8); \ - case BfTypeCode_UInt8: return CreateConst(constLHS->mTypeCode, constLHS->mUInt8 OP constRHS->mUInt8); \ - case BfTypeCode_Int16: return CreateConst(constLHS->mTypeCode, constLHS->mInt16 OP constRHS->mInt16); \ - case BfTypeCode_UInt16: return CreateConst(constLHS->mTypeCode, constLHS->mUInt16 OP constRHS->mUInt16); \ - case BfTypeCode_Char16: return CreateConst(constLHS->mTypeCode, constLHS->mUInt16 OP constRHS->mUInt16); \ - case BfTypeCode_Int32: return CreateConst(constLHS->mTypeCode, constLHS->mInt32 OP constRHS->mInt32); \ - case BfTypeCode_UInt32: return CreateConst(constLHS->mTypeCode, (uint64)(constLHS->mUInt32 OP constRHS->mUInt32)); \ - case BfTypeCode_Char32: return CreateConst(constLHS->mTypeCode, (uint64)(constLHS->mUInt32 OP constRHS->mUInt32)); \ - case BfTypeCode_Int64: return CreateConst(constLHS->mTypeCode, (uint64)(constLHS->mInt64 OP constRHS->mInt64)); \ - case BfTypeCode_UInt64: return CreateConst(constLHS->mTypeCode, constLHS->mUInt64 OP constRHS->mUInt64); \ - default: break; \ + BF_ASSERT(constLHS->mTypeCode == constRHS->mTypeCode); \ + uint64 val = 0; \ + switch (constLHS->mTypeCode) \ + { \ + case BfTypeCode_Int8: return CreateConst(constLHS->mTypeCode, constLHS->mInt8 OP constRHS->mInt8); \ + case BfTypeCode_Char8: return CreateConst(constLHS->mTypeCode, constLHS->mUInt8 OP constRHS->mUInt8); \ + case BfTypeCode_UInt8: return CreateConst(constLHS->mTypeCode, constLHS->mUInt8 OP constRHS->mUInt8); \ + case BfTypeCode_Int16: return CreateConst(constLHS->mTypeCode, constLHS->mInt16 OP constRHS->mInt16); \ + case BfTypeCode_UInt16: return CreateConst(constLHS->mTypeCode, constLHS->mUInt16 OP constRHS->mUInt16); \ + case BfTypeCode_Char16: return CreateConst(constLHS->mTypeCode, constLHS->mUInt16 OP constRHS->mUInt16); \ + case BfTypeCode_Int32: return CreateConst(constLHS->mTypeCode, constLHS->mInt32 OP constRHS->mInt32); \ + case BfTypeCode_UInt32: return CreateConst(constLHS->mTypeCode, (uint64)(constLHS->mUInt32 OP constRHS->mUInt32)); \ + case BfTypeCode_Char32: return CreateConst(constLHS->mTypeCode, (uint64)(constLHS->mUInt32 OP constRHS->mUInt32)); \ + case BfTypeCode_Int64: return CreateConst(constLHS->mTypeCode, (uint64)(constLHS->mInt64 OP constRHS->mInt64)); \ + case BfTypeCode_UInt64: return CreateConst(constLHS->mTypeCode, constLHS->mUInt64 OP constRHS->mUInt64); \ + default: break; \ + } \ } #define UNARYOP_APPLY(val, OP) \ auto constVal = GetConstantById(val.mId); \ if (constVal->mConstType == BfConstType_Undef) return val; \ - uint64 val = 0; \ - switch (constVal->mTypeCode) \ + if (constVal->mTypeCode < BfTypeCode_Length) \ { \ - case BfTypeCode_Int8: val = OP constVal->mInt8; break; \ - case BfTypeCode_UInt8: val = OP constVal->mUInt8; break; \ - case BfTypeCode_Char8: val = OP constVal->mUInt8; break; \ - case BfTypeCode_Int16: val = OP constVal->mInt16; break; \ - case BfTypeCode_UInt16: val = OP constVal->mUInt16; break; \ - case BfTypeCode_Char16: val = OP constVal->mUInt16; break; \ - case BfTypeCode_Int32: val = OP constVal->mInt32; break; \ - case BfTypeCode_UInt32: val = OP constVal->mUInt32; break; \ - case BfTypeCode_Char32: val = OP constVal->mUInt32; break; \ - case BfTypeCode_Int64: val = OP constVal->mInt64; break; \ - case BfTypeCode_UInt64: val = OP constVal->mUInt64; break; \ - case BfTypeCode_Float: \ - case BfTypeCode_Double: \ - return CreateConst(constVal->mTypeCode, OP constVal->mDouble); break; \ - default: break; \ - } \ - return CreateConst(constVal->mTypeCode, val); + uint64 val = 0; \ + switch (constVal->mTypeCode) \ + { \ + case BfTypeCode_Int8: val = OP constVal->mInt8; break; \ + case BfTypeCode_UInt8: val = OP constVal->mUInt8; break; \ + case BfTypeCode_Char8: val = OP constVal->mUInt8; break; \ + case BfTypeCode_Int16: val = OP constVal->mInt16; break; \ + case BfTypeCode_UInt16: val = OP constVal->mUInt16; break; \ + case BfTypeCode_Char16: val = OP constVal->mUInt16; break; \ + case BfTypeCode_Int32: val = OP constVal->mInt32; break; \ + case BfTypeCode_UInt32: val = OP constVal->mUInt32; break; \ + case BfTypeCode_Char32: val = OP constVal->mUInt32; break; \ + case BfTypeCode_Int64: val = OP constVal->mInt64; break; \ + case BfTypeCode_UInt64: val = OP constVal->mUInt64; break; \ + case BfTypeCode_Float: \ + case BfTypeCode_Double: \ + return CreateConst(constVal->mTypeCode, OP constVal->mDouble); break; \ + default: break; \ + } \ + return CreateConst(constVal->mTypeCode, val); \ + } #define CMP_APPLY(lhs, rhs, OP) \ auto constLHS = GetConstantById(lhs.mId); \ @@ -489,11 +503,12 @@ int BfIRConstHolder::CheckConstEquality(BfIRValue lhs, BfIRValue rhs) return 1; } - if (constLHS->mConstType == BfConstType_GlobalVar) - { - // We would have already caught the (constLHS == constRHS) case further up - return 0; - } + //TODO: Why did we do this? This made global variable comparisons (ie: sA != sB) const-evaluate to false always +// if (constLHS->mConstType == BfConstType_GlobalVar) +// { +// // We would have already caught the (constLHS == constRHS) case further up +// return 0; +// } return -1; } @@ -701,6 +716,29 @@ BfIRValue BfIRConstHolder::CreateConst(BfConstant* fromConst, BfIRConstHolder* f else return CreateConstNull(); } + else if (fromConst->mConstType == BfConstType_PtrToInt) + { + auto fromPtrToInt = (BfConstantPtrToInt*)fromConst; + auto fromTarget = fromHolder->GetConstantById(fromPtrToInt->mTarget); + auto copiedTarget = CreateConst(fromTarget, fromHolder); + auto ptrToInt = mTempAlloc.Alloc(); + ptrToInt->mConstType = BfConstType_PtrToInt; + ptrToInt->mTarget = copiedTarget.mId; + ptrToInt->mToTypeCode = fromPtrToInt->mToTypeCode; + copiedConst = (BfConstant*)ptrToInt; + } + else if (fromConst->mConstType == BfConstType_IntToPtr) + { + auto fromPtrToInt = (BfConstantIntToPtr*)fromConst; + auto fromTarget = fromHolder->GetConstantById(fromPtrToInt->mTarget); + auto copiedTarget = CreateConst(fromTarget, fromHolder); + auto ptrToInt = mTempAlloc.Alloc(); + ptrToInt->mConstType = BfConstType_IntToPtr; + ptrToInt->mTarget = copiedTarget.mId; + ptrToInt->mToType = fromPtrToInt->mToType; + copiedConst = (BfConstant*)ptrToInt; + } + else { BF_FATAL("not handled"); @@ -3603,7 +3641,15 @@ void BfIRBuilder::CreateTypeDefinition(BfType* type, bool forceDbgDefine) // isDefiningModule = true; if ((isDefiningModule) || (type->IsValueType())) - populateModule->PopulateType(type, BfPopulateType_DataAndMethods); + { + if ((typeInstance != NULL) && (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotting)) + { + // Don't re-enter + BfLogSys(mModule->mSystem, "BfIRBuilder::CreateTypeDefinition avoided PopulateType BfPopulateType_DataAndMethods re-entry typeInst: %p\n", typeInstance); + } + else + populateModule->PopulateType(type, BfPopulateType_DataAndMethods); + } if ((!isDefiningModule) && (!type->IsUnspecializedType()) && (type->IsValueType()) && (mHasDebugInfo)) { diff --git a/IDEHelper/Compiler/BfIRCodeGen.cpp b/IDEHelper/Compiler/BfIRCodeGen.cpp index 94ec1b69..ea4ebd87 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.cpp +++ b/IDEHelper/Compiler/BfIRCodeGen.cpp @@ -2519,6 +2519,8 @@ void BfIRCodeGen::HandleNextCmd() case BfIRCmd_SetInsertPoint: { CMD_PARAM(llvm::BasicBlock*, block); + if (mLockedBlocks.Contains(block)) + Fail("Attempt to modify locked block"); mIRBuilder->SetInsertPoint(block); } break; @@ -3861,6 +3863,8 @@ void BfIRCodeGen::HandleNextCmd() if (!useAsm) { + mLockedBlocks.Add(irBuilder->GetInsertBlock()); + // This is generates slower code than the inline asm in debug mode, but can optimize well in release auto int8Ty = llvm::Type::getInt8Ty(*mLLVMContext); auto int8Ptr = irBuilder->CreateBitCast(val, int8Ty->getPointerTo()); diff --git a/IDEHelper/Compiler/BfIRCodeGen.h b/IDEHelper/Compiler/BfIRCodeGen.h index 719a9f6b..30285c51 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.h +++ b/IDEHelper/Compiler/BfIRCodeGen.h @@ -117,6 +117,7 @@ public: Dictionary mReflectDataMap; Dictionary mAlignedTypeToNormalType; Dictionary mTypeToTypeIdMap; + HashSet mLockedBlocks; public: void InitTarget(); diff --git a/IDEHelper/Compiler/BfMangler.cpp b/IDEHelper/Compiler/BfMangler.cpp index 989bb87f..2f0905e2 100644 --- a/IDEHelper/Compiler/BfMangler.cpp +++ b/IDEHelper/Compiler/BfMangler.cpp @@ -318,6 +318,11 @@ void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name { name += "_"; name += methodDef->mParams[paramIdx]->mName; + if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_VarArgs) + { + name += "__varargs"; + continue; + } typeVec.push_back(BfNodeDynCast(methodDef->mParams[paramIdx]->mTypeRef)->mType); } for (auto type : typeVec) @@ -579,10 +584,22 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType } else if (type->IsSizedArray()) { - BfSizedArrayType* arrayType = (BfSizedArrayType*)type; - name += StrFormat("A%d_"); - Mangle(mangleContext, name, arrayType->mElementType); - return; + if (type->IsUnknownSizedArrayType()) + { + BfUnknownSizedArrayType* arrayType = (BfUnknownSizedArrayType*)type; + name += "A_"; + Mangle(mangleContext, name, arrayType->mElementType); + name += "_"; + Mangle(mangleContext, name, arrayType->mElementCountSource); + return; + } + else + { + BfSizedArrayType* arrayType = (BfSizedArrayType*)type; + name += StrFormat("A%d_", arrayType->mElementCount); + Mangle(mangleContext, name, arrayType->mElementType); + return; + } } else if (type->IsMethodRef()) { @@ -749,6 +766,15 @@ String BfGNUMangler::Mangle(BfMethodInstance* methodInst) case BfBinaryOp_Multiply: methodName = "ml"; break; + case BfBinaryOp_OverflowAdd: + methodName = "opl"; + break; + case BfBinaryOp_OverflowSubtract: + methodName = "omi"; + break; + case BfBinaryOp_OverflowMultiply: + methodName = "oml"; + break; case BfBinaryOp_Divide: methodName = "dv"; break; @@ -1700,7 +1726,7 @@ void BfMSMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType* name += "?$_ARRAY@"; Mangle(mangleContext, name, arrType->mElementType); - MangleConst(mangleContext, name, arrType->mSize); + MangleConst(mangleContext, name, arrType->mElementCount); name += '@'; } } @@ -1814,6 +1840,7 @@ void BfMSMangler::Mangle(StringImpl& name, bool is64Bit, BfMethodInstance* metho static int mangleIdx = 0; mangleIdx++; + int startNameLen = name.mLength; if ((methodInst->mMethodDef->mCLink) && (!methodInst->mMangleWithIdx)) { name += methodInst->mMethodDef->mName; @@ -1834,7 +1861,7 @@ void BfMSMangler::Mangle(StringImpl& name, bool is64Bit, BfMethodInstance* metho HandleCustomAttributes(methodInst->GetCustomAttributes(), typeInst->mConstHolder, mangleContext.mModule, name, isCMangle, mangleContext.mCPPMangle); if (isCMangle) name += methodInst->mMethodDef->mName; - if (!name.IsEmpty()) + if (name.mLength > startNameLen) return; name += '?'; @@ -1867,6 +1894,15 @@ void BfMSMangler::Mangle(StringImpl& name, bool is64Bit, BfMethodInstance* metho case BfBinaryOp_Multiply: name += "?D"; break; + case BfBinaryOp_OverflowAdd: + name += "?OH"; + break; + case BfBinaryOp_OverflowSubtract: + name += "?OG"; + break; + case BfBinaryOp_OverflowMultiply: + name += "?OD"; + break; case BfBinaryOp_Divide: name += "?K"; break; @@ -2367,7 +2403,7 @@ void BfMangler::HandleParamCustomAttributes(BfAttributeDirective* attributes, bo { if (attributes->mAttributeTypeRef != NULL) { - auto typeRefName = attributes->mAttributeTypeRef->ToString(); + auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); if (typeRefName == "MangleConst") isConst = true; } diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 8a9e3447..f9074614 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -827,9 +827,9 @@ public: BfModule* gLastCreatedModule = NULL; BfModule::BfModule(BfContext* context, const StringImpl& moduleName) -{ +{ BfLogSys(context->mSystem, "BfModule::BFModule %p %s\n", this, moduleName.c_str()); - + gLastCreatedModule = this; mContext = context; @@ -847,6 +847,7 @@ BfModule::BfModule(BfContext* context, const StringImpl& moduleName) mUsedSlotCount = -1; mIsReified = true; + mGeneratesCode = true; mReifyQueued = false; mIsSpecialModule = false; mIsComptimeModule = false; @@ -1048,6 +1049,20 @@ void BfModule::FinishInit() mAwaitingInitFinish = false; } +void BfModule::CalcGeneratesCode() +{ + if ((!mIsReified) || (mIsScratchModule)) + { + mGeneratesCode = false; + return; + } + + mGeneratesCode = false; + for (auto typeInst : mOwnedTypeInstances) + if (!typeInst->IsInterface()) + mGeneratesCode = true; +} + void BfModule::ReifyModule() { BF_ASSERT((mCompiler->mCompileState != BfCompiler::CompileState_Unreified) && (mCompiler->mCompileState != BfCompiler::CompileState_VData)); @@ -1055,9 +1070,10 @@ void BfModule::ReifyModule() BfLogSysM("ReifyModule %@ %s\n", this, mModuleName.c_str()); BF_ASSERT((this != mContext->mScratchModule) && (this != mContext->mUnreifiedModule)); mIsReified = true; + CalcGeneratesCode(); mReifyQueued = false; StartNewRevision(RebuildKind_SkipOnDemandTypes, true); - mCompiler->mStats.mModulesReified++; + mCompiler->mStats.mModulesReified++; } void BfModule::UnreifyModule() @@ -1065,6 +1081,7 @@ void BfModule::UnreifyModule() BfLogSysM("UnreifyModule %p %s\n", this, mModuleName.c_str()); BF_ASSERT((this != mContext->mScratchModule) && (this != mContext->mUnreifiedModule)); mIsReified = false; + CalcGeneratesCode(); mReifyQueued = false; StartNewRevision(RebuildKind_None, true); mCompiler->mStats.mModulesUnreified++; @@ -1126,8 +1143,8 @@ void BfModule::SetupIRBuilder(bool dbgVerifyCodeGen) // The only purpose of not ignoring writes is so we can verify the codegen one instruction at a time mBfIRBuilder->mDbgVerifyCodeGen = true; } - } - else if (!mIsReified) + } + else if (!mGeneratesCode) { mBfIRBuilder->mIgnoreWrites = true; } @@ -1135,7 +1152,7 @@ void BfModule::SetupIRBuilder(bool dbgVerifyCodeGen) { // We almost always want this to be 'false' unless we need need to be able to inspect the generated LLVM // code as we walk the AST - //mBfIRBuilder->mDbgVerifyCodeGen = true; + //mBfIRBuilder->mDbgVerifyCodeGen = true; if ( (mModuleName == "-") //|| (mModuleName == "BeefTest2_ClearColorValue") @@ -1717,7 +1734,10 @@ BfIRValue BfModule::GetStringObjectValue(const StringImpl& str, bool define, boo if ((mBfIRBuilder->mIgnoreWrites) && (!force)) { - mUnreifiedStringPoolRefs.Add(strId); + auto refModule = this; + if ((this == mContext->mUnreifiedModule) && (mCurTypeInstance != NULL)) + refModule = mCurTypeInstance->mModule; + refModule->mUnreifiedStringPoolRefs.Add(strId); return mBfIRBuilder->CreateConst(BfTypeCode_StringId, strId); } @@ -1797,6 +1817,7 @@ void BfModule::NewScopeState(bool createLexicalBlock, bool flushValueScope) } mCurMethodState->mCurScope->mLocalVarStart = (int)mCurMethodState->mLocals.size(); mCurMethodState->mCurScope->mBlock = mBfIRBuilder->MaybeChainNewBlock((!mCurMethodState->mCurScope->mLabel.empty()) ? mCurMethodState->mCurScope->mLabel : "newScope"); + mCurMethodState->mCurScope->mMixinState = mCurMethodState->mMixinState; } void BfModule::RestoreScoreState_LocalVariables() @@ -1915,7 +1936,7 @@ BfIRValue BfModule::CreateAllocaInst(BfTypeInstance* typeInst, bool addLifetime, return allocaInst; } -void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* refNode, BfScopeData* scopeData, bool condAlloca, bool mayEscape) +void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* refNode, BfScopeData* scopeData, bool condAlloca, bool mayEscape, BfIRBlock valBlock) { //This was removed because we want the alloc to be added to the __deferred list if it's actually a "stack" // 'stack' in a head scopeData is really the same as 'scopeData', so use the simpler scopeData handling @@ -1939,7 +1960,15 @@ void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* r { bool isDynAlloc = (scopeData != NULL) && (mCurMethodState->mCurScope->IsDyn(scopeData)); BfIRValue useVal = val.mValue; - useVal = mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapTypeInstPtr(checkBaseType)); + + BfIRBlock prevBlock = mBfIRBuilder->GetInsertBlock(); + if (valBlock) + mBfIRBuilder->SetInsertPoint(valBlock); + useVal = mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapTypeInstPtr(checkBaseType)); + if (!useVal.IsConst()) + mBfIRBuilder->ClearDebugLocation(useVal); + if (valBlock) + mBfIRBuilder->SetInsertPoint(prevBlock); if (isDynAlloc) { @@ -1950,7 +1979,9 @@ void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* r BF_ASSERT(!IsTargetingBeefBackend()); BF_ASSERT(!isDynAlloc); auto valPtr = CreateAlloca(checkBaseType); + mBfIRBuilder->ClearDebugLocation_Last(); mBfIRBuilder->CreateStore(useVal, valPtr); + mBfIRBuilder->ClearDebugLocation_Last(); useVal = valPtr; } @@ -1994,7 +2025,10 @@ void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* r { SizedArray llvmArgs; if (IsTargetingBeefBackend()) + { llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(nullPtrType))); + //mBfIRBuilder->ClearDebugLocation_Last(); + } else llvmArgs.push_back(val.mValue); llvmArgs.push_back(GetConstValue(val.mType->mSize)); @@ -2023,6 +2057,7 @@ void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* r { SizedArray llvmArgs; llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(nullPtrType))); + //mBfIRBuilder->ClearDebugLocation_Last(); llvmArgs.push_back(clearSize); AddDeferredCall(dtorMethodInstance, llvmArgs, scopeData, refNode, true); } @@ -2052,6 +2087,7 @@ void BfModule::AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* r { SizedArray llvmArgs; llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(nullPtrType))); + //mBfIRBuilder->ClearDebugLocation_Last(); AddDeferredCall(dtorMethodInstance, llvmArgs, scopeData, refNode, true); } } @@ -2164,7 +2200,7 @@ void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit) bool deferFullAnalysis = mCurMethodState->GetRootMethodState()->mLocalMethodCache.size() != 0; // We may have init blocks that we aren't processing here... - if ((mCurMethodInstance->mIsAutocompleteMethod) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor)) + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsAutocompleteMethod) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor)) deferFullAnalysis = true; //bool deferFullAnalysis = true; @@ -2643,7 +2679,7 @@ bool BfModule::CheckProtection(BfProtectionCheckFlags& flags, BfTypeInstance* me auto mixinOwner = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner(); curCheckType = mixinOwner; } - bool allowPrivate = (curCheckType != NULL) && (memberOwner->mTypeDef == curCheckType->mTypeDef); + bool allowPrivate = (curCheckType != NULL) && (memberOwner->IsInstanceOf(curCheckType->mTypeDef)); if (curCheckType != NULL) allowPrivate |= IsInnerType(curCheckType->mTypeDef, memberOwner->mTypeDef); if (allowPrivate) @@ -2811,6 +2847,102 @@ bool BfModule::IsSkippingExtraResolveChecks() return mCompiler->IsSkippingExtraResolveChecks(); } +bool BfModule::AddErrorContext(StringImpl& errorString, BfAstNode* refNode, bool& isWhileSpecializing) +{ + bool isWhileSpecializingMethod = false; + if ((mIsSpecialModule) && (mModuleName == "vdata")) + errorString += StrFormat("\n while generating vdata for project '%s'", mProject->mName.c_str()); + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend)) + errorString += StrFormat("\n while generating append size calculating method"); + else if (refNode == NULL) + { + if (mCurTypeInstance != NULL) + errorString += StrFormat("\n while compiling '%s'", TypeToString(mCurTypeInstance, BfTypeNameFlags_None).c_str()); + else if (mProject != NULL) + errorString += StrFormat("\n while compiling project '%s'", mProject->mName.c_str()); + } + + if (mCurTypeInstance != NULL) + { + auto _CheckMethodInstance = [&](BfMethodInstance* methodInstance) + { + // Propogate the fail all the way to the main method (assuming we're in a local method or lambda) + methodInstance->mHasFailed = true; + + bool isSpecializedMethod = ((methodInstance != NULL) && (!methodInstance->mIsUnspecialized) && (methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mMethodGenericArguments.size() != 0)); + if (isSpecializedMethod) + { + //auto unspecializedMethod = &mCurMethodInstance->mMethodInstanceGroup->mMethodSpecializationMap.begin()->second; + auto unspecializedMethod = methodInstance->mMethodInstanceGroup->mDefault; + if (unspecializedMethod == methodInstance) + { + // This is a local method inside a generic method + BF_ASSERT(methodInstance->mMethodDef->mIsLocalMethod); + } + else + { + if (unspecializedMethod->mHasFailed) + return false; // At least SOME error has already been reported + } + } + + if (isSpecializedMethod) + { + errorString += StrFormat("\n while specializing method '%s'", MethodToString(methodInstance).c_str()); + isWhileSpecializing = true; + isWhileSpecializingMethod = true; + } + else if ((methodInstance != NULL) && (methodInstance->mIsForeignMethodDef)) + { + errorString += StrFormat("\n while implementing default interface method '%s'", MethodToString(methodInstance).c_str()); + isWhileSpecializing = true; + isWhileSpecializingMethod = true; + } + else if ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType())) + { + errorString += StrFormat("\n while specializing type '%s'", TypeToString(mCurTypeInstance).c_str()); + isWhileSpecializing = true; + } + + return true; + }; + + bool hadMethodInstance = false; + if (mCurMethodState != NULL) + { + auto checkMethodState = mCurMethodState; + while (checkMethodState != NULL) + { + auto methodInstance = checkMethodState->mMethodInstance; + if (methodInstance == NULL) + { + checkMethodState = checkMethodState->mPrevMethodState; + continue; + } + + hadMethodInstance = true; + if (!_CheckMethodInstance(methodInstance)) + return false; + checkMethodState = checkMethodState->mPrevMethodState; + } + } + + if ((!hadMethodInstance) && (mCurMethodInstance != NULL)) + { + if (!_CheckMethodInstance(mCurMethodInstance)) + return false; + } + } + + if ((!isWhileSpecializing) && (mCurTypeInstance != NULL) && ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType()))) + { + errorString += StrFormat("\n while specializing type '%s'", TypeToString(mCurTypeInstance).c_str()); + isWhileSpecializing = true; + } + + return true; +} + BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPersistent, bool deferError) { BP_ZONE("BfModule::Fail"); @@ -2866,105 +2998,15 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers BfLogSysM("BfModule::Fail module %p type %p %s\n", this, mCurTypeInstance, error.c_str()); - String errorString = error; - bool isWhileSpecializing = false; - bool isWhileSpecializingMethod = false; - - if ((mIsSpecialModule) && (mModuleName == "vdata")) - errorString += StrFormat("\n while generating vdata for project '%s'", mProject->mName.c_str()); - if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend)) - errorString += StrFormat("\n while generating append size calculating method"); - else if (refNode == NULL) - { - if (mCurTypeInstance != NULL) - errorString += StrFormat("\n while compiling '%s'", TypeToString(mCurTypeInstance, BfTypeNameFlags_None).c_str()); - else if (mProject != NULL) - errorString += StrFormat("\n while compiling project '%s'", mProject->mName.c_str()); - } - - if (mCurTypeInstance != NULL) - { - auto _CheckMethodInstance = [&](BfMethodInstance* methodInstance) - { - // Propogate the fail all the way to the main method (assuming we're in a local method or lambda) - methodInstance->mHasFailed = true; - - bool isSpecializedMethod = ((methodInstance != NULL) && (!methodInstance->mIsUnspecialized) && (methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mMethodGenericArguments.size() != 0)); - if (isSpecializedMethod) - { - //auto unspecializedMethod = &mCurMethodInstance->mMethodInstanceGroup->mMethodSpecializationMap.begin()->second; - auto unspecializedMethod = methodInstance->mMethodInstanceGroup->mDefault; - if (unspecializedMethod == methodInstance) - { - // This is a local method inside a generic method - BF_ASSERT(methodInstance->mMethodDef->mIsLocalMethod); - } - else - { - if (unspecializedMethod->mHasFailed) - return false; // At least SOME error has already been reported - } - } - - if (isSpecializedMethod) - { - errorString += StrFormat("\n while specializing method '%s'", MethodToString(methodInstance).c_str()); - isWhileSpecializing = true; - isWhileSpecializingMethod = true; - } - else if ((methodInstance != NULL) && (methodInstance->mIsForeignMethodDef)) - { - errorString += StrFormat("\n while implementing default interface method '%s'", MethodToString(methodInstance).c_str()); - isWhileSpecializing = true; - isWhileSpecializingMethod = true; - } - else if ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType())) - { - errorString += StrFormat("\n while specializing type '%s'", TypeToString(mCurTypeInstance).c_str()); - isWhileSpecializing = true; - } - - return true; - }; - - bool hadMethodInstance = false; - if (mCurMethodState != NULL) - { - auto checkMethodState = mCurMethodState; - while (checkMethodState != NULL) - { - auto methodInstance = checkMethodState->mMethodInstance; - if (methodInstance == NULL) - { - checkMethodState = checkMethodState->mPrevMethodState; - continue; - } - - hadMethodInstance = true; - if (!_CheckMethodInstance(methodInstance)) - return NULL; - checkMethodState = checkMethodState->mPrevMethodState; - } - } - - if ((!hadMethodInstance) && (mCurMethodInstance != NULL)) - { - if (!_CheckMethodInstance(mCurMethodInstance)) - return NULL; - } - } - + String errorString = error; + bool isWhileSpecializing = false; + if (!AddErrorContext(errorString, refNode, isWhileSpecializing)) + return NULL; + BfError* bfError = NULL; - if (isWhileSpecializing) deferError = true; - if ((!isWhileSpecializing) && (mCurTypeInstance != NULL) && ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType()))) - { - errorString += StrFormat("\n while specializing type '%s'", TypeToString(mCurTypeInstance).c_str()); - isWhileSpecializing = true; - } - if (!mHadBuildError) mHadBuildError = true; @@ -3052,7 +3094,7 @@ BfError* BfModule::FailAfter(const StringImpl& error, BfAstNode* refNode) return bfError; } -BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* refNode, bool isPersistent) +BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* refNode, bool isPersistent, bool showInSpecialized) { if (mIgnoreErrors || mIgnoreWarnings) return NULL; @@ -3080,21 +3122,33 @@ BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* re { return NULL; } - + // Right now we're only warning on the unspecialized declarations, we may revisit this if (mCurMethodInstance != NULL) { if (mCurMethodInstance->IsSpecializedGenericMethodOrType()) - return NULL; + { + if (!showInSpecialized) + return NULL; + } if (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend) return NULL; // No ctorCalcAppend warnings } if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsSpecializedType())) - return NULL; + { + if (!showInSpecialized) + return NULL; + } if (refNode != NULL) refNode = BfNodeToNonTemporary(refNode); + String warningString = warning; + bool isWhileSpecializing = false; + if (!AddErrorContext(warningString, refNode, isWhileSpecializing)) + return NULL; + bool deferWarning = isWhileSpecializing; + if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL)) { // We used to bubble up warnings into the mixin injection site, BUT @@ -3106,17 +3160,18 @@ BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* re { BfError* bfError = mCompiler->mPassInstance->Warn(warningNum, "Emitted code had errors", mCurMethodState->mEmitRefNode); if (bfError != NULL) - mCompiler->mPassInstance->MoreInfo(warning, refNode); + mCompiler->mPassInstance->MoreInfo(warningString, refNode); return bfError; } BfError* bfError; if (refNode != NULL) - bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength()); + bfError = mCompiler->mPassInstance->WarnAt(warningNum, warningString, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength()); else - bfError = mCompiler->mPassInstance->Warn(warningNum, warning); + bfError = mCompiler->mPassInstance->Warn(warningNum, warningString); if (bfError != NULL) { + bfError->mIsWhileSpecializing = isWhileSpecializing; bfError->mProject = mProject; AddFailType(mCurTypeInstance); @@ -3196,7 +3251,7 @@ void BfModule::CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstan if (isError) error = Fail(err, targetSrc); else - error = Warn(0, err, targetSrc); + error = Warn(0, err, targetSrc, false, true); if (error != NULL) _AddDeclarationMoreInfo(); } @@ -3229,7 +3284,7 @@ void BfModule::CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstan if (str != NULL) err += *str; err += "'"; - if (Warn(0, err, targetSrc) != NULL) + if (Warn(0, err, targetSrc, false, true) != NULL) _AddDeclarationMoreInfo(); } } @@ -3333,6 +3388,12 @@ void BfModule::AddDependency(BfType* usedType, BfType* userType, BfDependencyMap if (usedType == userType) return; + if (((flags & BfDependencyMap::DependencyFlag_ConstValue) != 0) && (mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mResolveKind == BfTypeState::ResolveKind_FieldType)) + { + // This can be an `int32[UsedType.cVal]` type reference + flags = (BfDependencyMap::DependencyFlags)(flags | BfDependencyMap::DependencyFlag_ValueTypeSizeDep); + } + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsAutocompleteMethod)) { if (userType->IsMethodRef()) @@ -3403,20 +3464,25 @@ void BfModule::AddDependency(BfType* usedType, BfType* userType, BfDependencyMap if (addType) { auto checkTypeInst = checkType->ToTypeInstance(); - auto hotTypeVersion = checkTypeInst->mHotTypeData->GetLatestVersion(); - BF_ASSERT(hotTypeVersion != NULL); + if (checkTypeInst->mHotTypeData != NULL) + { + auto hotTypeVersion = checkTypeInst->mHotTypeData->GetLatestVersion(); + BF_ASSERT(hotTypeVersion != NULL); + if (hotTypeVersion != NULL) + { + bool isAllocation = ((flags & BfDependencyMap::DependencyFlag_Allocates) != 0); + if (((flags & BfDependencyMap::DependencyFlag_LocalUsage) != 0) && + (checkType->IsComposite())) + isAllocation = true; - bool isAllocation = ((flags & BfDependencyMap::DependencyFlag_Allocates) != 0); - if (((flags & BfDependencyMap::DependencyFlag_LocalUsage) != 0) && - (checkType->IsComposite())) - isAllocation = true; - - if (isAllocation) - { - mCurMethodState->mHotDataReferenceBuilder->mAllocatedData.Add(hotTypeVersion); + if (isAllocation) + { + mCurMethodState->mHotDataReferenceBuilder->mAllocatedData.Add(hotTypeVersion); + } + else + mCurMethodState->mHotDataReferenceBuilder->mUsedData.Add(hotTypeVersion); + } } - else - mCurMethodState->mHotDataReferenceBuilder->mUsedData.Add(hotTypeVersion); } } @@ -3452,11 +3518,21 @@ void BfModule::AddDependency(BfType* usedType, BfType* userType, BfDependencyMap auto depFlag = flags; if ((flags & (BfDependencyMap::DependencyFlag_MethodGenericArg | BfDependencyMap::DependencyFlag_TypeGenericArg)) != 0) - depFlag = BfDependencyMap::DependencyFlag_GenericArgRef; // Will cause a rebuild but not an outright deletion of the type + { + if (usedType->IsDependendType()) + { + // Cause a rebuild but not an outright deletion of the type + // We can only do this if the 'usedType' can actually hold the dependency which can actually trigger a deletion chain + depFlag = BfDependencyMap::DependencyFlag_GenericArgRef; + } + } - auto underlyingType = usedType->GetUnderlyingType(); - if (underlyingType != NULL) - AddDependency(underlyingType, userType, depFlag); + if (!usedType->IsGenericTypeInstance()) + { + auto underlyingType = usedType->GetUnderlyingType(); + if (underlyingType != NULL) + AddDependency(underlyingType, userType, depFlag); + } BfDependedType* checkDType = usedType->ToDependedType(); if (checkDType == NULL) @@ -3476,7 +3552,7 @@ void BfModule::AddDependency(BfType* usedType, BfType* userType, BfDependencyMap if (!checkDType->mDependencyMap.AddUsedBy(userType, flags)) return; - if (checkDType->IsGenericTypeInstance()) + if ((checkDType->IsGenericTypeInstance()) && (!userType->IsMethodRef())) { auto genericTypeInstance = (BfTypeInstance*) checkDType; for (auto genericArg : genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments) @@ -3513,7 +3589,7 @@ bool BfModule::IsAttribute(BfTypeInstance* typeInst) auto checkTypeInst = typeInst; while (checkTypeInst != NULL) { - if (checkTypeInst->mTypeDef == mCompiler->mAttributeTypeDef) + if (checkTypeInst->IsInstanceOf(mCompiler->mAttributeTypeDef)) return true; checkTypeInst = checkTypeInst->mBaseType; @@ -3637,10 +3713,10 @@ bool BfModule::CheckInternalProtection(BfTypeDef* usingTypeDef) for (auto internalType : internalAccessSet->mTypes) { - auto checkTypeDef = usingTypeDef; + auto checkTypeDef = usingTypeDef->GetDefinition(); while (checkTypeDef != NULL) { - if (checkTypeDef == internalType->mTypeDef) + if (checkTypeDef == internalType->mTypeDef->GetDefinition()) return true; checkTypeDef = checkTypeDef->mOuterType; } @@ -4025,7 +4101,7 @@ BfType* BfModule::ResolveVarFieldType(BfTypeInstance* typeInstance, BfFieldInsta SetAndRestoreValue prevTypeInstance(mCurTypeInstance, typeInstance); BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState); - SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState, mContext->mCurTypeState->mTypeInstance != typeInstance); + SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState, mContext->mCurTypeState->mType != typeInstance); auto typeDef = typeInstance->mTypeDef; @@ -4072,7 +4148,7 @@ BfType* BfModule::ResolveVarFieldType(BfTypeInstance* typeInstance, BfFieldInsta { BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; - typeState.mTypeInstance = typeInstance; + typeState.mType = typeInstance; typeState.mCurFieldDef = field; typeState.mResolveKind = BfTypeState::ResolveKind_ResolvingVarType; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -4174,7 +4250,7 @@ bool BfModule::IsThreadLocal(BfFieldInstance * fieldInstance) { for (auto customAttr : fieldInstance->mCustomAttributes->mAttributes) { - if (customAttr.mType->ToTypeInstance()->mTypeDef == mCompiler->mThreadStaticAttributeTypeDef) + if (customAttr.mType->ToTypeInstance()->IsInstanceOf(mCompiler->mThreadStaticAttributeTypeDef)) return true; } } @@ -4223,7 +4299,7 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, ceExecuteId = mCompiler->mCEMachine->mExecuteId; BfTypeState typeState; - typeState.mTypeInstance = mCurTypeInstance; + typeState.mType = mCurTypeInstance; typeState.mCurTypeDef = fieldDef->mDeclaringType; typeState.mCurFieldDef = fieldDef; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -4276,7 +4352,8 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, { if (fieldInstance->mResolvedType->IsUndefSizedArray()) { - AssertErrorState(); + if ((!mBfIRBuilder->mIgnoreWrites) && (!mCompiler->mFastFinish)) + AssertErrorState(); } else { @@ -4381,7 +4458,7 @@ void BfModule::CreateDynamicCastMethod() BfTypeVector genericArgs; for (int i = 0; i < (int) genericTypeInst->mGenericParamDefs.size(); i++) genericArgs.push_back(GetGenericParamType(BfGenericParamKind_Type, i)); - auto unboundType = ResolveTypeDef(mCurTypeInstance->mTypeDef, genericArgs, BfPopulateType_Declaration); + auto unboundType = ResolveTypeDef(mCurTypeInstance->mTypeDef->GetDefinition(), genericArgs, BfPopulateType_Declaration); typeMatches.push_back(unboundType->mTypeId); } @@ -4399,9 +4476,9 @@ void BfModule::CreateDynamicCastMethod() } auto innerTypeInst = innerType->ToTypeInstance(); - if ((innerTypeInst->mTypeDef == mCompiler->mSizedArrayTypeDef) || - (innerTypeInst->mTypeDef == mCompiler->mPointerTTypeDef) || - (innerTypeInst->mTypeDef == mCompiler->mMethodRefTypeDef)) + if ((innerTypeInst->IsInstanceOf(mCompiler->mSizedArrayTypeDef)) || + (innerTypeInst->IsInstanceOf(mCompiler->mPointerTTypeDef)) || + (innerTypeInst->IsInstanceOf(mCompiler->mMethodRefTypeDef))) { PopulateType(innerTypeInst); //TODO: What case was this supposed to handle? @@ -5081,6 +5158,121 @@ BfIRValue BfModule::CreateTypeDataRef(BfType* type) return mBfIRBuilder->CreateTypeOf(type, globalVariable); } +void BfModule::EncodeAttributeData(BfTypeInstance* typeInstance, BfType* argType, BfIRValue arg, SizedArrayImpl& data, Dictionary& usedStringIdMap) +{ +#define PUSH_INT8(val) data.push_back((uint8)val) +#define PUSH_INT16(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); } +#define PUSH_INT32(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); data.push_back((val >> 16) & 0xFF); data.push_back((val >> 24) & 0xFF); } +#define PUSH_INT64(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); data.push_back((val >> 16) & 0xFF); data.push_back((val >> 24) & 0xFF); \ + data.push_back((val >> 32) & 0xFF); data.push_back((val >> 40) & 0xFF); data.push_back((val >> 48) & 0xFF); data.push_back((val >> 56) & 0xFF); } + + auto constant = typeInstance->mConstHolder->GetConstant(arg); + bool handled = false; + + if (constant == NULL) + { + Fail(StrFormat("Unhandled attribute constant data in '%s'", TypeToString(typeInstance).c_str())); + return; + } + + if (argType->IsObject()) + { + if (argType->IsInstanceOf(mCompiler->mStringTypeDef)) + { + int stringId = constant->mInt32; + int* orderedIdPtr; + if (usedStringIdMap.TryAdd(stringId, NULL, &orderedIdPtr)) + { + *orderedIdPtr = (int)usedStringIdMap.size() - 1; + } + + GetStringObjectValue(stringId, true, true); + PUSH_INT8(0xFF); // String + PUSH_INT32(*orderedIdPtr); + return; + } + } + + if (argType->IsPointer()) + { + if (argType->GetUnderlyingType() == GetPrimitiveType(BfTypeCode_Char8)) + { + if (constant->mTypeCode == BfTypeCode_StringId) + { + int stringId = constant->mInt32; + int* orderedIdPtr; + if (usedStringIdMap.TryAdd(stringId, NULL, &orderedIdPtr)) + { + *orderedIdPtr = (int)usedStringIdMap.size() - 1; + } + + GetStringObjectValue(stringId, true, true); + PUSH_INT8(0xFF); // String + PUSH_INT32(*orderedIdPtr); + return; + } + } + } + + PUSH_INT8(constant->mTypeCode); + if ((constant->mTypeCode == BfTypeCode_Int64) || + (constant->mTypeCode == BfTypeCode_UInt64) || + (constant->mTypeCode == BfTypeCode_Double)) + { + PUSH_INT64(constant->mInt64); + } + else if ((constant->mTypeCode == BfTypeCode_Int32) || + (constant->mTypeCode == BfTypeCode_UInt32) || + (constant->mTypeCode == BfTypeCode_Char32)) + { + PUSH_INT32(constant->mInt32); + } + else if (constant->mTypeCode == BfTypeCode_Float) + { + float val = (float)constant->mDouble; + PUSH_INT32(*(int*)&val); + } + else if ((constant->mTypeCode == BfTypeCode_Int16) || + (constant->mTypeCode == BfTypeCode_UInt16) || + (constant->mTypeCode == BfTypeCode_Char16)) + { + PUSH_INT16(constant->mInt16); + } + else if ((constant->mTypeCode == BfTypeCode_Int8) || + (constant->mTypeCode == BfTypeCode_UInt8) || + (constant->mTypeCode == BfTypeCode_Boolean) || + (constant->mTypeCode == BfTypeCode_Char8)) + { + PUSH_INT8(constant->mInt8); + } + else if (constant->mConstType == BfConstType_TypeOf) + { + auto typeOf = (BfTypeOf_Const*)constant; + PUSH_INT32(typeOf->mType->mTypeId); + } + else if (constant->mConstType == BfConstType_AggZero) + { + for (int i = 0; i < argType->mSize; i++) + data.Add(0); + } +// else if (constant->mConstType == BfConstType_Agg) +// { +// BF_ASSERT(argType->IsComposite()); +// if (argType->IsSizedArray()) +// { +// auto argSizedArrayType = (BfSizedArrayType*)argType; +// } +// else +// { +// auto argTypeInstance = argType->ToTypeInstance(); +// } +// } + else + { + Fail(StrFormat("Unhandled attribute constant data in '%s'", TypeToString(typeInstance).c_str())); + } +} + BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStringIdMap, bool forceReflectFields, bool needsTypeData, bool needsTypeNames, bool needsVData) { if ((mCompiler->IsHotCompile()) && (!type->mDirty)) @@ -5275,7 +5467,15 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin boxedTypeId = boxedType->mTypeId; } - SizedArray typeDataParams = + int stackCount = 0; + if ((typeInstance != NULL) && (typeInstance->mTypeOptionsIdx != -1)) + { + auto typeOptions = mSystem->GetTypeOptions(typeInstance->mTypeOptionsIdx); + if (typeOptions->mAllocStackTraceDepth != -1) + stackCount = BF_MIN(0xFF, BF_MAX(0x01, typeOptions->mAllocStackTraceDepth)); + } + + SizedArray typeDataParams = { objectData, GetConstValue(type->mSize, intType), // mSize @@ -5284,7 +5484,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin GetConstValue(typeFlags, intType), // mTypeFlags GetConstValue(memberDataOffset, intType), // mMemberDataOffset GetConstValue(typeCode, byteType), // mTypeCode - GetConstValue(type->mAlign, byteType), + GetConstValue(type->mAlign, byteType), // mAlign + GetConstValue(stackCount, byteType), // mAllocStackCountOverride }; auto typeData = mBfIRBuilder->CreateConstAgg_Value(mBfIRBuilder->MapTypeInst(mContext->mBfTypeType, BfIRPopulateType_Full), typeDataParams); @@ -6018,9 +6219,6 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin #define PUSH_INT8(val) data.push_back((uint8)val) #define PUSH_INT16(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); } #define PUSH_INT32(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); data.push_back((val >> 16) & 0xFF); data.push_back((val >> 24) & 0xFF); } -#define PUSH_INT64(val) { data.push_back(val & 0xFF); data.push_back((val >> 8) & 0xFF); data.push_back((val >> 16) & 0xFF); data.push_back((val >> 24) & 0xFF); \ - data.push_back((val >> 32) & 0xFF); data.push_back((val >> 40) & 0xFF); data.push_back((val >> 48) & 0xFF); data.push_back((val >> 56) & 0xFF); } - SizedArray data; int customAttrIdx = (int)customAttrs.size(); @@ -6062,96 +6260,11 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin for (auto arg : attr->mCtorArgs) { - auto constant = typeInstance->mConstHolder->GetConstant(arg); - bool handled = false; - - if (constant != NULL) - { - auto argType = ctorMethodInstance->GetParamType(argIdx); - if (argType->IsObject()) - { - BF_ASSERT(argType->ToTypeInstance()->mTypeDef == mCompiler->mStringTypeDef); - - int stringId = constant->mInt32; - int* orderedIdPtr; - if (usedStringIdMap.TryAdd(stringId, NULL, &orderedIdPtr)) - { - *orderedIdPtr = (int)usedStringIdMap.size() - 1; - } - - GetStringObjectValue(stringId, true, true); - PUSH_INT8(0xFF); // String - PUSH_INT32(*orderedIdPtr); - argIdx++; - continue; - } - - if (argType->IsPointer()) - { - if (argType->GetUnderlyingType() == GetPrimitiveType(BfTypeCode_Char8)) - { - if (constant->mTypeCode == BfTypeCode_StringId) - { - int stringId = constant->mInt32; - int* orderedIdPtr; - if (usedStringIdMap.TryAdd(stringId, NULL, &orderedIdPtr)) - { - *orderedIdPtr = (int)usedStringIdMap.size() - 1; - } - - GetStringObjectValue(stringId, true, true); - PUSH_INT8(0xFF); // String - PUSH_INT32(*orderedIdPtr); - argIdx++; - continue; - } - } - } - - PUSH_INT8(constant->mTypeCode); - if ((constant->mTypeCode == BfTypeCode_Int64) || - (constant->mTypeCode == BfTypeCode_UInt64) || - (constant->mTypeCode == BfTypeCode_Double)) - { - PUSH_INT64(constant->mInt64); - } - else if ((constant->mTypeCode == BfTypeCode_Int32) || - (constant->mTypeCode == BfTypeCode_UInt32) || - (constant->mTypeCode == BfTypeCode_Char32)) - { - PUSH_INT32(constant->mInt32); - } - else if (constant->mTypeCode == BfTypeCode_Float) - { - float val = (float)constant->mDouble; - PUSH_INT32(*(int*)&val); - } - else if ((constant->mTypeCode == BfTypeCode_Int16) || - (constant->mTypeCode == BfTypeCode_UInt16) || - (constant->mTypeCode == BfTypeCode_Char16)) - { - PUSH_INT16(constant->mInt16); - } - else if ((constant->mTypeCode == BfTypeCode_Int8) || - (constant->mTypeCode == BfTypeCode_UInt8) || - (constant->mTypeCode == BfTypeCode_Boolean) || - (constant->mTypeCode == BfTypeCode_Char8)) - { - PUSH_INT8(constant->mInt8); - } - else - { - Fail(StrFormat("Unhandled attribute constant data in '%s'", TypeToString(type).c_str())); - } - } - else if (!handled) - { - BFMODULE_FATAL(this, "Unhandled"); - } - + auto argType = ctorMethodInstance->GetParamType(argIdx); + EncodeAttributeData(typeInstance, argType, arg, data, usedStringIdMap); argIdx++; } - + int size = (int)data.size() - sizeIdx; *((uint16*)&data[sizeIdx]) = size; } @@ -6333,9 +6446,10 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin } } else if (fieldInstance->GetFieldDef()->mIsStatic) - { - auto refVal = ReferenceStaticField(fieldInstance); - + { + BfTypedValue refVal; + if (!mIsComptimeModule) // This can create circular reference issues for a `Self` static + refVal = ReferenceStaticField(fieldInstance); if (refVal.mValue.IsConst()) { auto constant = mBfIRBuilder->GetConstant(refVal.mValue); @@ -6483,7 +6597,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin struct _SortedMethodInfo { BfMethodDef* mMethodDef; - BfCustomAttributes* mMethodCustomAttributes; + BfMethodCustomAttributes* mMethodCustomAttributes; }; Array<_SortedMethodInfo> sortedMethodList; @@ -6524,10 +6638,10 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin bool includeMethod = reflectIncludeAllMethods; - BfCustomAttributes* methodCustomAttributes = NULL; + BfMethodCustomAttributes* methodCustomAttributes = NULL; if ((defaultMethod->mMethodInfoEx != NULL) && (defaultMethod->mMethodInfoEx->mMethodCustomAttributes != NULL) && (defaultMethod->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes != NULL)) { - methodCustomAttributes = defaultMethod->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes; + methodCustomAttributes = defaultMethod->mMethodInfoEx->mMethodCustomAttributes; for (auto& customAttr : defaultMethod->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes->mAttributes) { if (customAttr.mType->mTypeDef->mName->ToString() == "ReflectAttribute") @@ -6563,6 +6677,14 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin if ((methodDef->mIsStatic) && ((methodReflectKind & BfReflectKind_StaticMethods) != 0)) includeMethod = true; + if (methodDef->mMethodType == BfMethodType_Ctor) + { + if ((methodReflectKind & BfReflectKind_Constructors) != 0) + includeMethod = true; + if ((methodDef->mParams.IsEmpty()) && ((methodReflectKind & BfReflectKind_DefaultConstructor) != 0)) + includeMethod = true; + } + if ((!includeMethod) && (typeOptions != NULL)) includeMethod = ApplyTypeOptionMethodFilters(includeMethod, methodDef, typeOptions); @@ -6617,7 +6739,13 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin BfMethodFlags methodFlags = defaultMethod->GetMethodFlags(); - int customAttrIdx = _HandleCustomAttrs(methodInfo.mMethodCustomAttributes); + int customAttrIdx = -1; + int returnCustomAttrIdx = -1; + if (methodInfo.mMethodCustomAttributes != NULL) + { + customAttrIdx = _HandleCustomAttrs(methodInfo.mMethodCustomAttributes->mCustomAttributes); + returnCustomAttrIdx = _HandleCustomAttrs(methodInfo.mMethodCustomAttributes->mReturnCustomAttributes); + } enum ParamFlags { @@ -6638,13 +6766,18 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin BfIRValue paramNameConst = GetStringObjectValue(paramName, !mIsComptimeModule); + int paramCustomAttrIdx = -1; + if ((methodInfo.mMethodCustomAttributes != NULL) && (paramIdx < (int)methodInfo.mMethodCustomAttributes->mParamCustomAttributes.mSize)) + paramCustomAttrIdx = _HandleCustomAttrs(methodInfo.mMethodCustomAttributes->mParamCustomAttributes[paramIdx]); + SizedArray paramDataVals = { emptyValueType, paramNameConst, GetConstValue(paramType->mTypeId, typeIdType), GetConstValue((int32)paramFlags, shortType), - GetConstValue(customAttrIdx, intType) // defaultIdx + GetConstValue(-1, intType), // defaultIdx + GetConstValue(paramCustomAttrIdx, intType) // customAttrIdx }; auto paramData = mBfIRBuilder->CreateConstAgg_Value(mBfIRBuilder->MapType(reflectParamDataType, BfIRPopulateType_Full), paramDataVals); paramVals.Add(paramData); @@ -6713,6 +6846,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin GetConstValue(methodIdx, intType), GetConstValue(vDataVal, intType), GetConstValue(customAttrIdx, intType), + GetConstValue(returnCustomAttrIdx, intType), }; auto methodData = mBfIRBuilder->CreateConstAgg_Value(mBfIRBuilder->MapTypeInst(reflectMethodDataType->ToTypeInstance(), BfIRPopulateType_Full), methodDataVals); methodTypes.push_back(methodData); @@ -6912,7 +7046,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin { auto genericTypeInstance = typeInstance->ToGenericTypeInstance(); auto reflectSpecializedGenericType = ResolveTypeDef(mCompiler->mReflectSpecializedGenericType); - auto unspecializedType = ResolveTypeDef(typeInstance->mTypeDef); + auto unspecializedType = ResolveTypeDef(typeInstance->mTypeDef->GetDefinition()); SizedArray resolvedTypes; for (auto typeGenericArg : genericTypeInstance->mGenericTypeInfo->mTypeGenericArguments) @@ -7083,7 +7217,7 @@ BfIRFunction BfModule::GetIntrinsic(BfMethodInstance* methodInstance, bool repor auto constant = methodOwner->mConstHolder->GetConstant(customAttribute.mCtorArgs[0]); String error; - if ((constant != NULL) && (constant->mTypeCode = BfTypeCode_StringId)) + if ((constant != NULL) && (constant->mTypeCode == BfTypeCode_StringId)) { int stringId = constant->mInt32; auto entry = mContext->mStringObjectIdMap[stringId]; @@ -7396,7 +7530,7 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar if (constraintType->IsVar()) { // From a `comptype` generic undef resolution. Ignore. - genericParamInstance->mGenericParamFlags |= BfGenericParamFlag_ComptypeExpr; + genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_ComptypeExpr); genericParamInstance->mComptypeConstraint.Add(constraintTypeRef); continue; } @@ -7458,7 +7592,7 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar if ((startingTypeConstraint != NULL) && (startingTypeConstraint->IsDelegate())) { // 'System.Delegate' means that we are expecting an actual delegate instance. Simulate this by wanting a class. - genericParamInstance->mGenericParamFlags |= BfGenericParamFlag_Class; + genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | BfGenericParamFlag_Class); } } else @@ -7483,8 +7617,8 @@ String BfModule::GenericParamSourceToString(const BfGenericParamSource & generic { if (genericParamSource.mMethodInstance != NULL) { - auto methodInst = GetUnspecializedMethodInstance(genericParamSource.mMethodInstance, false); - SetAndRestoreValue prevMethodInst(methodInst, NULL); + //auto methodInst = GetUnspecializedMethodInstance(genericParamSource.mMethodInstance, false); + //SetAndRestoreValue prevMethodInst(mCurMethodInstance, methodInst); return MethodToString(genericParamSource.mMethodInstance); } else @@ -7521,7 +7655,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if (origCheckArgType->IsRef()) origCheckArgType = origCheckArgType->GetUnderlyingType(); - bool argMayBeReferenceType = false; + bool argIsReferenceType = false; int checkGenericParamFlags = 0; if (checkArgType->IsGenericParam()) @@ -7531,18 +7665,18 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if (checkGenericParamInst->mTypeConstraint != NULL) checkArgType = checkGenericParamInst->mTypeConstraint; - if ((checkGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr)) != 0) - { - argMayBeReferenceType = false; - } - else - { - argMayBeReferenceType = true; - } +// if ((checkGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_StructPtr)) != 0) +// { +// argMayBeReferenceType = false; +// } +// else +// { +// argMayBeReferenceType = true; +// } } if (checkArgType->IsObjectOrInterface()) - argMayBeReferenceType = true; + argIsReferenceType = true; BfTypeInstance* typeConstraintInst = NULL; if (genericParamInst->mTypeConstraint != NULL) @@ -7567,7 +7701,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS } if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) && - ((checkGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Var)) == 0) && (!argMayBeReferenceType)) + ((checkGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Var)) == 0) && (!argIsReferenceType)) { if ((!ignoreErrors) && (PreFail())) *errorOut = Fail(StrFormat("The type '%s' must be a reference type in order to use it as parameter '%s' for '%s'", @@ -7615,7 +7749,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS *errorOut = Fail(StrFormat("The type '%s' must be a const value in order to use it as parameter '%s' for '%s'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; - } + } } else { @@ -7627,7 +7761,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS return false; } } - + if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Delete) != 0) { bool canDelete = false; @@ -7635,14 +7769,20 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS canDelete = true; else if (checkArgType->IsObjectOrInterface()) canDelete = true; - else if ((checkGenericParamFlags & BfGenericParamFlag_Delete) != 0) + else if ((checkGenericParamFlags & (BfGenericParamFlag_Delete | BfGenericParamFlag_Var)) != 0) canDelete = true; if (!canDelete) { if ((!ignoreErrors) && (PreFail())) - *errorOut = Fail(StrFormat("The type '%s' must be a deletable type in order to use it as parameter '%s' for '%s'", - TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); + { + if (checkArgType->IsGenericParam()) + *errorOut = Fail(StrFormat("Must add 'where %s : delete' constraint in order to use type as parameter '%s' for '%s'", + TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); + else + *errorOut = Fail(StrFormat("The type '%s' must be a deletable type in order to use it as parameter '%s' for '%s'", + TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); + } return false; } } @@ -7663,6 +7803,20 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS bool hadProtected = false; while (checkMethodDef != NULL) { + if (!checkMethodDef->mParams.IsEmpty()) + { + auto firstParam = checkMethodDef->mParams[0]; + if (((firstParam->mParamKind == BfParamKind_Params) || (firstParam->mParamKind == BfParamKind_AppendIdx) || (firstParam->mParamKind == BfParamKind_VarArgs)) || + ((firstParam->mParamDeclaration != NULL) && (firstParam->mParamDeclaration->mInitializer != NULL))) + { + // Allow all-default params + } + else + { + checkMethodDef = checkMethodDef->mNextWithSameName; + continue; + } + } if (checkMethodDef->mProtection == BfProtection_Public) { canAlloc = true; @@ -7677,7 +7831,11 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS canAlloc = TypeIsSubTypeOf(mCurTypeInstance, checkTypeInst, false); } } - else + else if (checkArgType->IsGenericParam()) + { + canAlloc = (checkGenericParamFlags & (BfGenericParamFlag_New | BfGenericParamFlag_Var)) != 0; + } + else if (checkArgType->IsPrimitiveType()) { // Any primitive types and stuff can be allocated canAlloc = true; @@ -7686,8 +7844,14 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if (!canAlloc) { if ((!ignoreErrors) && (PreFail())) - *errorOut = Fail(StrFormat("The type '%s' must have an accessible default constructor in order to use it as parameter '%s' for '%s'", - TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); + { + if (checkArgType->IsGenericParam()) + *errorOut = Fail(StrFormat("Must add 'where %s : new' constraint in order to use type as parameter '%s' for '%s'", + TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); + else + *errorOut = Fail(StrFormat("The type '%s' must have an accessible default constructor in order to use it as parameter '%s' for '%s'", + TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); + } return false; } } @@ -7796,7 +7960,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if (convCheckConstraint->IsGenericTypeInstance()) { auto convCheckConstraintInst = (BfTypeInstance*)convCheckConstraint; - if (convCheckConstraintInst->mTypeDef == mCompiler->mSizedArrayTypeDef) + if (convCheckConstraintInst->IsInstanceOf(mCompiler->mSizedArrayTypeDef)) { if (convCheckConstraintInst->mGenericTypeInfo->mTypeGenericArguments[0] == sizedArrayType->mElementType) { @@ -8104,11 +8268,15 @@ BF_NOINLINE void BfModule::EvaluateWithNewScope(BfExprEvaluator& exprEvaluator, BfScopeData newScope; newScope.mOuterIsConditional = true; newScope.mAllowTargeting = false; - mCurMethodState->AddScope(&newScope); - NewScopeState(true, false); + if (mCurMethodState != NULL) + { + mCurMethodState->AddScope(&newScope); + NewScopeState(true, false); + } exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | flags); exprEvaluator.Evaluate(expr, (flags & BfEvalExprFlags_PropogateNullConditional) != 0, (flags & BfEvalExprFlags_IgnoreNullConditional) != 0, (flags & BfEvalExprFlags_AllowSplat) != 0); - RestoreScopeState(); + if (mCurMethodState != NULL) + RestoreScopeState(); } BfTypedValue BfModule::CreateValueFromExpression(BfExprEvaluator& exprEvaluator, BfExpression* expr, BfType* wantTypeRef, BfEvalExprFlags flags, BfType** outOrigType) @@ -8375,7 +8543,17 @@ BfIRValue BfModule::AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTar else if (allocTarget.mCustomAllocator) { auto customTypeInst = allocTarget.mCustomAllocator.mType->ToTypeInstance(); - if (customTypeInst != NULL) + if (customTypeInst == NULL) + { + if (allocTarget.mCustomAllocator.mType->IsStructPtr()) + customTypeInst = allocTarget.mCustomAllocator.mType->GetUnderlyingType()->ToTypeInstance(); + } + + if (customTypeInst == NULL) + { + Fail(StrFormat("Type '%s' cannot be used as a custom allocator", TypeToString(allocTarget.mCustomAllocator.mType).c_str()), refNode); + } + else { BfTypedValueExpression typeValueExpr; String allocMethodName = "Alloc"; @@ -8819,13 +8997,23 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget auto allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(byteType), sizeValue); if (!isDynAlloc) mBfIRBuilder->ClearDebugLocation(allocaInst); + auto allocaBlock = mBfIRBuilder->GetInsertBlock(); mBfIRBuilder->SetAllocaAlignment(allocaInst, allocAlign); + + BfTypedValue typedVal; if (!isDynAlloc) + { + mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock); + typedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapType(arrayType)), arrayType); + mBfIRBuilder->ClearDebugLocation_Last(); mBfIRBuilder->SetInsertPoint(prevBlock); - auto typedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapType(arrayType)), arrayType); - mBfIRBuilder->ClearDebugLocation_Last(); + allocaBlock = mCurMethodState->mIRInitBlock; + } + else + typedVal = BfTypedValue(mBfIRBuilder->CreateBitCast(allocaInst, mBfIRBuilder->MapType(arrayType)), arrayType); + if (!noDtorCall) - AddStackAlloc(typedVal, BfIRValue(), NULL, scopeData, false, true); + AddStackAlloc(typedVal, BfIRValue(), NULL, scopeData, false, true, allocaBlock); InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue); return typedVal.mValue; } @@ -9426,10 +9614,10 @@ bool BfModule::WantsLifetimes() bool BfModule::HasCompiledOutput() { - return (!mSystem->mIsResolveOnly) && (mIsReified) && (!mIsComptimeModule); + return (!mSystem->mIsResolveOnly) && (mGeneratesCode) && (!mIsComptimeModule); } -// We will skip the object access check for any occurances of this value +// We will skip the object access check for any occurrences of this value void BfModule::SkipObjectAccessCheck(BfTypedValue typedVal) { if ((mBfIRBuilder->mIgnoreWrites) || (!typedVal.mType->IsObjectOrInterface()) || (mCurMethodState == NULL) || (mCurMethodState->mIgnoreObjectAccessCheck)) @@ -9498,17 +9686,18 @@ void BfModule::EmitDynamicCastCheck(const BfTypedValue& targetValue, BfType* tar auto irb = mBfIRBuilder; + auto checkBB = irb->CreateBlock("as.check"); + auto isNull = irb->CreateIsNull(targetValue.mValue); + mBfIRBuilder->CreateCondBr(isNull, nullSucceeds ? trueBlock : falseBlock, checkBB); + if (mIsComptimeModule) { + AddBasicBlock(checkBB); auto callResult = mBfIRBuilder->Comptime_DynamicCastCheck(targetValue.mValue, targetType->mTypeId, mBfIRBuilder->MapType(mContext->mBfObjectType)); auto cmpResult = mBfIRBuilder->CreateCmpNE(callResult, GetDefaultValue(mContext->mBfObjectType)); irb->CreateCondBr(cmpResult, trueBlock, falseBlock); return; - } - - auto checkBB = irb->CreateBlock("as.check"); - auto isNull = irb->CreateIsNull(targetValue.mValue); - mBfIRBuilder->CreateCondBr(isNull, nullSucceeds ? trueBlock : falseBlock, checkBB); + } auto intType = GetPrimitiveType(BfTypeCode_IntPtr); auto intPtrType = CreatePointerType(intType); @@ -9966,8 +10155,10 @@ BfMethodInstance* BfModule::GetUnspecializedMethodInstance(BfMethodInstance* met if (methodInstance->mMethodDef->mIsLocalMethod) return methodInstance; + if (methodInstance->mMethodDef->mDeclaringType->IsEmitted()) + return methodInstance; - auto unspecializedType = ResolveTypeDef(genericType->mTypeDef); + auto unspecializedType = ResolveTypeDef(genericType->mTypeDef->GetDefinition()); if (unspecializedType == NULL) { AssertErrorState(); @@ -10224,7 +10415,7 @@ BfOperatorInfo* BfModule::GetOperatorInfo(BfTypeInstance* typeInstance, BfOperat SetAndRestoreValue prevMethodInstance(mCurMethodInstance, NULL); BfTypeState typeState; - typeState.mTypeInstance = typeInstance; + typeState.mType = typeInstance; typeState.mCurTypeDef = operatorDef->mDeclaringType; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -10823,6 +11014,26 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con return CreateTypeDataRef(constTypeOf->mType); } + if (constant->mConstType == BfConstType_PtrToInt) + { + auto fromPtrToInt = (BfConstantPtrToInt*)constant; + auto fromTarget = constHolder->GetConstantById(fromPtrToInt->mTarget); + return mBfIRBuilder->CreatePtrToInt(ConstantToCurrent(fromTarget, constHolder, NULL), fromPtrToInt->mToTypeCode); + } + + if (constant->mConstType == BfConstType_IntToPtr) + { + auto fromPtrToInt = (BfConstantIntToPtr*)constant; + auto fromTarget = constHolder->GetConstantById(fromPtrToInt->mTarget); + BfIRType toIRType = fromPtrToInt->mToType; + if (toIRType.mKind == BfIRTypeData::TypeKind_TypeId) + { + auto toType = mContext->mTypes[toIRType.mId]; + toIRType = mBfIRBuilder->MapType(toType); + } + return mBfIRBuilder->CreateIntToPtr(ConstantToCurrent(fromTarget, constHolder, NULL), toIRType); + } + if (constant->mConstType == BfConstType_Agg) { auto constArray = (BfConstantAgg*)constant; @@ -10976,7 +11187,6 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri // We solve it by having a 'bypass' for known attributes that Object depends on if ((attributesDirective->mArguments.empty()) && (autoComplete == NULL) && (attrType != NULL) && (attrType->IsTypeInstance())) { - //if (attrTypeDef == mCompiler->mCReprAttributeTypeDef) if (attrType->IsInstanceOf(mCompiler->mCReprAttributeTypeDef)) { for (auto methodDef : attrTypeDef->mMethods) @@ -11013,8 +11223,10 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri BfTypeInstance* attrTypeInst = NULL; if (attrType == NULL) continue; - mContext->mUnreifiedModule->PopulateType(attrType, BfPopulateType_DataAndMethods); attrTypeInst = attrType->ToTypeInstance(); + if ((attrTypeInst != NULL) && (attrTypeInst->mDefineState != BfTypeDefineState_DefinedAndMethodsSlotting)) + mContext->mUnreifiedModule->PopulateType(attrType, BfPopulateType_DataAndMethods); + if ((attrTypeInst == NULL) || (!TypeIsSubTypeOf(attrTypeInst, baseAttrTypeInst)) || (attrTypeInst->mAttributeData == NULL)) { Fail(StrFormat("'%s' is not an attribute class", TypeToString(attrType).c_str()), attributesDirective->mAttributeTypeRef); //CS0616 @@ -11332,14 +11544,13 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri { Fail(StrFormat("'%s' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are '%s'. All attributes in this block will be ignored.", GetAttributesTargetListString(targetOverride).c_str(), GetAttributesTargetListString(attrTarget).c_str()), attributesDirective->mAttributeTargetSpecifier); // CS0657 - } - - success = false; + success = false; + } } if ((success) && (targetOverride != (BfAttributeTargets)0)) { - if ((targetOverride == BfAttributeTargets_ReturnValue) && (attrTarget == BfAttributeTargets_Method)) + if ((mCurMethodInstance != NULL) && (targetOverride == BfAttributeTargets_ReturnValue) && (attrTarget == BfAttributeTargets_Method)) { auto methodInfoEx = mCurMethodInstance->GetMethodInfoEx(); @@ -11352,11 +11563,9 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri methodInfoEx->mMethodCustomAttributes->mReturnCustomAttributes->mAttributes.push_back(customAttribute); } } - else - { - // Failed - ignore - success = false; - } + + // Mark as failed since we don't actually want to add this to the custom attributes set + success = false; } if (success) @@ -11367,7 +11576,7 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri { if (prevCustomAttribute.mType == attrTypeInst) { - Fail(StrFormat("Duplicate '%s' attribute", attributesDirective->mAttributeTypeRef->ToString().c_str()), attributesDirective->mAttributeTypeRef); // CS0579 + Fail(StrFormat("Duplicate '%s' attribute", attributesDirective->mAttributeTypeRef->ToCleanAttributeString().c_str()), attributesDirective->mAttributeTypeRef); // CS0579 } } } @@ -11394,6 +11603,8 @@ BfCustomAttributes* BfModule::GetCustomAttributes(BfTypeDef* typeDef) BfAttributeTargets attrTarget; if (typeDef->mIsDelegate) attrTarget = BfAttributeTargets_Delegate; + else if (typeDef->mIsFunction) + attrTarget = BfAttributeTargets_Function; else if (typeDef->mTypeCode == BfTypeCode_Enum) attrTarget = BfAttributeTargets_Enum; else if (typeDef->mTypeCode == BfTypeCode_Interface) @@ -11496,7 +11707,7 @@ void BfModule::ProcessCustomAttributeData() auto checkTypeInst = mCurTypeInstance->mBaseType; while (checkTypeInst != NULL) { - if (checkTypeInst->mTypeDef == mCompiler->mAttributeTypeDef) + if (checkTypeInst->IsInstanceOf(mCompiler->mAttributeTypeDef)) isAttribute = true; checkTypeInst = checkTypeInst->mBaseType; } @@ -11510,7 +11721,7 @@ void BfModule::ProcessCustomAttributeData() { for (auto& customAttribute : mCurTypeInstance->mCustomAttributes->mAttributes) { - if (customAttribute.mType->mTypeDef == mCompiler->mAttributeUsageAttributeTypeDef) + if (customAttribute.mType->IsInstanceOf(mCompiler->mAttributeUsageAttributeTypeDef)) { if (customAttribute.mCtorArgs.size() > 0) { @@ -11969,16 +12180,22 @@ BfTypedValue BfModule::MakeAddressable(BfTypedValue typedVal, bool forceMutable) wasReadOnly = true; // Any non-addr is implicitly read-only //static int gCallIdx = 0; - + FixValueActualization(typedVal); if (typedVal.IsAddr()) return typedVal; BfType* type = typedVal.mType; PopulateType(type); - auto tempVar = CreateAlloca(type); - if (typedVal.IsSplat()) - AggregateSplatIntoAddr(typedVal, tempVar); + BfIRValue tempVar; + if (typedVal.mValue.IsFake()) + tempVar = mBfIRBuilder->GetFakeVal(); else - mBfIRBuilder->CreateAlignedStore(typedVal.mValue, tempVar, type->mAlign); + { + tempVar = CreateAlloca(type); + if (typedVal.IsSplat()) + AggregateSplatIntoAddr(typedVal, tempVar); + else + mBfIRBuilder->CreateAlignedStore(typedVal.mValue, tempVar, type->mAlign); + } if (forceMutable) wasReadOnly = false; @@ -12536,6 +12753,9 @@ BfModuleMethodInstance BfModule::ReferenceExternalMethodInstance(BfMethodInstanc } bool isInlined = (methodInstance->mAlwaysInline) || ((flags & BfGetMethodInstanceFlag_ForceInline) != 0); + if ((methodInstance->mIsIntrinsic) || (methodInstance->mMethodDef->mIsExtern)) + isInlined = false; + BfMethodRef methodRef = methodInstance; if (isInlined) methodRef.mMethodRefFlags = (BfMethodRefFlags)(methodRef.mMethodRefFlags | BfMethodRefFlag_AlwaysInclude); @@ -12710,6 +12930,9 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM flags = (BfGetMethodInstanceFlags)(flags & ~BfGetMethodInstanceFlag_ForceInline); } + if (methodDef->mIsExtern) + flags = (BfGetMethodInstanceFlags)(flags & ~BfGetMethodInstanceFlag_ForceInline); + bool processNow = false; bool keepInCurrentModule = false; @@ -12803,7 +13026,7 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM { if ((!mIsComptimeModule) && (mIsReified) && (!instModule->mIsReified)) { - if (!typeInst->IsUnspecializedTypeVariation()) + if (!typeInst->IsUnspecializedType()) { if (!instModule->mReifyQueued) { @@ -12887,7 +13110,9 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM bool isExternalExtensionMethod = false; if ((!typeInst->IsUnspecializedType()) && (!isUnspecializedPass)) { - if (((flags & BfGetMethodInstanceFlag_ForeignMethodDef) == 0) && (methodDef->mDeclaringType->mProject != typeInst->mTypeDef->mProject)) + if (((flags & BfGetMethodInstanceFlag_ForeignMethodDef) == 0) && + (methodDef->mDeclaringType != NULL) && + (methodDef->mDeclaringType->mProject != typeInst->mTypeDef->mProject)) { auto specProject = methodDef->mDeclaringType->mProject; @@ -13047,9 +13272,9 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM { MarkDerivedDirty(typeInst); - if (mIsScratchModule) + if (!HasCompiledOutput()) { - BfLogSysM("Marking scratch module method instance as reified: %p\n", methodInstance); + BfLogSysM("Marking non-compiled-output module method instance as reified: %p\n", methodInstance); _SetReified(); CheckHotMethod(methodInstance, ""); } @@ -13254,6 +13479,8 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM // We need to refer to a function that was defined in a prior module bool isInlined = (methodInstance->mAlwaysInline) || ((flags & BfGetMethodInstanceFlag_ForceInline) != 0); + if (methodInstance->mIsIntrinsic) + isInlined = false; methodInstance->mIRFunction = CreateFunctionFrom(methodInstance, false, isInlined); BF_ASSERT((methodInstance->mDeclModule == this) || (methodInstance->mDeclModule == mContext->mUnreifiedModule) || (methodInstance->mDeclModule == NULL)); methodInstance->mDeclModule = this; @@ -13384,7 +13611,7 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM methodInstance->GetMethodInfoEx()->mForeignType = foreignType; } - if ((typeInst->mTypeDef == mCompiler->mValueTypeTypeDef) && (methodDef->mName == BF_METHODNAME_EQUALS)) + if ((typeInst->IsInstanceOf(mCompiler->mValueTypeTypeDef)) && (methodDef->mName == BF_METHODNAME_EQUALS)) { if (!lookupMethodGenericArguments.empty()) { @@ -13567,8 +13794,11 @@ void BfModule::SetupMethodIdHash(BfMethodInstance* methodInstance) if (methodInstance->mMethodDef->mIsLocalMethod) { auto outmostMethodInstance = mCurMethodState->GetRootMethodState()->mMethodInstance; - BF_ASSERT((outmostMethodInstance->mIdHash != 0) || (outmostMethodInstance->mIsAutocompleteMethod)); - hashCtx.Mixin(outmostMethodInstance->mIdHash); + if (outmostMethodInstance != NULL) + { + BF_ASSERT((outmostMethodInstance->mIdHash != 0) || (outmostMethodInstance->mIsAutocompleteMethod)); + hashCtx.Mixin(outmostMethodInstance->mIdHash); + } } methodInstance->mIdHash = (int64)hashCtx.Finish64(); @@ -13655,6 +13885,10 @@ BfTypedValue BfModule::GetCompilerFieldValue(const StringImpl& str) if (mProject != NULL) return BfTypedValue(GetStringObjectValue(mProject->mName), ResolveTypeDef(mCompiler->mStringTypeDef)); } + if (str == "#AllocStackCount") + { + return BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Int32, mCompiler->mOptions.mAllocStackCount), GetPrimitiveType(BfTypeCode_Int32)); + } if (mCurMethodState->mMixinState != NULL) { @@ -13947,8 +14181,11 @@ BfTypedValue BfModule::GetThis() return BfTypedValue(GetDefaultValue(thisType), thisType, BfTypedValueKind_ThisValue); } } - - auto localDef = useMethodState->mLocals[0]; + + if (mCurMethodInstance == NULL) + return BfTypedValue(); + + auto localDef = useMethodState->mLocals[0]; auto curMethodOwner = mCurMethodInstance->mMethodInstanceGroup->mOwner; if ((curMethodOwner->IsStruct()) || (curMethodOwner->IsTypedPrimitive())) { @@ -14007,8 +14244,10 @@ bool BfModule::IsInSpecializedSection() bool BfModule::IsInUnspecializedGeneric() { - if (mCurMethodInstance != NULL) - return mCurMethodInstance->mIsUnspecialized; + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized)) + return true; + if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsUnspecializedType())) + return true; return false; } @@ -14432,6 +14671,8 @@ BfTypeOptions* BfModule::GetTypeOptions() { if ((mCurMethodState != NULL) && (mCurMethodState->mMethodTypeOptions != NULL)) return mCurMethodState->mMethodTypeOptions; + if (mCurMethodInstance == NULL) + return NULL; return mSystem->GetTypeOptions(mCurTypeInstance->mTypeOptionsIdx); } @@ -14571,6 +14812,8 @@ void BfModule::EmitDeferredScopeCalls(bool useSrcPositions, BfScopeData* scopeDa { if (deferredCallEntry->mDeferredBlock != NULL) { + SetAndRestoreValue prevMixinState(mCurMethodState->mMixinState, checkScope->mMixinState); + if (checkScope == &mCurMethodState->mHeadScope) CreateRetValLocal(); @@ -14731,6 +14974,7 @@ void BfModule::EmitDeferredScopeCalls(bool useSrcPositions, BfScopeData* scopeDa deferredCallEmitState.mCloseNode = deferCloseNode; SetAndRestoreValue prevDeferredCallEmitState(mCurMethodState->mDeferredCallEmitState, &deferredCallEmitState); SetAndRestoreValue prevIgnoredWrites(mBfIRBuilder->mIgnoreWrites, deferredCallEntry->mIgnored); + SetAndRestoreValue prevMixinState(mCurMethodState->mMixinState, checkScope->mMixinState); if (deferCloseNode != NULL) { @@ -14946,12 +15190,15 @@ void BfModule::EmitReturn(const BfTypedValue& val) } else if (mIsComptimeModule) { - mBfIRBuilder->CreateSetRet(val.mValue, val.mType->mTypeId); + if (!val.mType->IsValuelessType()) + mBfIRBuilder->CreateSetRet(val.mValue, val.mType->mTypeId); + else + mBfIRBuilder->CreateSetRet(BfIRValue(), val.mType->mTypeId); } else { // Just ignore - BF_ASSERT(mCurMethodInstance->mReturnType->IsVar()); + BF_ASSERT(mCurMethodInstance->mReturnType->IsVar()); } } } @@ -14960,13 +15207,9 @@ void BfModule::EmitReturn(const BfTypedValue& val) else { EmitDeferredScopeCalls(false, NULL); - if (val) { - if (mCurMethodInstance->mReturnType->IsValuelessType()) - mBfIRBuilder->CreateRetVoid(); - else - mBfIRBuilder->CreateRet(val.mValue); + BF_ASSERT(mBfIRBuilder->mIgnoreWrites); } } @@ -14985,7 +15228,7 @@ void BfModule::EmitDefaultReturn() } else { - if (mCurMethodInstance->mReturnType->IsVar()) + if ((mCurMethodInstance == NULL) || (mCurMethodInstance->mReturnType->IsVar())) { // Ignore } @@ -15095,6 +15338,7 @@ void BfModule::CreateDelegateInvokeMethod() SizedArray origParamTypes; BfIRType origReturnType; + BfIRType staticReturnType; mCurMethodInstance->GetIRFunctionInfo(this, origReturnType, origParamTypes); if (mCurMethodInstance->mReturnType->IsValueType()) @@ -15106,15 +15350,17 @@ void BfModule::CreateDelegateInvokeMethod() int thisIdx = 0; if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1)) { - thisIdx = mCurMethodInstance->GetStructRetIdx() ^ 1; - staticFuncArgs.push_back(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx())); + thisIdx = mCurMethodInstance->GetStructRetIdx() ^ 1; memberFuncArgs.push_back(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx())); } + if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx(true) != -1)) + staticFuncArgs.push_back(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx())); + if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() == 0)) memberFuncArgs.push_back(BfIRValue()); // Push 'target' - mCurMethodInstance->GetIRFunctionInfo(this, origReturnType, staticParamTypes, true); + mCurMethodInstance->GetIRFunctionInfo(this, staticReturnType, staticParamTypes, true); for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++) { @@ -15123,7 +15369,7 @@ void BfModule::CreateDelegateInvokeMethod() exprEvaluator.PushArg(localVal, memberFuncArgs); } - auto staticFunc = mBfIRBuilder->CreateFunctionType(origReturnType, staticParamTypes, false); + auto staticFunc = mBfIRBuilder->CreateFunctionType(staticReturnType, staticParamTypes, false); auto staticFuncPtr = mBfIRBuilder->GetPointerTo(staticFunc); auto staticFuncPtrPtr = mBfIRBuilder->GetPointerTo(staticFuncPtr); @@ -15168,11 +15414,20 @@ void BfModule::CreateDelegateInvokeMethod() auto funcPtrPtr = mBfIRBuilder->CreateBitCast(fieldPtr, staticFuncPtrPtr); auto funcPtr = mBfIRBuilder->CreateLoad(funcPtrPtr); staticResult = mBfIRBuilder->CreateCall(funcPtr, staticFuncArgs); - if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1)) + if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx(true) != -1)) { // Note: since this is a forced static invocation, we know the sret will be the first parameter mBfIRBuilder->Call_AddAttribute(staticResult, 0 + 1, BfIRAttribute_StructRet); } + + // We had a sret for the non-static but no sret for the static (because we have a lowered return type there) + if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1) && (mCurMethodInstance->GetStructRetIdx(true) == -1)) + { + auto sretToType = mBfIRBuilder->GetPointerTo(staticReturnType); + auto sretCastedPtr = mBfIRBuilder->CreateBitCast(mBfIRBuilder->GetArgument(mCurMethodInstance->GetStructRetIdx()), sretToType); + mBfIRBuilder->CreateStore(staticResult, sretCastedPtr); + } + if (callingConv == BfIRCallingConv_ThisCall) callingConv = BfIRCallingConv_CDecl; if (callingConv != BfIRCallingConv_CDecl) @@ -15646,7 +15901,7 @@ void BfModule::CreateStaticCtor() BfType* wantType = NULL; if ((!BfNodeIsA(fieldDef->mTypeRef)) && (!BfNodeIsA(fieldDef->mTypeRef))) { - wantType = ResolveTypeRef(fieldDef->mTypeRef); + wantType = ResolveTypeRef(fieldDef->mTypeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowInferredSizedArray); } CreateValueFromExpression(fieldDef->mInitializer, wantType, BfEvalExprFlags_FieldInitializer); } @@ -15662,6 +15917,9 @@ void BfModule::CreateStaticCtor() void BfModule::EmitDtorBody() { + if (!mCurMethodState->mIRExitBlock) + mCurMethodState->mIRExitBlock = mBfIRBuilder->CreateBlock("exit", true); + if (mCurTypeInstance->IsClosure()) { BfFieldInstance* fieldInstance = &mCurTypeInstance->mFieldInstances.back(); @@ -15701,7 +15959,7 @@ void BfModule::EmitDtorBody() if (auto bodyBlock = BfNodeDynCast(methodDef->mBody)) { - VisitEmbeddedStatement(bodyBlock); + VisitEmbeddedStatement(bodyBlock); if (bodyBlock->mCloseBrace != NULL) { UpdateSrcPos(bodyBlock->mCloseBrace); @@ -16101,7 +16359,10 @@ void BfModule::SetupIRMethod(BfMethodInstance* methodInstance, BfIRFunction func if (methodInstance->mReturnType->IsVar()) mBfIRBuilder->Func_AddAttribute(func, -1, BfIRAttribute_VarRet); if (methodDef->mImportKind == BfImportKind_Export) - mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_DllExport); + { + if (methodDef->mDeclaringType->mProject->mTargetType != BfTargetType_BeefLib_StaticLib) + mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_DllExport); + } if (methodDef->mIsNoReturn) mBfIRBuilder->Func_AddAttribute(func, -1, BfIRAttribute_NoReturn); auto callingConv = GetIRCallingConvention(methodInstance); @@ -16217,9 +16478,9 @@ void BfModule::SetupIRMethod(BfMethodInstance* methodInstance, BfIRFunction func PopulateType(resolvedTypeRef, BfPopulateType_Data); addDeref = resolvedTypeRef->mSize; } - else if ((methodInstance->WantsStructsAttribByVal()) && (!resolvedTypeRef->IsSizedArray())) - { - mBfIRBuilder->PopulateType(resolvedTypeRef); + else if (methodInstance->WantsStructsAttribByVal(resolvedTypeRef)) + { + mBfIRBuilder->PopulateType(resolvedTypeRef, BfIRPopulateType_Full); BF_ASSERT(resolvedTypeRef->mAlign > 0); mBfIRBuilder->Func_AddAttribute(func, argIdx + 1, BfIRAttribute_ByVal, mSystem->mPtrSize); } @@ -16324,7 +16585,7 @@ void BfModule::EmitInitBlocks(const std::function& initBlockCa for (; initMethodDef != NULL; initMethodDef = initMethodDef->mNextWithSameName) { - if (initMethodDef->mDeclaringType != methodDef->mDeclaringType) + if (initMethodDef->mDeclaringType->GetDefinition() != methodDef->mDeclaringType->GetDefinition()) continue; if (initMethodDef->mMethodType != BfMethodType_Init) continue; @@ -16418,6 +16679,12 @@ void BfModule::EmitCtorBody(bool& skipBody) bool calledCtorNoBody = false; + if ((mCurTypeInstance->IsTypedPrimitive()) && (!mCurTypeInstance->IsValuelessType())) + { + // Zero out typed primitives in ctor + mBfIRBuilder->CreateStore(GetDefaultValue(mCurTypeInstance->GetUnderlyingType()), mBfIRBuilder->GetArgument(0)); + } + if ((!mCurTypeInstance->IsBoxed()) && (methodDef->mMethodType == BfMethodType_Ctor) && (!hadThisInitializer)) { // Call the root type's default ctor (with no body) to initialize its fields and call the chained ctors @@ -16633,7 +16900,7 @@ void BfModule::EmitCtorBody(bool& skipBody) mCompiler->mResolvePassData->mSourceClassifier->SetElementType(fieldDef->mInitializer, BfSourceElementType_Normal); mCompiler->mResolvePassData->mSourceClassifier->VisitChild(fieldDef->mInitializer); - auto wantType = ResolveTypeRef(fieldDef->mTypeRef); + auto wantType = ResolveTypeRef(fieldDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowInferredSizedArray); if ((wantType != NULL) && ((wantType->IsVar()) || (wantType->IsLet()) || (wantType->IsRef()))) wantType = NULL; @@ -16879,7 +17146,7 @@ void BfModule::EmitCtorBody(bool& skipBody) else autoComplete->mIsCapturingMethodMatchInfo = false; } - } + } } void BfModule::EmitEnumToStringBody() @@ -17165,8 +17432,8 @@ void BfModule::EmitIteratorBlock(bool& skipBody) auto retTypeInst = mCurMethodInstance->mReturnType->ToGenericTypeInstance(); if (retTypeInst != NULL) { - if ((retTypeInst->mTypeDef == mCompiler->mGenericIEnumerableTypeDef) || - (retTypeInst->mTypeDef == mCompiler->mGenericIEnumeratorTypeDef)) + if ((retTypeInst->IsInstanceOf(mCompiler->mGenericIEnumerableTypeDef)) || + (retTypeInst->IsInstanceOf(mCompiler->mGenericIEnumeratorTypeDef))) { innerRetType = retTypeInst->mGenericTypeInfo->mTypeGenericArguments[0]; } @@ -17441,7 +17708,9 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp prevIgnoreErrors.Restore(); PopulateType(resolvedType, BfPopulateType_Declaration); paramVar->mResolvedType = resolvedType; - paramVar->mName = methodInstance->GetParamName(paramIdx); + int namePrefixCount = 0; + paramVar->mName = methodInstance->GetParamName(paramIdx, namePrefixCount); + paramVar->mNamePrefixCount = (uint8)namePrefixCount; paramVar->mNameNode = methodInstance->GetParamNameNode(paramIdx); if (!isParamSkipped) { @@ -17564,6 +17833,7 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp auto paramInst = &methodInstance->mParams[paramIdx]; auto paramDef = methodDef->mParams[paramInst->mParamDefIdx]; localVar->mName = paramDef->mName; + localVar->mNamePrefixCount = paramDef->mNamePrefixCount; localVar->mResolvedType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam); localVar->mCompositeCount = 0; DoAddLocalVariable(localVar); @@ -17613,7 +17883,7 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance(); if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || ((typeInstConstraint != NULL) && - ((typeInstConstraint->mTypeDef == mCompiler->mDelegateTypeDef) || (typeInstConstraint->mTypeDef == mCompiler->mFunctionTypeDef)))) + ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef))))) { BfLocalVariable* localVar = new BfLocalVariable(); localVar->mName = paramDef->mName; @@ -18514,7 +18784,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) methodState.mGenericTypeBindings = &methodInstance->GetMethodInfoEx()->mGenericTypeBindings; } else if ((((methodInstance->mMethodInfoEx != NULL) && ((int)methodInstance->mMethodInfoEx->mMethodGenericArguments.size() > dependentGenericStartIdx)) || - ((mCurTypeInstance->IsGenericTypeInstance()) && (!isGenericVariation) && (!methodInstance->mMethodDef->mIsLocalMethod)))) + ((mCurTypeInstance->IsGenericTypeInstance()) && (!isGenericVariation) && (!methodInstance->mMethodDef->mIsLocalMethod) && (!methodInstance->mMethodDef->mDeclaringType->IsEmitted())))) { unspecializedMethodInstance = GetUnspecializedMethodInstance(methodInstance, !methodInstance->mMethodDef->mIsLocalMethod); @@ -18586,17 +18856,20 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) { if (methodDef->mIsStatic) { - auto checkParam0 = mCurMethodInstance->GetParamType(0); - if ((checkParam0->IsRef()) && (!checkParam0->IsOut())) - checkParam0 = checkParam0->GetUnderlyingType(); - if (methodDef->mParams.size() != 1) { Fail("Unary operators must declare one parameter", paramErrorRefNode); } - else if ((checkParam0 != mCurTypeInstance) && (!checkParam0->IsSelf())) + else { - Fail("The parameter of a unary operator must be the containing type", paramErrorRefNode); + auto checkParam0 = mCurMethodInstance->GetParamType(0); + if ((checkParam0->IsRef()) && (!checkParam0->IsOut())) + checkParam0 = checkParam0->GetUnderlyingType(); + + if ((checkParam0 != mCurTypeInstance) && (!checkParam0->IsSelf())) + { + Fail("The parameter of a unary operator must be the containing type", paramErrorRefNode); + } } if (((operatorDef->mOperatorDeclaration->mUnaryOp == BfUnaryOp_Increment) || @@ -19471,7 +19744,12 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) } else { - BF_ASSERT(innerMethodInstance.mMethodInstance->mMethodDef == methodDef); + BF_ASSERT(!innerMethodInstance.mMethodInstance->mMethodDef->mDeclaringType->IsEmitted()); + auto innerMethodDef = innerMethodInstance.mMethodInstance->mMethodDef; + if (innerType->mTypeDef->IsEmitted()) + innerMethodDef = innerType->mTypeDef->mEmitParent->mMethods[innerMethodDef->mIdx]; + + BF_ASSERT(innerMethodDef == methodDef); SizedArray innerParams; BfExprEvaluator exprEvaluator(this); @@ -19504,14 +19782,14 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) mBfIRBuilder->PopulateType(methodInstance->mReturnType); auto returnType = BfTypedValue(mBfIRBuilder->GetArgument(methodInstance->GetStructRetIdx()), methodInstance->mReturnType, true); exprEvaluator.mReceivingValue = &returnType; - auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, true); + auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, BfCreateCallFlags_TailCall); BF_ASSERT(exprEvaluator.mReceivingValue == NULL); // Ensure it was actually used mBfIRBuilder->CreateRetVoid(); } else { mBfIRBuilder->PopulateType(methodInstance->mReturnType); - auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, true); + auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, BfCreateCallFlags_TailCall); if (mCurMethodInstance->mReturnType->IsValueType()) retVal = LoadValue(retVal); CreateReturn(retVal.mValue); @@ -19734,7 +20012,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) skipBody = true; skipEndChecks = true; } - else if ((methodDef->mName == BF_METHODNAME_EQUALS) && (typeDef == mCompiler->mValueTypeTypeDef)) + else if ((methodDef->mName == BF_METHODNAME_EQUALS) && (typeDef->GetDefinition() == mCompiler->mValueTypeTypeDef)) { CreateValueTypeEqualsMethod(false); skipBody = true; @@ -19797,7 +20075,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) } else if (methodDef->mMethodType == BfMethodType_PropertySetter) { - if ((!methodDef->mIsMutating) && (mCurTypeInstance->IsValueType())) + if ((!methodDef->mIsMutating) && (!methodDef->mIsStatic) && (mCurTypeInstance->IsValueType())) { Fail("Auto-setter must be marked as 'mut'", methodDef->GetRefNode(), true); } @@ -20050,7 +20328,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) } if (mCurMethodState->mIRExitBlock) - { + { for (auto preExitBlock : mCurMethodState->mHeadScope.mAtEndBlocks) mBfIRBuilder->MoveBlockToEnd(preExitBlock); @@ -20294,7 +20572,7 @@ String BfModule::GetLocalMethodName(const StringImpl& baseName, BfAstNode* ancho } auto rootMethodState = mCurMethodState->GetRootMethodState(); - if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL) + if ((rootMethodState != NULL) && (rootMethodState->mMethodInstance != NULL) && (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)) { for (auto methodGenericArg : rootMethodState->mMethodInstance->mMethodInfoEx->mMethodGenericArguments) { @@ -20352,7 +20630,7 @@ BfMethodDef* BfModule::GetLocalMethodDef(BfLocalMethod* localMethod) BfMethodDef* outerMethodDef = NULL; if (localMethod->mOuterLocalMethod != NULL) outerMethodDef = localMethod->mOuterLocalMethod->mMethodDef; - else + else if (rootMethodState->mMethodInstance != NULL) outerMethodDef = rootMethodState->mMethodInstance->mMethodDef; if (methodDeclaration != NULL) @@ -20361,7 +20639,7 @@ BfMethodDef* BfModule::GetLocalMethodDef(BfLocalMethod* localMethod) defBuilder.mCurSource = localMethod->mMethodDeclaration->GetParser(); defBuilder.mPassInstance = mCompiler->mPassInstance; defBuilder.mCurTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType; - + defBuilder.mCurDeclaringTypeDef = defBuilder.mCurTypeDef; methodDef = defBuilder.CreateMethodDef(methodDeclaration, outerMethodDef); } else @@ -20483,14 +20761,14 @@ BfModuleMethodInstance BfModule::GetLocalMethodInstance(BfLocalMethod* localMeth // Ignore the outermost method's generic method arguments for the purpose of determining if we are the 'default' (ie: unspecialized) // version of this method for this pass through the outermost method int dependentGenericStartIdx = 0; - if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL) + if ((rootMethodState->mMethodInstance != NULL) && (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)) dependentGenericStartIdx = (int)rootMethodState->mMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); BfMethodInstance* outerMethodInstance = mCurMethodInstance; if (methodGenericArguments.size() == 0) { - if (rootMethodState->mMethodInstance->mMethodInfoEx != NULL) + if ((rootMethodState->mMethodInstance != NULL) && (rootMethodState->mMethodInstance->mMethodInfoEx != NULL)) sanitizedMethodGenericArguments = rootMethodState->mMethodInstance->mMethodInfoEx->mMethodGenericArguments; } else @@ -21089,8 +21367,9 @@ BfModuleMethodInstance BfModule::GetLocalMethodInstance(BfLocalMethod* localMeth if (CompareMethodSignatures(localMethod->mMethodInstanceGroup->mDefault, checkMethodInstance)) { - auto bfError = Fail("Method already declared with the same parameter types", methodInstance->mMethodDef->GetRefNode(), true); - if (bfError != NULL) + String error = "Method already declared with the same parameter types"; + auto bfError = Fail(error, methodInstance->mMethodDef->GetRefNode(), true); + if ((bfError != NULL) && (methodInstance->mMethodDef->GetRefNode() != checkMethodInstance->mMethodDef->GetRefNode())) mCompiler->mPassInstance->MoreInfo("First declaration", checkMethodInstance->mMethodDef->GetRefNode()); } } @@ -21575,11 +21854,14 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool // over the local method each time auto rootMethodInstance = prevMethodState.mPrevVal->GetRootMethodState()->mMethodInstance; dependentGenericStartIdx = 0; - if (rootMethodInstance->mMethodInfoEx != NULL) - dependentGenericStartIdx = (int)rootMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); + if (rootMethodInstance != NULL) + { + if (rootMethodInstance->mMethodInfoEx != NULL) + dependentGenericStartIdx = (int)rootMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); - methodInstance->mIsUnspecialized = rootMethodInstance->mIsUnspecialized; - methodInstance->mIsUnspecializedVariation = rootMethodInstance->mIsUnspecializedVariation; + methodInstance->mIsUnspecialized = rootMethodInstance->mIsUnspecialized; + methodInstance->mIsUnspecializedVariation = rootMethodInstance->mIsUnspecializedVariation; + } } for (int genericArgIdx = dependentGenericStartIdx; genericArgIdx < (int) methodInstance->GetNumGenericArguments(); genericArgIdx++) @@ -21697,6 +21979,8 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool for (auto genericParam : methodInstance->mMethodInfoEx->mGenericParams) { + if (!genericParam->mExternType->IsGenericParam()) + AddDependency(genericParam->mExternType, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint); for (auto constraintTypeInst : genericParam->mInterfaceConstraints) AddDependency(constraintTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint); if (genericParam->mTypeConstraint != NULL) @@ -22043,7 +22327,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool else { BfTypeState typeState; - typeState.mTypeInstance = mCurTypeInstance; + typeState.mType = mCurTypeInstance; typeState.mCurTypeDef = methodDef->mDeclaringType; //typeState.mCurMethodDef = methodDef; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -22095,7 +22379,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool mCurMethodInstance->mDefaultValues.Add(defaultValue); } } - } + } if ((paramDef != NULL) && (paramDef->mParamKind == BfParamKind_Params)) { @@ -22104,7 +22388,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool auto resolvedParamTypeInst = resolvedParamType->ToTypeInstance(); - if ((resolvedParamTypeInst != NULL) && (resolvedParamTypeInst->mTypeDef == mCompiler->mSpanTypeDef)) + if ((resolvedParamTypeInst != NULL) && (resolvedParamTypeInst->IsInstanceOf(mCompiler->mSpanTypeDef))) { // Span isValid = true; @@ -22152,7 +22436,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool } else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || ((genericParamInstance != NULL) && (typeInstConstraint != NULL) && - ((typeInstConstraint->mTypeDef == mCompiler->mDelegateTypeDef) || (typeInstConstraint->mTypeDef == mCompiler->mFunctionTypeDef)))) + ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef))))) { mCurMethodInstance->mHadGenericDelegateParams = true; isValid = true; @@ -22164,7 +22448,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool { auto paramTypeInst = resolvedParamType->ToTypeInstance(); if ((paramTypeInst != NULL) && - ((paramTypeInst->mTypeDef == mCompiler->mDelegateTypeDef) || (paramTypeInst->mTypeDef == mCompiler->mFunctionTypeDef))) + ((paramTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef)))) { // If we have a 'params T' and 'T' gets specialized with actually 'Delegate' or 'Function' then just ignore it isValid = true; @@ -22185,10 +22469,10 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool methodParam.mResolvedType = resolvedParamType; methodParam.mParamDefIdx = paramDefIdx; mCurMethodInstance->mParams.push_back(methodParam); - } + } if (paramDefIdx < (int)methodDef->mParams.size() - 1) - { + { Fail("Only the last parameter can specify 'params'", paramDef->mParamDeclaration->mModToken); } } @@ -22738,8 +23022,10 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool StrFormat("This method hides a method in the root type definition. Use the 'new' keyword if the hiding was intentional. Note that this method is not callable from project '%s'.", checkMethod->mDeclaringType->mProject->mName.c_str()), refNode); else - bfError = Fail("Method already declared with the same parameter types", refNode, true); - if (bfError != NULL) + { + bfError = Fail(StrFormat("Method '%s' already declared with the same parameter types", MethodToString(checkMethodInstance).c_str()), refNode, true); + } + if ((bfError != NULL) && (checkMethod->GetRefNode() != refNode)) mCompiler->mPassInstance->MoreInfo("First declaration", checkMethod->GetRefNode()); } } @@ -22767,7 +23053,10 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool while (checkMethod != NULL) { if (checkMethod->mMethodDeclaration == NULL) + { + checkMethod = checkMethod->mNextWithSameName; continue; + } if (baseType->mMethodInstanceGroups.size() == 0) { @@ -22776,9 +23065,15 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool } if (checkMethod == methodDef) + { + checkMethod = checkMethod->mNextWithSameName; continue; + } if (checkMethod->mName != methodDef->mName) + { + checkMethod = checkMethod->mNextWithSameName; continue; + } auto checkMethodInstance = GetRawMethodInstanceAtIdx(baseType, checkMethod->mIdx); if (checkMethodInstance != NULL) @@ -23437,7 +23732,7 @@ bool BfModule::SlotVirtualMethod(BfMethodInstance* methodInstance, BfAmbiguityCo } } } - + if (doesMethodSignatureMatch) { usedMethod = true; @@ -23478,15 +23773,15 @@ bool BfModule::SlotVirtualMethod(BfMethodInstance* methodInstance, BfAmbiguityCo storeIFaceMethod = isBetter; } } - } - + } + if (storeIFaceMethod) { if (methodInstance->GetNumGenericParams() != 0) _AddVirtualDecl(iMethodInst); *iMethodPtr = methodInstance; } - + checkMethodDef = checkMethodDef->mNextWithSameName; } @@ -23819,7 +24114,7 @@ bool BfModule::Finish() { BP_ZONE("BfModule::Finish"); BfLogSysM("BfModule finish: %p\n", this); - + if (mHadBuildError) { // Don't AssertErrorState here, this current pass may not have failed but @@ -23831,14 +24126,16 @@ bool BfModule::Finish() if (mUsedSlotCount != -1) { BF_ASSERT(mCompiler->mMaxInterfaceSlots != -1); - mUsedSlotCount = mCompiler->mMaxInterfaceSlots; + mUsedSlotCount = mCompiler->mMaxInterfaceSlots; } + if ((!mGeneratesCode) && (!mAddedToCount)) + return true; + BF_ASSERT(mAddedToCount); mAddedToCount = false; mAwaitingFinish = false; - mCompiler->mStats.mModulesFinished++; if (HasCompiledOutput()) @@ -23866,7 +24163,7 @@ bool BfModule::Finish() mIsModuleMutable = false; //mOutFileNames.Clear(); - BF_ASSERT((int)mOutFileNames.size() >= mExtensionCount); + BF_ASSERT(((int)mOutFileNames.size() >= mExtensionCount) || (mParentModule != NULL)); bool writeModule = mBfIRBuilder->HasExports(); String outputPath; @@ -23961,13 +24258,13 @@ bool BfModule::Finish() if ((writeModule) && (!mBfIRBuilder->mIgnoreWrites)) mCompiler->mCodeGen.WriteObjectFile(this, outputPath, codeGenOptions); mLastModuleWrittenRevision = mCompiler->mRevision; - } + } else { for (auto type : mOwnedTypeInstances) { BF_ASSERT((!type->IsIncomplete()) || (type->IsSpecializedByAutoCompleteMethod())); - } + } } for (auto& specModulePair : mSpecializedMethodModules) @@ -24037,8 +24334,7 @@ void BfModule::ClearModuleData(bool clearTransientData) mAddedToCount = false; } - mDICompileUnit = BfIRMDNode(); - mIsModuleMutable = false; + mDICompileUnit = BfIRMDNode(); if (clearTransientData) mIncompleteMethodCount = 0; mHasGenericMethods = false; @@ -24073,7 +24369,8 @@ void BfModule::ClearModuleData(bool clearTransientData) if (mNextAltModule != NULL) mNextAltModule->ClearModuleData(); - BfLogSysM("ClearModuleData. Deleting IRBuilder: %p\n", mBfIRBuilder); + BfLogSysM("ClearModuleData. Deleting IRBuilder: %p\n", mBfIRBuilder); + mIsModuleMutable = false; delete mBfIRBuilder; mBfIRBuilder = NULL; mWantsIRIgnoreWrites = false; diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 08cebbd6..30a11776 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -78,6 +78,7 @@ enum BfEvalExprFlags BfEvalExprFlags_InferReturnType = 0x400000, BfEvalExprFlags_WasMethodRef = 0x800000, BfEvalExprFlags_DeclType = 0x1000000, + BfEvalExprFlags_AllowBase = 0x2000000, BfEvalExprFlags_InheritFlags = BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_Comptime | BfEvalExprFlags_DeclType }; @@ -160,6 +161,7 @@ public: int mWrittenToId; int mReadFromId; int mParamIdx; + uint8 mNamePrefixCount; bool mIsThis; bool mHasLocalStructBacking; bool mIsStruct; @@ -186,6 +188,7 @@ public: mLocalVarId = -1; mCompositeCount = -1; mParamIdx = -2; + mNamePrefixCount = 0; mIsThis = false; mHasLocalStructBacking = false; mIsStruct = false; @@ -350,10 +353,11 @@ public: BfDeferredLocalAssignData* mChainedAssignData; bool mHadFallthrough; bool mHadReturn; + bool mHadBreak; bool mIsUnconditional; bool mIsIfCondition; bool mIfMayBeSkipped; - bool mLeftBlock; + bool mLeftBlock; public: BfDeferredLocalAssignData(BfScopeData* scopeData = NULL) @@ -362,6 +366,7 @@ public: mVarIdBarrier = -1; mHadFallthrough = false; mHadReturn = false; + mHadBreak = false; mChainedAssignData = NULL; mIsChained = false; mIsUnconditional = false; @@ -421,6 +426,7 @@ public: bool mIsDeferredBlock; bool mAllowVariableDeclarations; bool mInInitBlock; + BfMixinState* mMixinState; BfBlock* mAstBlock; BfAstNode* mCloseNode; BfExprEvaluator* mExprEvaluator; @@ -443,6 +449,7 @@ public: mPrevScope = NULL; mLocalVarStart = 0; mLabelNode = NULL; + mMixinState = NULL; mAstBlock = NULL; mCloseNode = NULL; mExprEvaluator = NULL; @@ -1474,6 +1481,7 @@ public: bool mAddedToCount; bool mHasForceLinkMarker; bool mIsReified; + bool mGeneratesCode; bool mReifyQueued; bool mWantsIRIgnoreWrites; bool mHasGenericMethods; @@ -1512,10 +1520,11 @@ public: void SetFail(); void VerifyOnDemandMethods(); bool IsSkippingExtraResolveChecks(); + bool AddErrorContext(StringImpl& errorString, BfAstNode* refNode, bool& isWhileSpecializing); BfError* Fail(const StringImpl& error, BfAstNode* refNode = NULL, bool isPersistent = false, bool deferError = false); BfError* FailInternal(const StringImpl& error, BfAstNode* refNode = NULL); BfError* FailAfter(const StringImpl& error, BfAstNode* refNode); - BfError* Warn(int warningNum, const StringImpl& warning, BfAstNode* refNode = NULL, bool isPersistent = false); + BfError* Warn(int warningNum, const StringImpl& warning, BfAstNode* refNode = NULL, bool isPersistent = false, bool showInSpecialized = false); void CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstance* methodInstance, BfCustomAttributes* customAttributes, BfAstNode* targetSrc); void CheckRangeError(BfType* type, BfAstNode* refNode); bool CheckCircularDataError(); @@ -1571,7 +1580,7 @@ public: void NewScopeState(bool createLexicalBlock = true, bool flushValueScope = true); // returns prev scope data BfIRValue CreateAlloca(BfType* type, bool addLifetime = true, const char* name = NULL, BfIRValue arraySize = BfIRValue()); BfIRValue CreateAllocaInst(BfTypeInstance* typeInst, bool addLifetime = true, const char* name = NULL); - void AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* refNode, BfScopeData* scope, bool condAlloca = false, bool mayEscape = false); + void AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* refNode, BfScopeData* scope, bool condAlloca = false, bool mayEscape = false, BfIRBlock valBlock = BfIRBlock()); void RestoreScoreState_LocalVariables(); void RestoreScopeState(); void MarkDynStack(BfScopeData* scope); @@ -1599,7 +1608,7 @@ public: bool CanCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags = BfCastFlags_None); bool AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNeedsMemberCasting); BfTypedValue BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType /*Can be System.Object or interface*/, const BfAllocTarget& allocTarget, bool callDtor = true); - BfIRValue CastToFunction(BfAstNode* srcNode, const BfTypedValue& targetValue, BfMethodInstance* methodInstance, BfType* toType, BfCastFlags castFlags = BfCastFlags_None); + BfIRValue CastToFunction(BfAstNode* srcNode, const BfTypedValue& targetValue, BfMethodInstance* methodInstance, BfType* toType, BfCastFlags castFlags = BfCastFlags_None, BfIRValue irFunc = BfIRValue()); BfIRValue CastToValue(BfAstNode* srcNode, BfTypedValue val, BfType* toType, BfCastFlags castFlags = BfCastFlags_None, BfCastResultFlags* resultFlags = NULL); BfTypedValue Cast(BfAstNode* srcNode, const BfTypedValue& val, BfType* toType, BfCastFlags castFlags = BfCastFlags_None); BfPrimitiveType* GetIntCoercibleType(BfType* type); @@ -1701,6 +1710,7 @@ public: bool InitGenericParams(BfType* resolvedTypeRef); bool FinishGenericParams(BfType* resolvedTypeRef); bool ValidateGenericConstraints(BfTypeReference* typeRef, BfTypeInstance* genericTypeInstance, bool ignoreErrors); + BfType* ResolveGenericMethodTypeRef(BfTypeReference* typeRef, BfMethodInstance* methodInstance, BfGenericParamInstance* genericParamInstance, BfTypeVector* methodGenericArgsOverride); bool AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGenericParamInstance* checkOuter); bool CheckConstraintState(BfAstNode* refNode); bool ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef); @@ -1731,8 +1741,8 @@ public: BfModuleOptions GetModuleOptions(); BfCheckedKind GetDefaultCheckedKind(); void FinishCEParseContext(BfAstNode* refNode, BfTypeInstance* typeInstance, BfCEParseContext* ceParseContext); - BfCEParseContext CEEmitParse(BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& src); - void UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& ctxString, BfAstNode* refNode); + BfCEParseContext CEEmitParse(BfTypeInstance* typeInstance, const StringImpl& src); + void UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, const StringImpl& ctxString, BfAstNode* refNode); void HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* typeInst, BfCustomAttributes* customAttributes, HashSet foundAttributes); void CEMixin(BfAstNode* refNode, const StringImpl& src); void ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* typeInst, BfCEOnCompileKind onCompileKind); @@ -1793,7 +1803,7 @@ public: void CheckTupleVariableDeclaration(BfTupleExpression* tupleExpr, BfType* initType); void HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl, BfTupleExpression* tupleExpr, BfTypedValue initTupleValue, bool isReadOnly, bool isConst, bool forceAddr, BfIRBlock* declBlock = NULL); void HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl); - void HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArray& arguments, BfAstNode* tooFewRef, BfIRValue phiVal, BfIRBlock& matchedBlock, BfIRBlock falseBlock, bool& hadConditional, bool clearOutOnMismatch); + void HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArray& arguments, BfAstNode* tooFewRef, BfIRValue phiVal, BfIRBlock& matchedBlockStart, BfIRBlock& matchedBlockEnd, BfIRBlock& falseBlockStart, BfIRBlock& falseBlockEnd, bool& hadConditional, bool clearOutOnMismatch); BfTypedValue TryCaseTupleMatch(BfTypedValue tupleVal, BfTupleExpression* tupleExpr, BfIRBlock* eqBlock, BfIRBlock* notEqBlock, BfIRBlock* matchBlock, bool& hadConditional, bool clearOutOnMismatch); BfTypedValue TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVal, BfExpression* expr, BfIRBlock* eqBlock, BfIRBlock* notEqBlock, BfIRBlock* matchBlock, int& uncondTagId, bool& hadConditional, bool clearOutOnMismatch); BfTypedValue HandleCaseBind(BfTypedValue enumVal, const BfTypedValue& tagVal, BfEnumCaseBindExpression* bindExpr, BfIRBlock* eqBlock = NULL, BfIRBlock* notEqBlock = NULL, BfIRBlock* matchBlock = NULL, int* outEnumIdx = NULL); @@ -1815,6 +1825,7 @@ public: BfGenericParamInstance* GetGenericTypeParamInstance(int paramIdx); BfGenericParamInstance* GetGenericParamInstance(BfGenericParamType* type); void GetActiveTypeGenericParamInstances(SizedArray& genericParamInstance); + BfGenericParamInstance* GetMergedGenericParamData(BfGenericParamType* type, BfGenericParamFlags& outFlags, BfType*& outTypeConstraint); BfTypeInstance* GetBaseType(BfTypeInstance* typeInst); void HandleTypeGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, int typeGenericParamIdx); void HandleMethodGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, BfMethodDef* methodDef, int typeGenericParamIdx); @@ -1823,9 +1834,9 @@ public: void ShowAmbiguousTypeError(BfAstNode* refNode, BfTypeDef* typeDef, BfTypeDef* otherTypeDef); void ShowGenericArgCountError(BfAstNode* typeRef, int wantedGenericParams); BfTypeDef* GetActiveTypeDef(BfTypeInstance* typeInstanceOverride = NULL, bool useMixinDecl = false); // useMixinDecl is useful for type lookup, but we don't want the decl project to limit what methods the user can call - BfTypeDef* FindTypeDefRaw(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstance, BfTypeDef* useTypeDef, BfTypeLookupError* error, BfTypeLookupResultCtx* lookupResultCtx = NULL); - BfTypeDef* FindTypeDef(const BfAtomComposite& findName, int numGenericArgs = 0, BfTypeInstance* typeInstanceOverride = NULL, BfTypeLookupError* error = NULL); - BfTypeDef* FindTypeDef(const StringImpl& typeName, int numGenericArgs = 0, BfTypeInstance* typeInstanceOverride = NULL, BfTypeLookupError* error = NULL); + BfTypeDef* FindTypeDefRaw(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstance, BfTypeDef* useTypeDef, BfTypeLookupError* error, BfTypeLookupResultCtx* lookupResultCtx = NULL, BfResolveTypeRefFlags resolveFlags = (BfResolveTypeRefFlags)0); + BfTypeDef* FindTypeDef(const BfAtomComposite& findName, int numGenericArgs = 0, BfTypeInstance* typeInstanceOverride = NULL, BfTypeLookupError* error = NULL, BfResolveTypeRefFlags resolveFlags = (BfResolveTypeRefFlags)0); + BfTypeDef* FindTypeDef(const StringImpl& typeName, int numGenericArgs = 0, BfTypeInstance* typeInstanceOverride = NULL, BfTypeLookupError* error = NULL, BfResolveTypeRefFlags resolveFlags = (BfResolveTypeRefFlags)0); BfTypeDef* FindTypeDef(BfTypeReference* typeRef, BfTypeInstance* typeInstanceOverride = NULL, BfTypeLookupError* error = NULL, int numGenericParams = 0, BfResolveTypeRefFlags resolveFlags = (BfResolveTypeRefFlags)0); BfTypedValue TryLookupGenericConstVaue(BfIdentifierNode* identifierNode, BfType* expectingType); void CheckTypeRefFixit(BfAstNode* typeRef, const char* appendName = NULL); @@ -1935,6 +1946,7 @@ public: BfIRValue GetClassVDataPtr(BfTypeInstance* typeInstance); BfIRValue CreateClassVDataExtGlobal(BfTypeInstance* declTypeInst, BfTypeInstance* implTypeInst, int startVirtIdx); BfIRValue CreateTypeDataRef(BfType* type); + void EncodeAttributeData(BfTypeInstance* typeInstance, BfType* argType, BfIRValue arg, SizedArrayImpl& data, Dictionary& usedStringIdMap); BfIRValue CreateTypeData(BfType* type, Dictionary& usedStringIdMap, bool forceReflectFields, bool needsTypeData, bool needsTypeNames, bool needsVData); BfIRValue FixClassVData(BfIRValue value); @@ -1945,6 +1957,7 @@ public: void Init(bool isFullRebuild = true); bool WantsFinishModule(); void FinishInit(); + void CalcGeneratesCode(); void ReifyModule(); void UnreifyModule(); void Cleanup(); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 01d568e8..81db527c 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -60,7 +60,7 @@ BfGenericExtensionEntry* BfModule::BuildGenericExtensionInfo(BfTypeInstance* gen BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; - typeState.mTypeInstance = genericTypeInst; + typeState.mType = genericTypeInst; typeState.mCurTypeDef = partialTypeDef; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -102,13 +102,15 @@ BfGenericExtensionEntry* BfModule::BuildGenericExtensionInfo(BfTypeInstance* gen auto rootGenericParamInstance = genericTypeInst->mGenericTypeInfo->mGenericParams[paramIdx]; genericParamInstance->mTypeConstraint = rootGenericParamInstance->mTypeConstraint; genericParamInstance->mInterfaceConstraints = rootGenericParamInstance->mInterfaceConstraints; - genericParamInstance->mGenericParamFlags |= rootGenericParamInstance->mGenericParamFlags; + genericParamInstance->mGenericParamFlags = (BfGenericParamFlags)(genericParamInstance->mGenericParamFlags | rootGenericParamInstance->mGenericParamFlags); ResolveGenericParamConstraints(genericParamInstance, genericTypeInst->IsUnspecializedType()); } for (auto genericParam : genericExEntry->mGenericParams) { + if (!genericParam->mExternType->IsGenericParam()) + AddDependency(genericParam->mExternType, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint); for (auto constraintTypeInst : genericParam->mInterfaceConstraints) AddDependency(constraintTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint); if (genericParam->mTypeConstraint != NULL) @@ -123,7 +125,7 @@ bool BfModule::InitGenericParams(BfType* resolvedTypeRef) BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; typeState.mResolveKind = BfTypeState::ResolveKind_BuildingGenericParams; - typeState.mTypeInstance = resolvedTypeRef->ToTypeInstance(); + typeState.mType = resolvedTypeRef; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); BF_ASSERT(mCurMethodInstance == NULL); @@ -162,7 +164,7 @@ bool BfModule::FinishGenericParams(BfType* resolvedTypeRef) BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; typeState.mResolveKind = BfTypeState::ResolveKind_BuildingGenericParams; - typeState.mTypeInstance = resolvedTypeRef->ToTypeInstance(); + typeState.mType = resolvedTypeRef; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); Array deferredResolveTypes; @@ -301,6 +303,8 @@ bool BfModule::FinishGenericParams(BfType* resolvedTypeRef) for (auto genericParam : genericTypeInst->mGenericTypeInfo->mGenericParams) { + if (!genericParam->mExternType->IsGenericParam()) + AddDependency(genericParam->mExternType, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint); for (auto constraintTypeInst : genericParam->mInterfaceConstraints) AddDependency(constraintTypeInst, mCurTypeInstance, BfDependencyMap::DependencyFlag_Constraint); if (genericParam->mTypeConstraint != NULL) @@ -377,12 +381,34 @@ bool BfModule::ValidateGenericConstraints(BfTypeReference* typeRef, BfTypeInstan return true; } +BfType* BfModule::ResolveGenericMethodTypeRef(BfTypeReference* typeRef, BfMethodInstance* methodInstance, BfGenericParamInstance* genericParamInstance, BfTypeVector* methodGenericArgsOverride) +{ + BfConstraintState constraintSet; + constraintSet.mPrevState = mContext->mCurConstraintState; + constraintSet.mGenericParamInstance = genericParamInstance; + constraintSet.mMethodInstance = methodInstance; + constraintSet.mMethodGenericArgsOverride = methodGenericArgsOverride; + + SetAndRestoreValue prevConstraintSet(mContext->mCurConstraintState, &constraintSet); + if (!CheckConstraintState(NULL)) + return NULL; + + SetAndRestoreValue prevMethodInstance(mCurMethodInstance, methodInstance); + SetAndRestoreValue prevTypeInstance(mCurTypeInstance, methodInstance->GetOwner()); + SetAndRestoreValue prevIgnoreErrors(mIgnoreErrors, true); + + BfType* type = ResolveTypeRef(typeRef); + if (type == NULL) + type = GetPrimitiveType(BfTypeCode_Var); + return type; +} + bool BfModule::AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGenericParamInstance* checkOuter) { if (checkOuter == NULL) - return true; - if (checkInner == NULL) return false; + if (checkInner == NULL) + return true; // Added new flags? if ((checkInner->mGenericParamFlags | checkOuter->mGenericParamFlags) != checkOuter->mGenericParamFlags) @@ -390,25 +416,25 @@ bool BfModule::AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGeneri // If the outer had a type flag and the inner has a specific type constraint, then see if those are compatible auto outerFlags = checkOuter->mGenericParamFlags; if ((outerFlags & BfGenericParamFlag_Enum) != 0) - outerFlags |= BfGenericParamFlag_Struct; + outerFlags = (BfGenericParamFlags)(outerFlags | BfGenericParamFlag_Struct); if (checkOuter->mTypeConstraint != NULL) { if (checkOuter->mTypeConstraint->IsStruct()) - outerFlags |= BfGenericParamFlag_Struct; + outerFlags = (BfGenericParamFlags)(outerFlags | BfGenericParamFlag_Struct); else if (checkOuter->mTypeConstraint->IsStructOrStructPtr()) - outerFlags |= BfGenericParamFlag_StructPtr; + outerFlags = (BfGenericParamFlags)(outerFlags | BfGenericParamFlag_StructPtr); else if (checkOuter->mTypeConstraint->IsObject()) - outerFlags |= BfGenericParamFlag_Class; + outerFlags = (BfGenericParamFlags)(outerFlags | BfGenericParamFlag_Class); else if (checkOuter->mTypeConstraint->IsEnum()) - outerFlags |= BfGenericParamFlag_Enum | BfGenericParamFlag_Struct; + outerFlags = (BfGenericParamFlags)(outerFlags | BfGenericParamFlag_Enum | BfGenericParamFlag_Struct); else if (checkOuter->mTypeConstraint->IsInterface()) - outerFlags |= BfGenericParamFlag_Interface; + outerFlags = (BfGenericParamFlags)(outerFlags | BfGenericParamFlag_Interface); } auto innerFlags = checkInner->mGenericParamFlags; if ((innerFlags & BfGenericParamFlag_Enum) != 0) - innerFlags |= BfGenericParamFlag_Struct; + innerFlags = (BfGenericParamFlags)(innerFlags | BfGenericParamFlag_Struct); if (((innerFlags | outerFlags) & ~BfGenericParamFlag_Var) != (outerFlags & ~BfGenericParamFlag_Var)) return false; @@ -506,7 +532,10 @@ void BfModule::CheckInjectNewRevision(BfTypeInstance* typeInstance) { if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL)) { - if (typeInstance->mTypeDef->mNextRevision != NULL) + auto typeDef = typeInstance->mTypeDef; + if (typeDef->mEmitParent != NULL) + typeDef = typeDef->mEmitParent; + if (typeDef->mNextRevision != NULL) { // It's possible that our main compiler thread is generating a new typedef while we're autocompleting. This handles that case... if (typeInstance->mDefineState == BfTypeDefineState_Undefined) @@ -519,8 +548,8 @@ void BfModule::CheckInjectNewRevision(BfTypeInstance* typeInstance) } else { - mContext->HandleChangedTypeDef(typeInstance->mTypeDef); - mSystem->InjectNewRevision(typeInstance->mTypeDef); + mContext->HandleChangedTypeDef(typeDef); + mSystem->InjectNewRevision(typeDef); } } else @@ -529,7 +558,10 @@ void BfModule::CheckInjectNewRevision(BfTypeInstance* typeInstance) } } if ((!typeInstance->IsDeleting()) && (!mCompiler->IsAutocomplete())) - BF_ASSERT((typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_Defined) || (typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_New)); + BF_ASSERT((typeDef->mDefState == BfTypeDef::DefState_Defined) || (typeDef->mDefState == BfTypeDef::DefState_New)); + + if ((typeInstance->mTypeDef->mDefState == BfTypeDef::DefState_EmittedDirty) && (typeInstance->mTypeDef->mEmitParent->mNextRevision == NULL)) + mSystem->UpdateEmittedTypeDef(typeInstance->mTypeDef); } } @@ -560,6 +592,8 @@ void BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType) { CheckInjectNewRevision(typeInst); + BF_ASSERT(!typeInst->mTypeDef->IsEmitted()); + if (typeInst->mBaseType != NULL) BF_ASSERT((typeInst->mBaseType->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0); @@ -891,8 +925,10 @@ void BfModule::TypeFailed(BfTypeInstance* typeInstance) } bool BfModule::CheckCircularDataError() -{ - bool hadError = false; +{ + // Find two loops of mCurTypeInstance. Just finding one loop can give some false errors. + + BfTypeState* circularTypeStateEnd = NULL; int checkIdx = 0; auto checkTypeState = mContext->mCurTypeState; @@ -900,7 +936,7 @@ bool BfModule::CheckCircularDataError() while (true) { if (checkTypeState == NULL) - return hadError; + return false; if (checkTypeState->mResolveKind == BfTypeState::ResolveKind_UnionInnerType) { @@ -911,29 +947,38 @@ bool BfModule::CheckCircularDataError() if (isPreBaseCheck) { if (checkTypeState->mPopulateType != BfPopulateType_Declaration) - return hadError; + return false; } else { if (checkTypeState->mPopulateType == BfPopulateType_Declaration) - return hadError; - if ((checkIdx > 0) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL)) - return hadError; + return false; + if ((checkIdx > 0) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL) && + ((checkTypeState->mType == NULL) || (checkTypeState->mType->IsTypeInstance()))) + return false; } - if ((checkTypeState->mTypeInstance == mCurTypeInstance) && (checkIdx > 0)) - break; + if ((checkTypeState->mType == mCurTypeInstance) && (checkIdx > 1)) + { + if (circularTypeStateEnd == NULL) + circularTypeStateEnd = checkTypeState; + else + break; + } checkTypeState = checkTypeState->mPrevState; checkIdx++; } - + bool hadError = false; checkTypeState = mContext->mCurTypeState->mPrevState; while (true) { if (checkTypeState == NULL) return hadError; + if (checkTypeState == circularTypeStateEnd) + return hadError; + if (checkTypeState->mResolveKind == BfTypeState::ResolveKind_UnionInnerType) { // Skip over this to actual data references @@ -941,7 +986,8 @@ bool BfModule::CheckCircularDataError() continue; } - if ((checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL) ) + if ((checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL) && + ((checkTypeState->mType == NULL) || (checkTypeState->mType->IsTypeInstance()))) return hadError; // We only get one chance to fire off these errors, they can't be ignored. @@ -956,21 +1002,27 @@ bool BfModule::CheckCircularDataError() { Fail(StrFormat("Base type '%s' causes a data cycle", BfTypeUtils::TypeToString(checkTypeState->mCurBaseTypeRef).c_str()), checkTypeState->mCurBaseTypeRef, true); } - else if (checkTypeState->mCurFieldDef->mFieldDeclaration != NULL) + else if ((checkTypeState->mCurFieldDef != NULL) && (checkTypeState->mCurFieldDef->mFieldDeclaration != NULL)) { - Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mTypeInstance).c_str(), checkTypeState->mCurFieldDef->mName.c_str()), + Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mType).c_str(), checkTypeState->mCurFieldDef->mName.c_str()), checkTypeState->mCurFieldDef->mFieldDeclaration->mTypeRef, true); } + else if (checkTypeState->mCurFieldDef != NULL) + { + Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mType).c_str(), checkTypeState->mCurFieldDef->mName.c_str())); + } else { - Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mTypeInstance).c_str(), checkTypeState->mCurFieldDef->mName.c_str())); + Fail(StrFormat("Type '%s' causes a data cycle", TypeToString(checkTypeState->mType).c_str())); } - auto module = GetModuleFor(checkTypeState->mTypeInstance); + auto typeInstance = checkTypeState->mType->ToTypeInstance(); + + auto module = GetModuleFor(checkTypeState->mType); if (module != NULL) - module->TypeFailed(checkTypeState->mTypeInstance); - else - checkTypeState->mTypeInstance->mTypeFailed = true; + module->TypeFailed(typeInstance); + else if (typeInstance != NULL) + typeInstance->mTypeFailed = true; checkTypeState = checkTypeState->mPrevState; } @@ -1038,6 +1090,7 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType { BfLogSysM("Setting reified type %p in module %p in PopulateType on module awaiting finish\n", resolvedTypeRef, typeModule); typeModule->mIsReified = true; + typeModule->CalcGeneratesCode(); typeModule->mWantsIRIgnoreWrites = false; for (auto ownedTypes : typeModule->mOwnedTypeInstances) { @@ -1175,9 +1228,10 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType if (elementType->IsValueType()) { resolvedTypeRef->mDefineState = BfTypeDefineState_ResolvingBaseType; - BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState); - typeState.mPopulateType = populateType; + BfTypeState typeState(arrayType, mContext->mCurTypeState); + typeState.mPopulateType = BfPopulateType_Data; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); + SetAndRestoreValue prevTypeInstance(mCurTypeInstance, NULL); if (!CheckCircularDataError()) { @@ -1199,7 +1253,16 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType } if (arrayType->mElementCount > 0) { - arrayType->mSize = (int)(arrayType->mElementType->GetStride() * arrayType->mElementCount); + arrayType->mSize = (int)(arrayType->mElementType->GetStride() * arrayType->mElementCount); + if (arrayType->mElementType->mSize > 0) + { + int64 maxElements = 0x7FFFFFFF / arrayType->mElementType->GetStride(); + if (arrayType->mElementCount > maxElements) + { + Fail(StrFormat("Array size overflow: %s", TypeToString(arrayType).c_str())); + arrayType->mSize = 0x7FFFFFFF; + } + } arrayType->mAlign = std::max((int32)arrayType->mElementType->mAlign, 1); } else if (arrayType->mElementCount < 0) @@ -1214,6 +1277,8 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType arrayType->mAlign = 1; } + BF_ASSERT(arrayType->mSize >= 0); + if (!typeFailed) arrayType->mWantsGCMarking = elementType->WantsGCMarking(); resolvedTypeRef->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotted; @@ -1412,7 +1477,7 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType if (mContext->mBfObjectType == NULL) { - if (typeInstance->mTypeDef == mCompiler->mBfObjectTypeDef) + if (typeInstance->IsInstanceOf(mCompiler->mBfObjectTypeDef)) mContext->mBfObjectType = typeInstance; else if (mCompiler->mBfObjectTypeDef != NULL) ResolveTypeDef(mCompiler->mBfObjectTypeDef); @@ -1835,7 +1900,7 @@ int BfModule::GenerateTypeOptions(BfCustomAttributes* customAttributes, BfTypeIn } } - if ((!typeInstance->IsBoxed()) && (typeInstance->mTypeDef == mCompiler->mPointerTTypeDef)) + if ((!typeInstance->IsBoxed()) && (typeInstance->IsInstanceOf(mCompiler->mPointerTTypeDef))) { BF_ASSERT(typeInstance->IsGenericTypeInstance()); auto innerType = typeInstance->mGenericTypeInfo->mTypeGenericArguments[0]; @@ -1915,7 +1980,7 @@ void BfModule::SetTypeOptions(BfTypeInstance* typeInstance) typeInstance->mTypeOptionsIdx = GenerateTypeOptions(typeInstance->mCustomAttributes, typeInstance, true); } -BfCEParseContext BfModule::CEEmitParse(BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& src) +BfCEParseContext BfModule::CEEmitParse(BfTypeInstance* typeInstance, const StringImpl& src) { BfCEParseContext ceParseContext; ceParseContext.mFailIdx = mCompiler->mPassInstance->mFailedIdx; @@ -1923,45 +1988,74 @@ BfCEParseContext BfModule::CEEmitParse(BfTypeInstance* typeInstance, BfTypeDef* bool createdParser = false; int startSrcIdx = 0; - if (activeTypeDef->mEmitParser == NULL) - { - createdParser = true; - BfParser* parser = new BfParser(mSystem, typeInstance->mTypeDef->mProject); - parser->mIsEmitted = true; - parser->mFileName = typeInstance->mTypeDef->mName->ToString(); + + BfParser* emitParser = NULL; - BfLogSys(mSystem, "CreateParser (emit): %p\n", parser); + if (typeInstance->mTypeDef->mEmitParent == NULL) + { + BF_ASSERT(typeInstance->mTypeDef->mNextRevision == NULL); + + BfTypeDef* emitTypeDef = new BfTypeDef(); + emitTypeDef->mEmitParent = typeInstance->mTypeDef; + mSystem->CopyTypeDef(emitTypeDef, typeInstance->mTypeDef); + emitTypeDef->mDefState = BfTypeDef::DefState_Emitted; + + typeInstance->mTypeDef = emitTypeDef; + + createdParser = true; + emitParser = new BfParser(mSystem, typeInstance->mTypeDef->mProject); + emitParser->mIsEmitted = true; + emitParser->mFileName = typeInstance->mTypeDef->mName->ToString(); + + BfLogSys(mSystem, "Emit typeDef for type %p created %p parser %p typeDecl %p\n", typeInstance, emitTypeDef, emitParser, emitTypeDef->mTypeDeclaration); if (mCompiler->mIsResolveOnly) - parser->mFileName += "$EmitR$"; + emitParser->mFileName += "$EmitR$"; else - parser->mFileName += "$Emit$"; + emitParser->mFileName += "$Emit$"; - parser->mFileName += StrFormat("%d", typeInstance->mTypeId); - if (activeTypeDef->mPartialIdx != -1) - parser->mFileName + StrFormat(":%d", activeTypeDef->mPartialIdx); + emitParser->mFileName += StrFormat("%d", typeInstance->mTypeId); + emitParser->mFileName += StrFormat(".bf|%d", typeInstance->mRevision); + emitTypeDef->mSource = emitParser; + emitParser->mRefCount++; + emitParser->SetSource(src.c_str(), src.mLength); - parser->mFileName += StrFormat(".bf|%d", typeInstance->mRevision); - activeTypeDef->mEmitParser = parser; - parser->mRefCount++; - parser->SetSource(src.c_str(), src.mLength); + // If we emit only from method attributes then we will already have method instances created + auto _FixMethod = [&](BfMethodInstance* methodInstance) + { + if (methodInstance == NULL) + return; + methodInstance->mMethodDef = emitTypeDef->mMethods[methodInstance->mMethodDef->mIdx]; + }; + + for (auto& methodInstanceGroup : typeInstance->mMethodInstanceGroups) + { + _FixMethod(methodInstanceGroup.mDefault); + if (methodInstanceGroup.mMethodSpecializationMap != NULL) + { + for (auto& kv : *methodInstanceGroup.mMethodSpecializationMap) + _FixMethod(kv.mValue); + } + }; } else - { - int idx = activeTypeDef->mEmitParser->AllocChars(src.mLength + 1); - memcpy((uint8*)activeTypeDef->mEmitParser->mSrc + idx, src.c_str(), src.mLength + 1); - activeTypeDef->mEmitParser->mSrcIdx = idx; - activeTypeDef->mEmitParser->mSrcLength = idx + src.mLength; - activeTypeDef->mEmitParser->mParserData->mSrcLength = activeTypeDef->mEmitParser->mSrcLength; + { + emitParser = typeInstance->mTypeDef->mSource->ToParser(); + + int idx = emitParser->AllocChars(src.mLength + 1); + memcpy((uint8*)emitParser->mSrc + idx, src.c_str(), src.mLength + 1); + emitParser->mSrcIdx = idx; + emitParser->mSrcLength = idx + src.mLength; + emitParser->mParserData->mSrcLength = emitParser->mSrcLength; } - activeTypeDef->mEmitParser->Parse(mCompiler->mPassInstance); - activeTypeDef->mEmitParser->FinishSideNodes(); + emitParser->Parse(mCompiler->mPassInstance); + emitParser->FinishSideNodes(); if (createdParser) { AutoCrit crit(mSystem->mDataLock); - mSystem->mParsers.Add(activeTypeDef->mEmitParser); + mSystem->mParsers.Add(emitParser); } return ceParseContext; @@ -1980,14 +2074,14 @@ void BfModule::FinishCEParseContext(BfAstNode* refNode, BfTypeInstance* typeInst } } -void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& ctxString, BfAstNode* refNode) +void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, const StringImpl& ctxString, BfAstNode* refNode) { if (ceEmitContext->mEmitData.IsEmpty()) return; String src; - - if (activeTypeDef->mEmitParser != NULL) + + if (typeInstance->mTypeDef->mEmitParent != NULL) src += "\n\n"; src += "// Code emission in "; @@ -1996,28 +2090,44 @@ void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeIn src += ceEmitContext->mEmitData; ceEmitContext->mEmitData.Clear(); - BfCEParseContext ceParseContext = CEEmitParse(typeInstance, activeTypeDef, src); - - auto typeDeclaration = activeTypeDef->mEmitParser->mAlloc->Alloc(); + BfCEParseContext ceParseContext = CEEmitParse(typeInstance, src); + auto emitParser = typeInstance->mTypeDef->mSource->ToParser(); + + auto typeDeclaration = emitParser->mAlloc->Alloc(); BfReducer bfReducer; - bfReducer.mSource = activeTypeDef->mEmitParser; + bfReducer.mSource = emitParser; bfReducer.mPassInstance = mCompiler->mPassInstance; - bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; + bfReducer.mAlloc = emitParser->mAlloc; bfReducer.mSystem = mSystem; bfReducer.mCurTypeDecl = typeDeclaration; - typeDeclaration->mDefineNode = activeTypeDef->mEmitParser->mRootNode; + typeDeclaration->mDefineNode = emitParser->mRootNode; bfReducer.HandleTypeDeclaration(typeDeclaration, NULL); BfDefBuilder defBuilder(mSystem); - defBuilder.mCurSource = activeTypeDef->mEmitParser; + defBuilder.mCurSource = emitParser; defBuilder.mCurTypeDef = typeInstance->mTypeDef; + defBuilder.mCurDeclaringTypeDef = typeInstance->mTypeDef; + + if (typeInstance->mTypeDef->mIsCombinedPartial) + { + // Always define generated methods on the primary type declaration + defBuilder.mCurDeclaringTypeDef = typeInstance->mTypeDef->mPartials[0]->GetLatest(); + } defBuilder.mPassInstance = mCompiler->mPassInstance; defBuilder.mIsComptime = true; defBuilder.DoVisitChild(typeDeclaration->mDefineNode); defBuilder.FinishTypeDef(typeInstance->mTypeDef->mTypeCode == BfTypeCode_Enum); - FinishCEParseContext(refNode, typeInstance, &ceParseContext); + typeInstance->mTypeDef->ClearOldMemberSets(); + + FinishCEParseContext(refNode, typeInstance, &ceParseContext); + + if (typeInstance->mTypeDef->mEmitParent != NULL) + { + // Remove generated fields like the 'underlying type' enum field + typeInstance->mFieldInstances.Resize(typeInstance->mTypeDef->mEmitParent->mFields.mSize); + } } void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfCustomAttributes* customAttributes, HashSet foundAttributes) @@ -2061,6 +2171,9 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* auto result = ceContext->Call(customAttribute.mRef, this, methodInstance, args, CeEvalFlags_None, NULL); + if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotted) + return; + if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) { // We populated before we could finish @@ -2105,7 +2218,7 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* ctxStr += TypeToString(typeInstance); ctxStr += " "; ctxStr += customAttribute.mRef->LocationToString(); - UpdateCEEmit(ceEmitContext, typeInstance, typeInstance->mTypeDef, ctxStr, customAttribute.mRef); + UpdateCEEmit(ceEmitContext, typeInstance, ctxStr, customAttribute.mRef); } } @@ -2117,17 +2230,17 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* void BfModule::CEMixin(BfAstNode* refNode, const StringImpl& code) { auto activeTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType; - + //auto emitParser = activeTypeDef->mEmitParser; + String src; - if (activeTypeDef->mEmitParser != NULL) + if (mCurTypeInstance->mTypeDef->mEmitParent != NULL) src += "\n\n"; src += "// Code emission in "; src += MethodToString(mCurMethodInstance); src += "\n"; src += code; - BfReducer bfReducer; - bfReducer.mSource = activeTypeDef->mEmitParser; + BfReducer bfReducer; bfReducer.mPassInstance = mCompiler->mPassInstance; bfReducer.mSystem = mSystem; bfReducer.mCurTypeDecl = activeTypeDef->mTypeDeclaration; @@ -2139,9 +2252,11 @@ void BfModule::CEMixin(BfAstNode* refNode, const StringImpl& code) bool wantsDIData = (mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo); mBfIRBuilder->SaveDebugLocation(); - BfCEParseContext ceParseContext = CEEmitParse(mCurTypeInstance, activeTypeDef, src); - bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; - bfReducer.HandleBlock(activeTypeDef->mEmitParser->mRootNode, false); + BfCEParseContext ceParseContext = CEEmitParse(mCurTypeInstance, src); + auto emitParser = mCurTypeInstance->mTypeDef->mSource->ToParser(); + bfReducer.mSource = emitParser; + bfReducer.mAlloc = emitParser->mAlloc; + bfReducer.HandleBlock(emitParser->mRootNode, false); SetAndRestoreValue prevInlinedAt(mCurMethodState->mCurScope->mDIInlinedAt); SetAndRestoreValue prevDIScope(mCurMethodState->mCurScope->mDIScope); @@ -2160,7 +2275,7 @@ void BfModule::CEMixin(BfAstNode* refNode, const StringImpl& code) // We used to have the "def" line be the inlining position, but the linker we de-duplicate instances of these functions without regard to their unique line // definitions, so we need to be consistent and use the actual line - UpdateSrcPos(activeTypeDef->mEmitParser->mRootNode, BfSrcPosFlag_NoSetDebugLoc); + UpdateSrcPos(emitParser->mRootNode, BfSrcPosFlag_NoSetDebugLoc); int defLine = mCurFilePosition.mCurLine; auto diParentType = mBfIRBuilder->DbgGetTypeInst(mCurTypeInstance); if (!mBfIRBuilder->mIgnoreWrites) @@ -2172,11 +2287,11 @@ void BfModule::CEMixin(BfAstNode* refNode, const StringImpl& code) } } - UpdateSrcPos(activeTypeDef->mEmitParser->mRootNode); + UpdateSrcPos(emitParser->mRootNode); SetIllegalSrcPos(); - Visit(activeTypeDef->mEmitParser->mRootNode); + Visit(emitParser->mRootNode); mBfIRBuilder->RestoreDebugLocation(); mBfIRBuilder->DupDebugLocation(); @@ -2268,7 +2383,11 @@ void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* auto methodInstance = GetRawMethodInstanceAtIdx(typeInstance, methodDef->mIdx); auto result = mCompiler->mCEMachine->Call(methodDef->GetRefNode(), this, methodInstance, {}, (CeEvalFlags)(CeEvalFlags_PersistantError | CeEvalFlags_DeferIfNotOnlyError), NULL); - if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) + if ((onCompileKind == BfCEOnCompileKind_TypeDone) && (typeInstance->mDefineState > BfTypeDefineState_CETypeInit)) + { + // Type done, okay + } + else if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) { // We populated before we could finish AssertErrorState(); @@ -2308,7 +2427,7 @@ void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* ctxStr += MethodToString(methodInstance); ctxStr += " "; ctxStr += methodInstance->mMethodDef->GetRefNode()->LocationToString(); - UpdateCEEmit(ceEmitContext, typeInstance, methodInstance->mMethodDef->mDeclaringType, ctxStr, methodInstance->mMethodDef->GetRefNode()); + UpdateCEEmit(ceEmitContext, typeInstance, ctxStr, methodInstance->mMethodDef->GetRefNode()); } } @@ -2317,27 +2436,27 @@ void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* DeferRebuildType(typeInstance); } } + +// if ((!typeInstance->IsInstanceOf(mCompiler->mValueTypeTypeDef)) && +// (!typeInstance->IsInstanceOf(mCompiler->mBfObjectTypeDef)) && +// (!typeInstance->IsBoxed()) && +// (!typeInstance->IsDelegate()) && +// (!typeInstance->IsTuple())) +// { +// //zTODO: TESTING, remove! +// CEEmitParse(typeInstance, "// Testing"); +// } } void BfModule::DoCEEmit(BfTypeInstance* typeInstance, bool& hadNewMembers) -{ - typeInstance->mTypeDef->ClearEmitted(); - - int startMethodCount = typeInstance->mTypeDef->mMethods.mSize; - int startFieldCount = typeInstance->mTypeDef->mFields.mSize; - int startPropCount = typeInstance->mTypeDef->mProperties.mSize; - +{ CeEmitContext ceEmitContext; ceEmitContext.mType = typeInstance; ExecuteCEOnCompile(&ceEmitContext, typeInstance, BfCEOnCompileKind_TypeInit); + hadNewMembers = (typeInstance->mTypeDef->mEmitParent != NULL); - if ((startMethodCount != typeInstance->mTypeDef->mMethods.mSize) || - (startFieldCount != typeInstance->mTypeDef->mFields.mSize) || - (startPropCount != typeInstance->mTypeDef->mProperties.mSize)) - { - typeInstance->mTypeDef->ClearMemberSets(); - hadNewMembers = true; - } + if (ceEmitContext.mFailed) + TypeFailed(typeInstance); } void BfModule::DoCEEmit(BfMethodInstance* methodInstance) @@ -2408,8 +2527,13 @@ void BfModule::DoCEEmit(BfMethodInstance* methodInstance) src += customAttribute.mRef->LocationToString(); src += "\n"; + //auto emitTypeDef = typeInstance->mCeTypeInfo->mNext->mTypeDef; + //auto emitParser = emitTypeDef->mSource->ToParser(); + + //auto emitParser = activeTypeDef->mEmitParser; + BfReducer bfReducer; - bfReducer.mSource = activeTypeDef->mEmitParser; + //bfReducer.mSource = emitParser; bfReducer.mPassInstance = mCompiler->mPassInstance; bfReducer.mSystem = mSystem; bfReducer.mCurTypeDecl = activeTypeDef->mTypeDeclaration; @@ -2420,28 +2544,32 @@ void BfModule::DoCEEmit(BfMethodInstance* methodInstance) SetAndRestoreValue prevCustomAttribute(mCurMethodState->mEmitRefNode, customAttribute.mRef); String entrySrc = src; - if (activeTypeDef->mEmitParser != NULL) + if (mCurTypeInstance->mTypeDef->mEmitParent != NULL) entrySrc += "\n\n"; entrySrc += src; entrySrc += ceEmitContext.mEmitData; - BfCEParseContext ceParseContext = CEEmitParse(typeInstance, activeTypeDef, entrySrc); - bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; - bfReducer.HandleBlock(activeTypeDef->mEmitParser->mRootNode, false); - Visit(activeTypeDef->mEmitParser->mRootNode); + BfCEParseContext ceParseContext = CEEmitParse(typeInstance, entrySrc); + auto emitParser = mCurTypeInstance->mTypeDef->mSource->ToParser(); + bfReducer.mSource = emitParser; + bfReducer.mAlloc = emitParser->mAlloc; + bfReducer.HandleBlock(emitParser->mRootNode, false); + Visit(emitParser->mRootNode); FinishCEParseContext(customAttribute.mRef, typeInstance, &ceParseContext); } if (!ceEmitContext.mExitEmitData.IsEmpty()) { String exitSrc; - if (activeTypeDef->mEmitParser != NULL) + if (mCurTypeInstance->mTypeDef->mEmitParent != NULL) exitSrc += "\n\n"; exitSrc += src; exitSrc += ceEmitContext.mExitEmitData; - BfCEParseContext ceParseContext = CEEmitParse(typeInstance, activeTypeDef, exitSrc); - bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; - bfReducer.HandleBlock(activeTypeDef->mEmitParser->mRootNode, false); - auto deferredBlock = AddDeferredBlock(activeTypeDef->mEmitParser->mRootNode, &mCurMethodState->mHeadScope); + BfCEParseContext ceParseContext = CEEmitParse(typeInstance, exitSrc); + auto emitParser = mCurTypeInstance->mTypeDef->mSource->ToParser(); + bfReducer.mSource = emitParser; + bfReducer.mAlloc = emitParser->mAlloc; + bfReducer.HandleBlock(emitParser->mRootNode, false); + auto deferredBlock = AddDeferredBlock(emitParser->mRootNode, &mCurMethodState->mHeadScope); deferredBlock->mEmitRefNode = customAttribute.mRef; FinishCEParseContext(customAttribute.mRef, typeInstance, &ceParseContext); } @@ -2487,6 +2615,9 @@ void BfModule::DoPopulateType_TypeAlias(BfTypeInstance* typeAlias) auto typeAliasDecl = (BfTypeAliasDeclaration*)typeDef->mTypeDeclaration; BfType* aliasToType = NULL; + if (typeAlias->mBaseType == NULL) + typeAlias->mBaseType = ResolveTypeDef(mCompiler->mValueTypeTypeDef)->ToTypeInstance(); + typeAlias->mDefineState = BfTypeDefineState_ResolvingBaseType; BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState); typeState.mPopulateType = BfPopulateType_Data; @@ -2596,6 +2727,8 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy DoTypeInstanceMethodProcessing(typeInstance); return true; } + if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotted) + return true; return false; }; @@ -2614,7 +2747,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy resolvedTypeRef->mSize = typeInstance->mAlign = mSystem->mPtrSize; } - BF_ASSERT((typeInstance->mMethodInstanceGroups.size() == 0) || (typeInstance->mMethodInstanceGroups.size() == typeDef->mMethods.size()) || (typeInstance->mTypeDef->mHasEmitMembers)); + BF_ASSERT((typeInstance->mMethodInstanceGroups.size() == 0) || (typeInstance->mMethodInstanceGroups.size() == typeDef->mMethods.size()) || (typeInstance->mCeTypeInfo != NULL)); typeInstance->mMethodInstanceGroups.Resize(typeDef->mMethods.size()); for (int i = 0; i < (int)typeInstance->mMethodInstanceGroups.size(); i++) { @@ -2676,6 +2809,99 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if (!typeInstance->mTypeFailed) CheckCircularDataError(); + if (typeInstance->mDefineState < BfTypeDefineState_Declaring) + { + typeInstance->mDefineState = BfTypeDefineState_Declaring; + + if (typeInstance->IsGenericTypeInstance()) + { + DoPopulateType_SetGenericDependencies(typeInstance); + } + + auto _AddStaticSearch = [&](BfTypeDef* typeDef) + { + if (!typeDef->mStaticSearch.IsEmpty()) + { + BfStaticSearch* staticSearch; + if (typeInstance->mStaticSearchMap.TryAdd(typeDef, NULL, &staticSearch)) + { + SetAndRestoreValue prevTypeDef(mContext->mCurTypeState->mCurTypeDef, typeDef); + for (auto typeRef : typeDef->mStaticSearch) + { + auto staticType = ResolveTypeRef(typeRef, NULL, BfPopulateType_Identity); + if (staticType != NULL) + { + auto staticTypeInst = staticType->ToTypeInstance(); + if (staticTypeInst == NULL) + { + Fail(StrFormat("Type '%s' cannot be used in a 'using static' declaration", TypeToString(staticType).c_str()), typeRef); + } + else + { + staticSearch->mStaticTypes.Add(staticTypeInst); + AddDependency(staticTypeInst, typeInstance, BfDependencyMap::DependencyFlag_StaticValue); + } + } + } + } + } + if (!typeDef->mInternalAccessSet.IsEmpty()) + { + BfInternalAccessSet* internalAccessSet; + BF_ASSERT(!typeDef->IsEmitted()); + if (typeInstance->mInternalAccessMap.TryAdd(typeDef, NULL, &internalAccessSet)) + { + for (auto typeRef : typeDef->mInternalAccessSet) + { + if ((typeRef->IsA()) || + (typeRef->IsA())) + { + String checkNamespaceStr; + typeRef->ToString(checkNamespaceStr); + BfAtomComposite checkNamespace; + if (mSystem->ParseAtomComposite(checkNamespaceStr, checkNamespace)) + { + if (mSystem->ContainsNamespace(checkNamespace, typeDef->mProject)) + { + mSystem->RefAtomComposite(checkNamespace); + internalAccessSet->mNamespaces.Add(checkNamespace); + continue; + } + } + } + + BfType* internalType = NULL; + if (auto genericTypeRef = BfNodeDynCast(typeRef)) + internalType = mContext->mScratchModule->ResolveTypeRefAllowUnboundGenerics(typeRef, BfPopulateType_Identity); + else + internalType = ResolveTypeRef(typeRef, NULL, BfPopulateType_Identity); + if (internalType != NULL) + { + auto internalTypeInst = internalType->ToTypeInstance(); + if (internalTypeInst == NULL) + { + Fail(StrFormat("Type '%s' cannot be used in a 'using internal' declaration", TypeToString(internalType).c_str()), typeRef); + } + else + { + internalAccessSet->mTypes.Add(internalTypeInst); + AddDependency(internalTypeInst, typeInstance, BfDependencyMap::DependencyFlag_StaticValue); + } + } + } + } + } + }; + + if (typeDef->mIsCombinedPartial) + { + for (auto partialTypeDef : typeDef->mPartials) + _AddStaticSearch(partialTypeDef); + } + else + _AddStaticSearch(typeDef); + } + bool underlyingTypeDeferred = false; BfType* underlyingType = NULL; if (typeInstance->mBaseType != NULL) @@ -2731,7 +2957,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy else { AssertErrorState(); - typeInstance->mTypeFailed = true; + TypeFailed(typeInstance); } } @@ -2779,7 +3005,13 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy else { AssertErrorState(); - typeInstance->mTypeFailed = true; + TypeFailed(typeInstance); + } + + if (_CheckTypeDone()) + { + prevDefineState.CancelRestore(); + return; } } @@ -2815,97 +3047,10 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } // Partial population break out point - if (typeInstance->mDefineState < BfTypeDefineState_Declared) - { + + if (typeInstance->mDefineState < BfTypeDefineState_Declared) typeInstance->mDefineState = BfTypeDefineState_Declared; - - if (typeInstance->IsGenericTypeInstance()) - { - DoPopulateType_SetGenericDependencies(typeInstance); - } - - auto _AddStaticSearch = [&](BfTypeDef* typeDef) - { - if (!typeDef->mStaticSearch.IsEmpty()) - { - BfStaticSearch* staticSearch; - if (typeInstance->mStaticSearchMap.TryAdd(typeDef, NULL, &staticSearch)) - { - SetAndRestoreValue prevTypeDef(mContext->mCurTypeState->mCurTypeDef, typeDef); - for (auto typeRef : typeDef->mStaticSearch) - { - auto staticType = ResolveTypeRef(typeRef, NULL, BfPopulateType_Identity); - if (staticType != NULL) - { - auto staticTypeInst = staticType->ToTypeInstance(); - if (staticTypeInst == NULL) - { - Fail(StrFormat("Type '%s' cannot be used in a 'using static' declaration", TypeToString(staticType).c_str()), typeRef); - } - else - { - staticSearch->mStaticTypes.Add(staticTypeInst); - AddDependency(staticTypeInst, typeInstance, BfDependencyMap::DependencyFlag_StaticValue); - } - } - } - } - } - if (!typeDef->mInternalAccessSet.IsEmpty()) - { - BfInternalAccessSet* internalAccessSet; - if (typeInstance->mInternalAccessMap.TryAdd(typeDef, NULL, &internalAccessSet)) - { - for (auto typeRef : typeDef->mInternalAccessSet) - { - if ((typeRef->IsA()) || - (typeRef->IsA())) - { - String checkNamespaceStr; - typeRef->ToString(checkNamespaceStr); - BfAtomComposite checkNamespace; - if (mSystem->ParseAtomComposite(checkNamespaceStr, checkNamespace)) - { - if (mSystem->ContainsNamespace(checkNamespace, typeDef->mProject)) - { - mSystem->RefAtomComposite(checkNamespace); - internalAccessSet->mNamespaces.Add(checkNamespace); - continue; - } - } - } - - BfType* internalType = NULL; - if (auto genericTypeRef = BfNodeDynCast(typeRef)) - internalType = mContext->mScratchModule->ResolveTypeRefAllowUnboundGenerics(typeRef, BfPopulateType_Identity); - else - internalType = ResolveTypeRef(typeRef, NULL, BfPopulateType_Identity); - if (internalType != NULL) - { - auto internalTypeInst = internalType->ToTypeInstance(); - if (internalTypeInst == NULL) - { - Fail(StrFormat("Type '%s' cannot be used in a 'using internal' declaration", TypeToString(internalType).c_str()), typeRef); - } - else - { - internalAccessSet->mTypes.Add(internalTypeInst); - AddDependency(internalTypeInst, typeInstance, BfDependencyMap::DependencyFlag_StaticValue); - } - } - } - } - } - }; - - if (typeDef->mIsCombinedPartial) - { - for (auto partialTypeDef : typeDef->mPartials) - _AddStaticSearch(partialTypeDef); - } - else - _AddStaticSearch(typeDef); - } + if (populateType == BfPopulateType_Declaration) { return; @@ -3022,9 +3167,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy baseType = ResolveTypeDef(mCompiler->mFunctionTypeDef)->ToTypeInstance(); } else - { + { for (auto checkTypeRef : typeDef->mBaseTypes) { + if ((typeInstance->mDefineState == BfTypeDefineState_ResolvingBaseType) && (typeInstance->mTypeFailed)) + break; + auto declTypeDef = typeDef; if (typeDef->mIsCombinedPartial) declTypeDef = typeDef->mPartials.front(); @@ -3032,8 +3180,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurBaseTypeRef, checkTypeRef); SetAndRestoreValue prevDefineState(typeInstance->mDefineState, BfTypeDefineState_ResolvingBaseType); - bool populateBase = !typeInstance->mTypeFailed; - auto checkType = ResolveTypeRef(checkTypeRef, BfPopulateType_Declaration); + bool populateBase = !typeInstance->mTypeFailed; + BfType* checkType = checkType = ResolveTypeRef(checkTypeRef, BfPopulateType_Declaration); + if ((checkType != NULL) && (!checkType->IsInterface()) && (populateBase)) { SetAndRestoreValue prevBaseType(mContext->mCurTypeState->mCurBaseType, checkType->ToTypeInstance()); @@ -3093,7 +3242,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy BfInterfaceDecl ifaceDecl; ifaceDecl.mIFaceTypeInst = ifaceInst; ifaceDecl.mTypeRef = checkTypeRef; - ifaceDecl.mDeclaringType = typeDef; + ifaceDecl.mDeclaringType = typeDef->GetDefinition(); interfaces.push_back(ifaceDecl); } else @@ -3138,6 +3287,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } } + + if (_CheckTypeDone()) + { + prevDefineState.CancelRestore(); + return; + } } else { @@ -3150,6 +3305,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy wantPopulateInterfaces = true; } + if (_CheckTypeDone()) + return; + if (resolvedTypeRef->IsBoxed()) { if ((baseType != NULL) && (baseType->IsStruct())) @@ -3351,8 +3509,8 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy typeInterfaceInst.mStartInterfaceTableIdx = -1; typeInterfaceInst.mStartVirtualIdx = -1; typeInterfaceInst.mIsRedeclared = false; - typeInstance->mInterfaces.push_back(typeInterfaceInst); - + typeInstance->mInterfaces.push_back(typeInterfaceInst); + AddDependency(checkInterface, typeInstance, BfDependencyMap::DependencyFlag_ImplementsInterface); // Interfaces can list other interfaces in their declaration, so pull those in too @@ -3435,7 +3593,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy attrTarget = BfAttributeTargets_Enum; else if (typeInstance->IsInterface()) attrTarget = BfAttributeTargets_Interface; - else if (typeInstance->IsStruct()) + else if ((typeInstance->IsStruct()) || (typeInstance->IsTypedPrimitive())) attrTarget = BfAttributeTargets_Struct; else attrTarget = BfAttributeTargets_Class; @@ -3444,7 +3602,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; typeState.mResolveKind = BfTypeState::ResolveKind_Attributes; - typeState.mTypeInstance = typeInstance; + typeState.mType = typeInstance; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); // This allows us to avoid reentrancy when checking for inner types @@ -3621,6 +3779,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy continue; SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurFieldDef, field); + SetAndRestoreValue prevResolveKind(mContext->mCurTypeState->mResolveKind, BfTypeState::ResolveKind_FieldType); BfType* resolvedFieldType = NULL; @@ -3666,8 +3825,11 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy // For 'let', make read-only } else - { - resolvedFieldType = ResolveTypeRef(field->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam); + { + BfResolveTypeRefFlags resolveFlags = BfResolveTypeRefFlag_NoResolveGenericParam; + if (field->mInitializer != NULL) + resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_AllowInferredSizedArray); + resolvedFieldType = ResolveTypeRef(field->mTypeRef, BfPopulateType_Declaration, resolveFlags); if (resolvedFieldType == NULL) { // Failed, just put in placeholder 'var' @@ -3797,26 +3959,47 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + bool tryCE = true; if (typeInstance->mDefineState == BfTypeDefineState_CETypeInit) { if (populateType <= BfPopulateType_AllowStaticMethods) return; - String error = "OnCompile const evaluation creates a data dependency during TypeInit"; - if (mCompiler->mCEMachine->mCurBuilder != NULL) + int foundTypeCount = 0; + auto typeState = mContext->mCurTypeState; + while (typeState != NULL) { - error += StrFormat(" during const-eval generation of '%s'", MethodToString(mCompiler->mCEMachine->mCurBuilder->mCeFunction->mMethodInstance).c_str()); + if (typeState->mType == typeInstance) + { + foundTypeCount++; + if (foundTypeCount == 2) + break; + } + typeState = typeState->mPrevState; } - auto refNode = typeDef->GetRefNode(); - Fail(error, refNode); - if ((mCompiler->mCEMachine->mCurContext != NULL) && (mCompiler->mCEMachine->mCurContext->mCurFrame != NULL)) - mCompiler->mCEMachine->mCurContext->Fail(*mCompiler->mCEMachine->mCurContext->mCurFrame, error); - else if (mCompiler->mCEMachine->mCurContext != NULL) - mCompiler->mCEMachine->mCurContext->Fail(error); + if ((foundTypeCount >= 2) || (typeInstance->mTypeDef->IsEmitted())) + { + String error = "OnCompile const evaluation creates a data dependency during TypeInit"; + if (mCompiler->mCEMachine->mCurBuilder != NULL) + { + error += StrFormat(" during const-eval generation of '%s'", MethodToString(mCompiler->mCEMachine->mCurBuilder->mCeFunction->mMethodInstance).c_str()); + } + + auto refNode = typeDef->GetRefNode(); + Fail(error, refNode); + if ((mCompiler->mCEMachine->mCurContext != NULL) && (mCompiler->mCEMachine->mCurContext->mCurFrame != NULL)) + mCompiler->mCEMachine->mCurContext->Fail(*mCompiler->mCEMachine->mCurContext->mCurFrame, error); + else if (mCompiler->mCEMachine->mCurContext != NULL) + mCompiler->mCEMachine->mCurContext->Fail(error); + tryCE = false; + } } - else if (typeInstance->mDefineState < BfTypeDefineState_CEPostTypeInit) - { + + if ((typeInstance->mDefineState < BfTypeDefineState_CEPostTypeInit) && (tryCE)) + { + BF_ASSERT(!typeInstance->mTypeDef->IsEmitted()); + typeInstance->mDefineState = BfTypeDefineState_CETypeInit; bool hadNewMembers = false; DoCEEmit(typeInstance, hadNewMembers); @@ -3849,9 +4032,17 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { if ((typeInstance->mCeTypeInfo->mHash != typeInstance->mCeTypeInfo->mNext->mHash) && (!typeInstance->mCeTypeInfo->mHash.IsZero())) { + int prevDeletedTypes = mCompiler->mStats.mTypesDeleted; if (mCompiler->mIsResolveOnly) mCompiler->mNeedsFullRefresh = true; + BfLogSysM("Type %p hash changed, rebuilding dependent types\n", typeInstance); mContext->RebuildDependentTypes(typeInstance); + + if (mCompiler->mStats.mTypesDeleted != prevDeletedTypes) + { + BfLogSysM("Type %p hash changed, rebuilding dependent types - updating after deleting types\n", typeInstance); + mContext->UpdateAfterDeletingTypes(); + } } typeInstance->mCeTypeInfo->mOnCompileMap = typeInstance->mCeTypeInfo->mNext->mOnCompileMap; typeInstance->mCeTypeInfo->mTypeIFaceMap = typeInstance->mCeTypeInfo->mNext->mTypeIFaceMap; @@ -3863,9 +4054,21 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + if ((typeInstance->mTypeDef->IsEmitted()) && (typeInstance->mCeTypeInfo == NULL)) + { + BF_ASSERT(mCompiler->mCanceling); + if (mCompiler->mCanceling) + { + TypeFailed(typeInstance); + auto prevTypeDef = typeInstance->mTypeDef->mEmitParent; + delete typeInstance->mTypeDef; + typeInstance->mTypeDef = prevTypeDef; + hadNewMembers = false; + } + } + if (hadNewMembers) { - typeInstance->mTypeDef->mHasEmitMembers = true; DoPopulateType(resolvedTypeRef, populateType); return; } @@ -3901,7 +4104,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy typeState.mPrevState = mContext->mCurTypeState; typeState.mCurTypeDef = propDef->mDeclaringType; typeState.mCurFieldDef = propDef; - typeState.mTypeInstance = typeInstance; + typeState.mType = typeInstance; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); BfAttributeTargets target = BfAttributeTargets_Property; @@ -3985,6 +4188,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if ((fieldInstance->GetFieldDef() != NULL) && (fieldInstance->GetFieldDef()->mIsConst)) { // Resolve later + AddDependency(resolvedFieldType, typeInstance, BfDependencyMap::DependencyFlag_ConstValue); } else if (fieldInstance->GetFieldDef() != NULL) { @@ -4009,7 +4213,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy typeState.mPrevState = mContext->mCurTypeState; typeState.mCurFieldDef = fieldDef; typeState.mCurTypeDef = fieldDef->mDeclaringType; - typeState.mTypeInstance = typeInstance; + typeState.mType = typeInstance; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); fieldInstance->mCustomAttributes = GetCustomAttributes(fieldDef->mFieldDeclaration->mAttributes, fieldDef->mIsStatic ? BfAttributeTargets_StaticField : BfAttributeTargets_Field); @@ -4398,7 +4602,11 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { typeInstance->mDefineState = BfTypeDefineState_Defined; if (!typeInstance->IsBoxed()) + { ExecuteCEOnCompile(NULL, typeInstance, BfCEOnCompileKind_TypeDone); + if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotted) + return; + } } if (typeInstance->mTypeFailed) @@ -4406,7 +4614,10 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy CheckAddFailType(); - BfLogSysM("Setting mNeedsMethodProcessing on %p\n", typeInstance); + BF_ASSERT_REL(typeInstance->mDefineState != BfTypeDefineState_DefinedAndMethodsSlotting); + BF_ASSERT_REL(typeInstance->mDefineState != BfTypeDefineState_DefinedAndMethodsSlotted); + + BfLogSysM("Setting mNeedsMethodProcessing=true on %p\n", typeInstance); typeInstance->mNeedsMethodProcessing = true; typeInstance->mIsFinishingType = false; @@ -4512,7 +4723,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; typeState.mCurTypeDef = propDef->mDeclaringType; - typeState.mTypeInstance = typeInstance; + typeState.mType = typeInstance; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); ResolveTypeRef(propDef->mTypeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowRef); } @@ -4793,7 +5004,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy FinishGenericParams(resolvedTypeRef); } - if (populateType == BfPopulateType_Data) + if (populateType <= BfPopulateType_Data) return; disableYield.Release(); @@ -4803,14 +5014,24 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { if (typeInstance->mNeedsMethodProcessing) // May have been handled by GetRawMethodInstanceAtIdx above DoTypeInstanceMethodProcessing(typeInstance); - } + } } void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) { if (typeInstance->IsSpecializedByAutoCompleteMethod()) return; - + + if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotting) + { + BfLogSysM("DoTypeInstanceMethodProcessing %p re-entrancy exit\n", typeInstance); + return; + } + + BF_ASSERT_REL(typeInstance->mNeedsMethodProcessing); + BF_ASSERT_REL(typeInstance->mDefineState == BfTypeDefineState_Defined); + typeInstance->mDefineState = BfTypeDefineState_DefinedAndMethodsSlotting; + BF_ASSERT(typeInstance->mModule == this); //TODO: This is new, make sure this is in the right place @@ -4819,9 +5040,9 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) AutoDisallowYield disableYield(mSystem); SetAndRestoreValue prevTypeInstance(mCurTypeInstance, typeInstance); - SetAndRestoreValue prevMethodInstance(mCurMethodInstance, NULL); + SetAndRestoreValue prevMethodInstance(mCurMethodInstance, NULL); - BfLogSysM("DoTypeInstanceMethodProcessing: %p %s Revision:%d\n", typeInstance, TypeToString(typeInstance).c_str(), typeInstance->mRevision); + BfLogSysM("DoTypeInstanceMethodProcessing: %p %s Revision:%d DefineState:%d\n", typeInstance, TypeToString(typeInstance).c_str(), typeInstance->mRevision, typeInstance->mDefineState); auto typeDef = typeInstance->mTypeDef; @@ -4909,7 +5130,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) // Reserve empty entries for (int methodIdx = 0; methodIdx < (int)interfaceTypeDef->mMethods.size(); methodIdx++) - typeInstance->mInterfaceMethodTable.push_back(BfTypeInterfaceMethodEntry()); + typeInstance->mInterfaceMethodTable.push_back(BfTypeInterfaceMethodEntry()); } } @@ -5158,7 +5379,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) { if (attributes->mAttributeTypeRef != NULL) { - auto typeRefName = attributes->mAttributeTypeRef->ToString(); + auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); if (typeRefName == "AlwaysInclude") implRequired = true; @@ -5179,6 +5400,10 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) } if (typeInstance->IncludeAllMethods()) implRequired = true; + // "AssumeInstantiated" also forces default ctor + if (((typeInstance->mAlwaysIncludeFlags & BfAlwaysIncludeFlag_AssumeInstantiated) != 0) && + (methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mParams.IsEmpty())) + implRequired = true; if ((typeOptionsIncludeAll) && (ApplyTypeOptionMethodFilters(true, methodDef, typeOptions))) implRequired = true; @@ -5264,6 +5489,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) } } + BF_ASSERT_REL(typeInstance->mDefineState < BfTypeDefineState_DefinedAndMethodsSlotted); BfLogSysM("Starting DoTypeInstanceMethodProcessing %p GetMethodInstance pass. OnDemandMethods: %d\n", typeInstance, mOnDemandMethodCount); // Def passes. First non-overrides then overrides (for in-place overrides in methods) @@ -5319,7 +5545,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) for (auto& attrAttr : attrCustomAttributes->mAttributes) { - if (attrAttr.mType->ToTypeInstance()->mTypeDef == mCompiler->mAttributeUsageAttributeTypeDef) + if (attrAttr.mType->ToTypeInstance()->IsInstanceOf(mCompiler->mAttributeUsageAttributeTypeDef)) { // Check for Flags arg if (attrAttr.mCtorArgs.size() < 2) @@ -5339,6 +5565,11 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) { if ((attrTypeInst->mAttributeData->mAlwaysIncludeUser & BfAlwaysIncludeFlag_IncludeAllMethods) != 0) forceMethodImpl = true; + + // "AssumeInstantiated" also forces default ctor + if (((attrTypeInst->mAttributeData->mAlwaysIncludeUser & BfAlwaysIncludeFlag_AssumeInstantiated) != 0) && + (methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mParams.IsEmpty())) + forceMethodImpl = true; } } } @@ -5427,6 +5658,16 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) } else { + auto matchedMethodDef = matchedMethod->mMethodDef; + if (matchedMethodDef->mDeclaringType->IsEmitted()) + { + Fail("Boxed interface binding error to emitted method", mCurTypeInstance->mTypeDef->GetRefNode()); + continue; + } + + if (underlyingTypeInstance->mTypeDef->IsEmitted()) + matchedMethodDef = underlyingTypeInstance->mTypeDef->mEmitParent->mMethods[matchedMethodDef->mIdx]; + if (!matchedMethod->mIsForeignMethodDef) { BfMethodInstanceGroup* boxedMethodInstanceGroup = &typeInstance->mMethodInstanceGroups[matchedMethod->mMethodDef->mIdx]; @@ -5440,7 +5681,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) auto methodFlags = matchedMethod->mIsForeignMethodDef ? BfGetMethodInstanceFlag_ForeignMethodDef : BfGetMethodInstanceFlag_None; methodFlags = (BfGetMethodInstanceFlags)(methodFlags | BfGetMethodInstanceFlag_MethodInstanceOnly); - auto moduleMethodInstance = GetMethodInstance(typeInstance, matchedMethod->mMethodDef, BfTypeVector(), + auto moduleMethodInstance = GetMethodInstance(typeInstance, matchedMethodDef, BfTypeVector(), methodFlags, matchedMethod->GetForeignType()); auto methodInstance = moduleMethodInstance.mMethodInstance; @@ -5540,7 +5781,11 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) { if (methodInstance->mMethodDef->mIsAbstract) { - if (!typeInstance->IsUnspecializedTypeVariation()) + if (typeInstance->mVirtualMethodTable[methodIdx].mDeclaringMethod.mTypeInstance == typeInstance) + { + Fail("Method is abstract but it is declared in non-abstract class", methodInstance->mMethodDef->GetRefNode()); + } + else if (!typeInstance->IsUnspecializedTypeVariation()) { if (Fail(StrFormat("'%s' does not implement inherited abstract method '%s'", TypeToString(typeInstance).c_str(), MethodToString(methodInstance).c_str()), typeDef->mTypeDeclaration->mNameNode, true) != NULL) mCompiler->mPassInstance->MoreInfo("Abstract method declared", methodInstance->mMethodDef->GetRefNode()); @@ -5692,7 +5937,8 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) auto checkIFaceMethodInst = checkIFaceInst->mMethodInstanceGroups[checkIMethodIdx].mDefault; if ((checkIFaceMethodInst != NULL) && (checkIFaceMethodInst->mMethodDef->mIsOverride)) { - if (CompareMethodSignatures(checkIFaceMethodInst, ifaceMethodInst)) + bool cmpResult = CompareMethodSignatures(checkIFaceMethodInst, ifaceMethodInst); + if (cmpResult) { bool isBetter = TypeIsSubTypeOf(checkIFaceInst, bestInterface); bool isWorse = TypeIsSubTypeOf(bestInterface, checkIFaceInst); @@ -5790,6 +6036,8 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) methodString = MethodToString(ifaceMethodInst); } + OutputDebugStrF("Failed in %s %p\n", mModuleName.c_str(), this); + BfTypeDeclaration* typeDecl = declTypeDef->mTypeDeclaration; BfError* error = Fail(StrFormat("'%s' does not implement interface member '%s'", TypeToString(typeInstance).c_str(), methodString.c_str()), typeDecl->mNameNode, true); if ((matchedMethod != NULL) && (error != NULL)) @@ -5851,7 +6099,9 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) mCompiler->mStats.mTypesPopulated++; mCompiler->UpdateCompletion(); - BfLogSysM("Finished DoTypeInstanceMethodProcessing %p. OnDemandMethods: %d Virtual Size: %d\n", typeInstance, mOnDemandMethodCount, typeInstance->mVirtualMethodTable.size()); + BF_ASSERT_REL(!typeInstance->mNeedsMethodProcessing); + + BfLogSysM("Finished DoTypeInstanceMethodProcessing %p. OnDemandMethods: %d Virtual Size: %d InterfaceMethodTableSize: %d\n", typeInstance, mOnDemandMethodCount, typeInstance->mVirtualMethodTable.size(), typeInstance->mInterfaceMethodTable.size()); } void BfModule::RebuildMethods(BfTypeInstance* typeInstance) @@ -5859,6 +6109,9 @@ void BfModule::RebuildMethods(BfTypeInstance* typeInstance) if (typeInstance->IsIncomplete()) return; + BfLogSysM("RebuildMethods setting mNeedsMethodProcessing=true on %p\n", typeInstance); + + BF_ASSERT_REL(typeInstance->mDefineState != BfTypeDefineState_DefinedAndMethodsSlotting); typeInstance->mNeedsMethodProcessing = true; typeInstance->mDefineState = BfTypeDefineState_Defined; typeInstance->mTypeIncomplete = true; @@ -6014,6 +6267,15 @@ void BfModule::AddMethodToWorkList(BfMethodInstance* methodInstance) auto typeInstance = methodInstance->GetOwner(); BfMethodProcessRequest* methodProcessRequest = mContext->mMethodWorkList.Alloc(); + if (mCompiler->mCompileState == BfCompiler::CompileState_Unreified) + { + if (methodInstance->mIsReified) + { + BfLogSysM("Marking method %d as unreified due to CompileState_Unreified\n", methodInstance); + methodInstance->mIsReified = false; + } + } + //BF_ASSERT(!methodInstance->mIsReified); methodProcessRequest->mType = typeInstance; methodProcessRequest->mMethodInstance = methodInstance; methodProcessRequest->mRevision = typeInstance->mRevision; @@ -6021,7 +6283,15 @@ void BfModule::AddMethodToWorkList(BfMethodInstance* methodInstance) methodProcessRequest->mFromModule = this; if ((!mCompiler->mIsResolveOnly) && (methodInstance->mIsReified)) + { + if ((!mIsModuleMutable) && (!mIsScratchModule)) + { + BF_ASSERT(!mGeneratesCode); + StartNewRevision(BfModule::RebuildKind_None); + } + BF_ASSERT(mIsModuleMutable || mReifyQueued); + } BF_ASSERT(mBfIRBuilder != NULL); @@ -6060,7 +6330,10 @@ BfArrayType* BfModule::CreateArrayType(BfType* resolvedType, int dimensions) arrayType->mGenericTypeInfo->mTypeGenericArguments.push_back(resolvedType); auto resolvedArrayType = ResolveType(arrayType); if (resolvedArrayType != arrayType) + { + arrayType->Dispose(); mContext->mArrayTypePool.GiveBack(arrayType); + } return (BfArrayType*)resolvedArrayType; } @@ -6622,13 +6895,16 @@ BfBoxedType* BfModule::CreateBoxedType(BfType* resolvedTypeRef, bool allowCreate boxedType->mContext = mContext; boxedType->mElementType = resolvedTypeRef; if (typeInst != NULL) - boxedType->mTypeDef = typeInst->mTypeDef; + boxedType->mTypeDef = typeInst->mTypeDef->GetDefinition(); else boxedType->mTypeDef = mCompiler->mValueTypeTypeDef; boxedType->mBoxedFlags = isStructPtr ? BfBoxedType::BoxedFlags_StructPtr : BfBoxedType::BoxedFlags_None; auto resolvedBoxedType = ResolveType(boxedType, populateType, resolveFlags); if (resolvedBoxedType != boxedType) + { + boxedType->Dispose(); mContext->mBoxedTypePool.GiveBack(boxedType); + } return (BfBoxedType*)resolvedBoxedType; } @@ -6682,6 +6958,7 @@ BfTypeInstance* BfModule::CreateTupleType(const BfTypeVector& fieldTypes, const if (resolvedTupleType != tupleType) { BF_ASSERT(tupleType->mContext != NULL); + tupleType->Dispose(); mContext->mTupleTypePool.GiveBack((BfTupleType*)tupleType); } @@ -6743,7 +7020,7 @@ BfModifiedTypeType* BfModule::CreateModifiedTypeType(BfType* resolvedTypeRef, Bf retTypeType->mContext = mContext; retTypeType->mModifiedKind = modifiedKind; retTypeType->mElementType = resolvedTypeRef; - auto resolvedRetTypeType = ResolveType(retTypeType); + auto resolvedRetTypeType = ResolveType(retTypeType); if (resolvedRetTypeType != retTypeType) mContext->mModifiedTypeTypePool.GiveBack(retTypeType); return (BfModifiedTypeType*)resolvedRetTypeType; @@ -6770,6 +7047,8 @@ BfPointerType* BfModule::CreatePointerType(BfTypeReference* typeRef) BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags) { + BF_ASSERT(typeDef->mDefState != BfTypeDef::DefState_Emitted); + if (typeDef->mTypeDeclaration == NULL) { BF_ASSERT(!typeDef->mIsDelegate && !typeDef->mIsFunction); @@ -6798,13 +7077,18 @@ BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, BfPopulateType populateType return resolvedtypeDefType; } -// Get BaseClass even when we haven't populated the type yet2 +// Get BaseClass even when we haven't populated the type yet BfTypeInstance* BfModule::GetBaseType(BfTypeInstance* typeInst) { - if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mTypeInstance == typeInst)) + if (typeInst->mBaseType == NULL) { - if (typeInst->mBaseType == NULL) - return NULL; + auto checkTypeState = mContext->mCurTypeState; + while (checkTypeState != NULL) + { + if (checkTypeState->mType == typeInst) + return NULL; + checkTypeState = checkTypeState->mPrevState; + } } if ((typeInst->mBaseType == NULL) && (typeInst != mContext->mBfObjectType)) @@ -7083,7 +7367,7 @@ bool BfModule::IsInnerType(BfTypeDef* checkInnerType, BfTypeDef* checkOuterType) return false; if (outerType->mIsPartial) outerType = mSystem->GetCombinedPartial(outerType); - if (outerType == checkOuterType) + if (outerType->GetDefinition() == checkOuterType->GetDefinition()) return true; checkInnerType = checkInnerType->mOuterType; } @@ -7091,6 +7375,8 @@ bool BfModule::IsInnerType(BfTypeDef* checkInnerType, BfTypeDef* checkOuterType) BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, const BfTypeVector& genericArgs, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags) { + BF_ASSERT(typeDef->mDefState != BfTypeDef::DefState_Emitted); + if (typeDef->mGenericParamDefs.size() == 0) return ResolveTypeDef(typeDef, populateType, resolveFlags); @@ -7130,6 +7416,7 @@ BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, const BfTypeVector& generic { delete arrayInstType->mGenericTypeInfo; arrayInstType->mGenericTypeInfo = NULL; + arrayInstType->Dispose(); mContext->mArrayTypeInstancePool.GiveBack(arrayInstType); mContext->mTypeDefTypeRefPool.GiveBack(typeRef); } @@ -7174,29 +7461,7 @@ BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, const BfTypeVector& generic BfType* resolvedType = NULL; bool failed = false; -// if (typeDef->mTypeCode == BfTypeCode_TypeAlias) -// { -// auto aliasType = (BfGenericTypeAliasType*)genericInstType; -// aliasType->mAliasToType = NULL; -// auto typeAliasDecl = (BfTypeAliasDeclaration*)typeDef->mTypeDeclaration; -// SetAndRestoreValue prevTypeInstance(mCurTypeInstance, aliasType); -// SetAndRestoreValue prevMethodInstance(mCurMethodInstance, NULL); -// BfTypeState typeState(mCurTypeInstance, mContext->mCurTypeState); -// typeState.mCurTypeDef = typeDef; -// SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); -// if (typeAliasDecl->mAliasToType != NULL) -// aliasType->mAliasToType = ResolveTypeRef(typeAliasDecl->mAliasToType); -// -// resolvedType = ResolveType(genericInstType, BfPopulateType_IdentityNoRemapAlias); -// if ((resolvedType != NULL) && (populateType >= BfPopulateType_Declaration)) -// PopulateType(resolvedType, populateType); -// } -// else - { - resolvedType = ResolveType(genericInstType, populateType, resolveFlags); - } - - + resolvedType = ResolveType(genericInstType, populateType, resolveFlags); if (resolvedType != genericInstType) { BF_ASSERT(genericInstType->mGenericTypeInfo->mGenericParams.size() == 0); @@ -7207,7 +7472,10 @@ BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, const BfTypeVector& generic if (typeDef->mTypeCode == BfTypeCode_TypeAlias) mContext->mAliasTypePool.GiveBack((BfTypeAliasType*)genericInstType); else + { + genericInstType->Dispose(); mContext->mGenericTypeInstancePool.GiveBack(genericInstType); + } mContext->mTypeDefTypeRefPool.GiveBack(typeRef); } BF_ASSERT((resolvedType == NULL) || resolvedType->IsTypeInstance() || resolvedType->IsPrimitiveType()); @@ -7226,19 +7494,18 @@ BfTypeDef* BfModule::ResolveGenericInstanceDef(BfGenericInstanceTypeRef* generic BfTypeDef* curTypeDef = NULL; if (mCurTypeInstance != NULL) - curTypeDef = mCurTypeInstance->mTypeDef; + curTypeDef = mCurTypeInstance->mTypeDef->GetDefinition(); if (auto directTypeDef = BfNodeDynCast(typeRef)) { auto typeInst = directTypeDef->mType->ToTypeInstance(); - return typeInst->mTypeDef; + return typeInst->mTypeDef->GetDefinition(); } auto namedTypeRef = BfNodeDynCast(typeRef); auto directStrTypeDef = BfNodeDynCastExact(typeRef); if ((namedTypeRef != NULL) || (directStrTypeDef != NULL)) - { - + { BfTypeLookupError error; error.mRefNode = typeRef; BfTypeDef* typeDef = FindTypeDef(typeRef, NULL, &error, numGenericParams); @@ -7316,7 +7583,7 @@ BfTypeDef* BfModule::ResolveGenericInstanceDef(BfGenericInstanceTypeRef* generic *outType = type; auto typeInst = type->ToTypeInstance(); if (typeInst != NULL) - return typeInst->mTypeDef; + return typeInst->mTypeDef->GetDefinition(); } if ((resolveFlags & BfResolveTypeRefFlag_IgnoreLookupError) == 0) @@ -7555,6 +7822,7 @@ BfType* BfModule::ResolveGenericType(BfType* unspecializedType, BfTypeVector* ty { delete tupleType->mGenericTypeInfo; tupleType->mGenericTypeInfo = NULL; + tupleType->Dispose(); mContext->mTupleTypePool.GiveBack((BfTupleType*)tupleType); } BF_ASSERT((resolvedType == NULL) || resolvedType->IsTypeInstance() || resolvedType->IsPrimitiveType()); @@ -7752,9 +8020,8 @@ BfType* BfModule::ResolveGenericType(BfType* unspecializedType, BfTypeVector* ty AddDependency(paramType, delegateType, BfDependencyMap::DependencyFlag_ParamOrReturnValue); } else - { - delete delegateType->mGenericTypeInfo; - delegateType->mGenericTypeInfo = NULL; + { + delegateType->Dispose(); mContext->mDelegateTypePool.GiveBack((BfDelegateType*)delegateType); } BF_ASSERT((resolvedType == NULL) || resolvedType->IsTypeInstance() || resolvedType->IsPrimitiveType()); @@ -7781,7 +8048,7 @@ BfType* BfModule::ResolveGenericType(BfType* unspecializedType, BfTypeVector* ty genericArgs.push_back(genericArg); } - auto resolvedType = ResolveTypeDef(genericTypeInst->mTypeDef, genericArgs, BfPopulateType_BaseType); + auto resolvedType = ResolveTypeDef(genericTypeInst->mTypeDef->GetDefinition(), genericArgs, BfPopulateType_BaseType); BfTypeInstance* specializedType = NULL; if (resolvedType != NULL) specializedType = resolvedType->ToGenericTypeInstance(); @@ -7942,6 +8209,30 @@ void BfModule::GetActiveTypeGenericParamInstances(SizedArraymGenericParamFlags; + outTypeConstraint = genericParam->mTypeConstraint; + + // Check method generic constraints + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL)) + { + for (int genericParamIdx = (int)mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); + genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) + { + auto genericParam = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; + if (genericParam->mExternType == type) + { + outFlags = (BfGenericParamFlags)(outFlags | genericParam->mGenericParamFlags); + if (genericParam->mTypeConstraint != NULL) + outTypeConstraint = genericParam->mTypeConstraint; + } + } + } + return genericParam; +} + BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* type) { if (type->mGenericParamKind == BfGenericParamKind_Method) @@ -7982,7 +8273,7 @@ bool BfModule::ResolveTypeResult_Validate(BfTypeReference* typeRef, BfType* reso if (curGenericTypeInstance->mGenericTypeInfo->mHadValidateErrors) doValidate = false; } - if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mCurBaseTypeRef != NULL) && (!mContext->mCurTypeState->mTypeInstance->IsTypeAlias())) // We validate constraints for base types later + if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mCurBaseTypeRef != NULL) && (!mContext->mCurTypeState->mType->IsTypeAlias())) // We validate constraints for base types later doValidate = false; } @@ -8285,8 +8576,15 @@ BfType* BfModule::ResolveTypeResult(BfTypeReference* typeRef, BfType* resolvedTy populateModule->PopulateType(resolvedTypeRef, populateType); + if ((typeInstance != NULL) && (typeInstance->mTypeDef != NULL) && (typeInstance->mTypeDef->mProtection == BfProtection_Internal) && + (typeInstance->mTypeDef->mOuterType == NULL) && (!typeRef->IsTemporary())) + { + if (!CheckProtection(typeInstance->mTypeDef->mProtection, typeInstance->mTypeDef, false, false)) + Fail(StrFormat("'%s' is inaccessible due to its protection level", TypeToString(typeInstance).c_str()), typeRef); // CS0122 + } + if ((populateType > BfPopulateType_Identity) && (!ResolveTypeResult_Validate(typeRef, resolvedTypeRef))) - return NULL; + return NULL; if (populateType != BfPopulateType_IdentityNoRemapAlias) { @@ -8394,22 +8692,22 @@ BfTypeDef* BfModule::GetActiveTypeDef(BfTypeInstance* typeInstanceOverride, bool if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mForceActiveTypeDef != NULL)) return mContext->mCurTypeState->mForceActiveTypeDef; if (typeInstance != NULL) - useTypeDef = typeInstance->mTypeDef; + useTypeDef = typeInstance->mTypeDef->GetDefinition(); if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL) && (useMixinDecl)) - useTypeDef = mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType; + useTypeDef = mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType->GetDefinition(); else if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mDeclaringType != NULL)) - useTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType; + useTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType->GetDefinition(); else if (mContext->mCurTypeState != NULL) { if ((mContext->mCurTypeState->mCurFieldDef != NULL) && (mContext->mCurTypeState->mCurFieldDef->mDeclaringType != NULL)) - useTypeDef = mContext->mCurTypeState->mCurFieldDef->mDeclaringType; + useTypeDef = mContext->mCurTypeState->mCurFieldDef->mDeclaringType->GetDefinition(); else if (mContext->mCurTypeState->mCurTypeDef != NULL) - useTypeDef = mContext->mCurTypeState->mCurTypeDef; + useTypeDef = mContext->mCurTypeState->mCurTypeDef->GetDefinition(); } return useTypeDef; } -BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstance, BfTypeDef* useTypeDef, BfTypeLookupError* error, BfTypeLookupResultCtx* lookupResultCtx) +BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstance, BfTypeDef* useTypeDef, BfTypeLookupError* error, BfTypeLookupResultCtx* lookupResultCtx, BfResolveTypeRefFlags resolveFlags) { if ((findName.mSize == 1) && (findName.mParts[0]->mIsSystemType)) { @@ -8421,9 +8719,9 @@ BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGene if (mContext->mCurTypeState != NULL) { if (mContext->mCurTypeState->mCurBaseTypeRef != NULL) - skipCheckBaseType = mContext->mCurTypeState->mTypeInstance; + skipCheckBaseType = mContext->mCurTypeState->mType->ToTypeInstance(); if (mContext->mCurTypeState->mResolveKind == BfTypeState::ResolveKind_BuildingGenericParams) - skipCheckBaseType = mContext->mCurTypeState->mTypeInstance; + skipCheckBaseType = mContext->mCurTypeState->mType->ToTypeInstance(); } BfTypeDefLookupContext lookupCtx; @@ -8557,10 +8855,13 @@ BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGene if ((lookupResultCtx != NULL) && (lookupResultCtx->mResult != NULL) && (!lookupResultCtx->mIsVerify) && (foundInnerType != NULL) && (foundInnerType == lookupCtx.mBestTypeDef)) lookupResultCtx->mResult->mFoundInnerType = true; + if (((resolveFlags & BfResolveTypeRefFlag_AllowGlobalContainer) == 0) && (lookupCtx.mBestTypeDef != NULL) && (lookupCtx.mBestTypeDef->IsGlobalsContainer())) + return NULL; + return lookupCtx.mBestTypeDef; } -BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error) +BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGenericArgs, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error, BfResolveTypeRefFlags resolveFlags) { BP_ZONE("BfModule::FindTypeDef_1"); @@ -8590,7 +8891,7 @@ BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGeneric if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL) && (typeInstance != NULL)) { if (mCompiler->mResolvePassData->mAutoCompleteTempTypes.Contains(useTypeDef)) - return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error); + return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error, NULL, resolveFlags); } BfTypeLookupEntry typeLookupEntry; @@ -8614,7 +8915,7 @@ BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGeneric BfTypeLookupResultCtx lookupResultCtx; lookupResultCtx.mResult = resultPtr; - auto typeDef = FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, errorPtr, &lookupResultCtx); + auto typeDef = FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, errorPtr, &lookupResultCtx, resolveFlags); if (prevAllocSize != typeInstance->mLookupResults.size()) { @@ -8630,21 +8931,20 @@ BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGeneric else { if ((resultPtr == NULL) || (resultPtr->mForceLookup)) - return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error); + return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error, NULL, resolveFlags); else return resultPtr->mTypeDef; - } - + } } -BfTypeDef* BfModule::FindTypeDef(const StringImpl& typeName, int numGenericArgs, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error) +BfTypeDef* BfModule::FindTypeDef(const StringImpl& typeName, int numGenericArgs, BfTypeInstance* typeInstanceOverride, BfTypeLookupError* error, BfResolveTypeRefFlags resolveFlags) { BP_ZONE("BfModule::FindTypeDef_4"); BfSizedAtomComposite findName; if (!mSystem->ParseAtomComposite(typeName, findName)) return NULL; - auto result = FindTypeDef(findName, numGenericArgs, typeInstanceOverride, error); + auto result = FindTypeDef(findName, numGenericArgs, typeInstanceOverride, error, resolveFlags); // Don't allow just finding extensions here. This can happen in some 'using static' cases but generally shouldn't happen if ((result != NULL) && (result->mTypeCode == BfTypeCode_Extension)) return NULL; @@ -8718,7 +9018,7 @@ BfTypeDef* BfModule::FindTypeDef(BfTypeReference* typeRef, BfTypeInstance* typeI } #endif - auto typeDef = FindTypeDef(findName, numGenericParams, typeInstanceOverride, error); + auto typeDef = FindTypeDef(findName, numGenericParams, typeInstanceOverride, error, resolveFlags); //TYPEDEF if (namedTypeRef != NULL) // namedTypeRef->mTypeDef = typeDef; @@ -9075,22 +9375,27 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula if (findName == "Self") { BfType* selfType = mCurTypeInstance; - if (selfType->IsInterface()) // For interfaces, 'Self' refers to the identity of the implementing type, so we use a placeholder - return GetPrimitiveType(BfTypeCode_Self); - else - resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource); - - if (selfType->IsBoxed()) - selfType = selfType->GetUnderlyingType(); - if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + if (selfType->IsTypeAlias()) + selfType = GetOuterType(selfType); + if (selfType != NULL) { - if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation())) - selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType); + if (selfType->IsInterface()) // For interfaces, 'Self' refers to the identity of the implementing type, so we use a placeholder + return GetPrimitiveType(BfTypeCode_Self); + else + resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource); + + if (selfType->IsBoxed()) + selfType = selfType->GetUnderlyingType(); + if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + { + if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation())) + selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType); + } } if (selfType != NULL) { auto selfTypeInst = selfType->ToTypeInstance(); - if ((selfTypeInst != NULL) && (selfTypeInst->mTypeDef->IsGlobalsContainer())) + if ((selfTypeInst != NULL) && (selfTypeInst->mTypeDef->IsGlobalsContainer()) && ((resolveFlags & BfResolveTypeRefFlag_AllowGlobalsSelf) == 0)) selfType = NULL; } @@ -9102,15 +9407,19 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula } else if (findName == "SelfBase") { - BfType* selfType = mCurTypeInstance; - resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource); - - if (selfType->IsBoxed()) - selfType = selfType->GetUnderlyingType(); - if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + BfType* selfType = mCurTypeInstance; + if (selfType->IsTypeAlias()) + selfType = GetOuterType(selfType); + if (selfType != NULL) { - if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation())) - selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType); + resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource); + if (selfType->IsBoxed()) + selfType = selfType->GetUnderlyingType(); + if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + { + if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation())) + selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType); + } } BfType* baseType = NULL; if (selfType != NULL) @@ -9135,15 +9444,20 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula else if (findName == "SelfOuter") { BfType* selfType = mCurTypeInstance; - resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource); - if (selfType->IsBoxed()) - selfType = selfType->GetUnderlyingType(); - if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + if (selfType->IsTypeAlias()) + selfType = GetOuterType(selfType); + if (selfType != NULL) { - if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation())) - selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType); + resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_FromIndirectSource); + if (selfType->IsBoxed()) + selfType = selfType->GetUnderlyingType(); + if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + { + if ((selfType->IsSpecializedType()) || (selfType->IsUnspecializedTypeVariation())) + selfType = ResolveTypeDef(selfType->ToTypeInstance()->mTypeDef, populateType); + } + selfType = GetOuterType(selfType->ToTypeInstance()); } - selfType = GetOuterType(mCurTypeInstance); if (selfType == NULL) Fail("'SelfOuter' type is not usable here", typeRef); return ResolveTypeResult(typeRef, selfType, populateType, resolveFlags); @@ -9161,7 +9475,63 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula BfGenericParamDef* genericParamDef = NULL; BfType* genericParamResult = NULL; bool disallowConstExprValue = false; - if ((genericCheckTypeInstance != NULL) && (genericCheckTypeInstance->IsGenericTypeInstance())) + + if ((contextMethodInstance != NULL) && (genericParamResult == NULL)) + { + BfMethodInstance* prevMethodInstance = NULL; + + // If we're in a closure then use the outside method generic arguments + auto checkMethodInstance = contextMethodInstance; + if ((mCurMethodState != NULL) && (checkMethodInstance->mIsClosure)) + { + auto checkMethodState = mCurMethodState; + while (checkMethodState != NULL) + { + if ((checkMethodState->mMethodInstance != NULL) && (checkMethodState->mMethodInstance->mIsClosure)) + { + checkMethodInstance = checkMethodState->mPrevMethodState->mMethodInstance; + } + checkMethodState = checkMethodState->mPrevMethodState; + } + } + + for (int genericParamIdx = (int)checkMethodInstance->mMethodDef->mGenericParams.size() - 1; genericParamIdx >= 0; genericParamIdx--) + { + auto checkGenericParamDef = checkMethodInstance->mMethodDef->mGenericParams[genericParamIdx]; + String genericName = checkGenericParamDef->mName; + if (genericName == findName) + { + genericParamDef = checkGenericParamDef; + if (((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) && + ((resolveFlags & BfResolveTypeRefFlag_AllowGenericMethodParamConstValue) == 0)) + disallowConstExprValue = true; + + HandleMethodGenericParamRef(typeRef, checkMethodInstance->GetOwner()->mTypeDef, checkMethodInstance->mMethodDef, genericParamIdx); + + if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) + return GetGenericParamType(BfGenericParamKind_Method, genericParamIdx); + else + { + if ((mContext->mCurConstraintState != NULL) && (mContext->mCurConstraintState->mMethodInstance == checkMethodInstance) && + (mContext->mCurConstraintState->mMethodGenericArgsOverride != NULL)) + { + return ResolveTypeResult(typeRef, (*mContext->mCurConstraintState->mMethodGenericArgsOverride)[genericParamIdx], populateType, resolveFlags); + } + + SetAndRestoreValue prevSymbolRefKind; + if (mCompiler->mResolvePassData != NULL) // Don't add these typeRefs, they are indirect + prevSymbolRefKind.Init(mCompiler->mResolvePassData->mGetSymbolReferenceKind, BfGetSymbolReferenceKind_None); + genericParamResult = checkMethodInstance->mMethodInfoEx->mMethodGenericArguments[genericParamIdx]; + if ((genericParamResult != NULL) && + (genericParamResult->IsConstExprValue()) && + ((resolveFlags & BfResolveTypeRefFlag_AllowGenericMethodParamConstValue) == 0)) + disallowConstExprValue = true; + } + } + } + } + + if ((genericCheckTypeInstance != NULL) && (genericCheckTypeInstance->IsGenericTypeInstance()) && (genericParamResult == NULL)) { auto genericTypeInst = (BfTypeInstance*)genericCheckTypeInstance; auto* genericParams = &curTypeDef->mGenericParamDefs; @@ -9200,62 +9570,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula } } } - } - - if ((contextMethodInstance != NULL) && (genericParamResult == NULL)) - { - BfMethodInstance* prevMethodInstance = NULL; - - // If we're in a closure then use the outside method generic arguments - auto checkMethodInstance = contextMethodInstance; - if ((mCurMethodState != NULL) && (checkMethodInstance->mIsClosure)) - { - auto checkMethodState = mCurMethodState; - while (checkMethodState != NULL) - { - if ((checkMethodState->mMethodInstance != NULL) && (checkMethodState->mMethodInstance->mIsClosure)) - { - checkMethodInstance = checkMethodState->mPrevMethodState->mMethodInstance; - } - checkMethodState = checkMethodState->mPrevMethodState; - } - } - - for (int genericParamIdx = (int)checkMethodInstance->mMethodDef->mGenericParams.size() - 1; genericParamIdx >= 0; genericParamIdx--) - { - auto checkGenericParamDef = checkMethodInstance->mMethodDef->mGenericParams[genericParamIdx]; - String genericName = checkGenericParamDef->mName; - if (genericName == findName) - { - genericParamDef = checkGenericParamDef; - if (((genericParamDef->mGenericParamFlags & BfGenericParamFlag_Const) != 0) && - ((resolveFlags & BfResolveTypeRefFlag_AllowGenericMethodParamConstValue) == 0)) - disallowConstExprValue = true; - - HandleMethodGenericParamRef(typeRef, checkMethodInstance->GetOwner()->mTypeDef, checkMethodInstance->mMethodDef, genericParamIdx); - - if ((resolveFlags & BfResolveTypeRefFlag_NoResolveGenericParam) != 0) - return GetGenericParamType(BfGenericParamKind_Method, genericParamIdx); - else - { - if ((mContext->mCurConstraintState != NULL) && (mContext->mCurConstraintState->mMethodInstance == checkMethodInstance) && - (mContext->mCurConstraintState->mMethodGenericArgsOverride != NULL)) - { - return ResolveTypeResult(typeRef, (*mContext->mCurConstraintState->mMethodGenericArgsOverride)[genericParamIdx], populateType, resolveFlags); - } - - SetAndRestoreValue prevSymbolRefKind; - if (mCompiler->mResolvePassData != NULL) // Don't add these typeRefs, they are indirect - prevSymbolRefKind.Init(mCompiler->mResolvePassData->mGetSymbolReferenceKind, BfGetSymbolReferenceKind_None); - genericParamResult = checkMethodInstance->mMethodInfoEx->mMethodGenericArguments[genericParamIdx]; - if ((genericParamResult != NULL) && - (genericParamResult->IsConstExprValue()) && - ((resolveFlags & BfResolveTypeRefFlag_AllowGenericMethodParamConstValue) == 0)) - disallowConstExprValue = true; - } - } - } - } + } if (genericParamResult != NULL) { @@ -9557,6 +9872,8 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula if (invokeMethodInstance != NULL) { resolvedType = invokeMethodInstance->mReturnType; + if ((resolvedType != NULL) && (resolvedType->IsRef())) + resolvedType = resolvedType->GetUnderlyingType(); return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); } } @@ -9578,9 +9895,8 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula resolvedType = GetDelegateReturnType(genericParamInstance->mTypeConstraint); return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); } - else if ((genericParamInstance->mTypeConstraint->IsTypeInstance()) && - ((genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mDelegateTypeDef) || - (genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mFunctionTypeDef))) + else if ((genericParamInstance->mTypeConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || + (genericParamInstance->mTypeConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef))) { allowThrough = true; } @@ -9591,6 +9907,8 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula { auto methodRefType = (BfMethodRefType*)innerType; resolvedType = methodRefType->mMethodRef->mReturnType; + if ((resolvedType != NULL) && (resolvedType->IsRef())) + resolvedType = resolvedType->GetUnderlyingType(); return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); } } @@ -9704,8 +10022,15 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula } } + static int sCallIdx = 0; + int callIdx = sCallIdx++; + if (callIdx == 0x00006CA4) + { + NOP; + } + BfResolvedTypeSet::LookupContext lookupCtx; - lookupCtx.mResolveFlags = (BfResolveTypeRefFlags)(resolveFlags & (BfResolveTypeRefFlag_NoCreate | BfResolveTypeRefFlag_IgnoreLookupError | BfResolveTypeRefFlag_DisallowComptime)); + lookupCtx.mResolveFlags = (BfResolveTypeRefFlags)(resolveFlags & (BfResolveTypeRefFlag_NoCreate | BfResolveTypeRefFlag_IgnoreLookupError | BfResolveTypeRefFlag_DisallowComptime | BfResolveTypeRefFlag_AllowInferredSizedArray)); lookupCtx.mRootTypeRef = typeRef; lookupCtx.mRootTypeDef = typeDef; lookupCtx.mModule = this; @@ -9722,6 +10047,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula if (!inserted) { BF_ASSERT(resolvedEntry->mValue != NULL); + BF_ASSERT(!resolvedEntry->mValue->IsDeleting()); return ResolveTypeResult(typeRef, resolvedEntry->mValue, populateType, resolveFlags); } @@ -9893,7 +10219,7 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula typedVal = constResolver.Resolve(sizeExpr, NULL, BfConstResolveFlag_ArrayInitSize); } if (typedVal.mKind == BfTypedValueKind_GenericConstValue) - { + { BfUnknownSizedArrayType* arrayType = new BfUnknownSizedArrayType(); arrayType->mContext = mContext; arrayType->mElementType = elementType; @@ -10195,10 +10521,15 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula for (int i = 0; i < parentTypeInstance->mGenericTypeInfo->mGenericParams.size(); i++) { actualTupleType->mGenericTypeInfo->mGenericParams.push_back(parentTypeInstance->mGenericTypeInfo->mGenericParams[i]->AddRef()); + } + + for (int i = 0; i < parentTypeInstance->mGenericTypeInfo->mTypeGenericArguments.size(); i++) + { actualTupleType->mGenericTypeInfo->mTypeGenericArguments.push_back(parentTypeInstance->mGenericTypeInfo->mTypeGenericArguments[i]); auto typeGenericArg = actualTupleType->mGenericTypeInfo->mTypeGenericArguments[i]; actualTupleType->mGenericTypeInfo->mIsUnspecialized |= typeGenericArg->IsGenericParam() || typeGenericArg->IsUnspecializedType(); } + CheckUnspecializedGenericType(actualTupleType, populateType); if (isUnspecialized) { @@ -10737,7 +11068,7 @@ BfTypeInstance* BfModule::GetUnspecializedTypeInstance(BfTypeInstance* typeInst) BF_ASSERT((!typeInst->IsDelegateFromTypeRef()) && (!typeInst->IsFunctionFromTypeRef())); auto genericTypeInst = (BfTypeInstance*)typeInst; - auto result = ResolveTypeDef(genericTypeInst->mTypeDef, BfPopulateType_Declaration); + auto result = ResolveTypeDef(genericTypeInst->mTypeDef->GetDefinition(), BfPopulateType_Declaration); BF_ASSERT((result != NULL) && (result->IsUnspecializedType())); if (result == NULL) return NULL; @@ -10871,11 +11202,33 @@ bool BfModule::AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNe return true; } -BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targetValue, BfMethodInstance* methodInstance, BfType* toType, BfCastFlags castFlags) +BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targetValue, BfMethodInstance* methodInstance, BfType* toType, BfCastFlags castFlags, BfIRValue irFunc) { auto invokeMethodInstance = GetDelegateInvokeMethod(toType->ToTypeInstance()); - if (invokeMethodInstance->IsExactMatch(methodInstance, false, true)) + bool methodsThisMatch = true; + if (invokeMethodInstance->mMethodDef->mIsStatic != methodInstance->mMethodDef->mIsStatic) + methodsThisMatch = false; + else + { + if (!methodInstance->mMethodDef->mIsStatic) + { + BfType* thisType = methodInstance->GetThisType(); + if (thisType->IsPointer()) + thisType = thisType->GetUnderlyingType(); + BfType* invokeThisType = invokeMethodInstance->GetThisType(); + if (invokeThisType->IsPointer()) + invokeThisType = invokeThisType->GetUnderlyingType(); + if (!TypeIsSubTypeOf(thisType->ToTypeInstance(), invokeThisType->ToTypeInstance())) + methodsThisMatch = false; + } + } + + bool methodMatches = methodsThisMatch; + if (methodMatches) + methodMatches = invokeMethodInstance->IsExactMatch(methodInstance, false, false); + + if (methodMatches) { if (methodInstance->GetOwner()->IsFunction()) { @@ -10883,19 +11236,23 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targe return targetValue.mValue; } - BfModuleMethodInstance methodRefMethod; - if (methodInstance->mDeclModule == this) - methodRefMethod = methodInstance; - else - methodRefMethod = ReferenceExternalMethodInstance(methodInstance); - auto dataType = GetPrimitiveType(BfTypeCode_IntPtr); - if (!methodRefMethod.mFunc) + BfIRFunction bindFuncVal = irFunc; + if (!bindFuncVal) { - if ((!methodInstance->mIsUnspecialized) && (HasCompiledOutput())) - AssertErrorState(); - return GetDefaultValue(dataType); + BfModuleMethodInstance methodRefMethod; + if (methodInstance->mDeclModule == this) + methodRefMethod = methodInstance; + else + methodRefMethod = ReferenceExternalMethodInstance(methodInstance); + auto dataType = GetPrimitiveType(BfTypeCode_IntPtr); + if (!methodRefMethod.mFunc) + { + if ((!methodInstance->mIsUnspecialized) && (HasCompiledOutput())) + AssertErrorState(); + return GetDefaultValue(dataType); + } + bindFuncVal = methodRefMethod.mFunc; } - auto bindFuncVal = methodRefMethod.mFunc; if (mCompiler->mOptions.mAllowHotSwapping) bindFuncVal = mBfIRBuilder->RemapBindFunction(bindFuncVal); return mBfIRBuilder->CreatePtrToInt(bindFuncVal, BfTypeCode_IntPtr); @@ -10903,7 +11260,7 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targe if ((castFlags & BfCastFlags_SilentFail) == 0) { - if (invokeMethodInstance->IsExactMatch(methodInstance, true, true)) + if ((methodsThisMatch) && (invokeMethodInstance->IsExactMatch(methodInstance, true, false))) { Fail(StrFormat("Non-static method '%s' cannot match '%s' because it contains captured variables, consider using a delegate or removing captures", MethodToString(methodInstance).c_str(), TypeToString(toType).c_str()), srcNode); } @@ -10931,7 +11288,7 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targe invokeThisWasPtr = true; } - if (invokeThisType == thisType) + if (TypeIsSubTypeOf(thisType->ToTypeInstance(), invokeThisType->ToTypeInstance())) { if (invokeThisWasPtr != thisWasPtr) { @@ -11148,7 +11505,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp { SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance(); - if ((constraintTypeInst != NULL) && (constraintTypeInst->mTypeDef == mCompiler->mEnumTypeDef) && (explicitCast)) + if ((constraintTypeInst != NULL) && (constraintTypeInst->IsInstanceOf(mCompiler->mEnumTypeDef)) && (explicitCast)) { // Enum->int if ((explicitCast) && (toType->IsInteger())) @@ -11936,6 +12293,8 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp if (allowCast) { + if (typedVal.IsAddr()) + typedVal = LoadValue(typedVal); return mBfIRBuilder->CreateNumericCast(typedVal.mValue, typedVal.mType->IsSigned(), toTypeCode); } } @@ -12261,7 +12620,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp if (returnType == toType) return mBfIRBuilder->GetFakeVal(); - operatorOut = GetDefaultTypedValue(returnType); + operatorOut = GetDefaultTypedValue(returnType, false, BfDefaultValueKind_Addr); } else { @@ -12699,8 +13058,10 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf if (!explicitCast) { - fromMethodInst->GetParamName(paramIdx, fromParamName); - toMethodInst->GetParamName(paramIdx, toParamName); + int fromNamePrefixCount = 0; + int toNamePrefixCount = 0; + fromMethodInst->GetParamName(paramIdx, fromParamName, fromNamePrefixCount); + toMethodInst->GetParamName(paramIdx, toParamName, toNamePrefixCount); if ((!fromParamName.IsEmpty()) && (!toParamName.IsEmpty())) nameMatches = fromParamName == toParamName; } @@ -12861,7 +13222,7 @@ bool BfModule::TypeHasParentOrEquals(BfTypeDef* checkChildTypeDef, BfTypeDef* ch while (checkType->mNestDepth > checkParentTypeDef->mNestDepth) checkType = checkType->mOuterType; - if (checkType == checkParentTypeDef) + if (checkType->GetDefinition() == checkParentTypeDef->GetDefinition()) return true; if (checkType->mNameEx != checkParentTypeDef->mNameEx) return false; @@ -12880,7 +13241,7 @@ BfTypeDef* BfModule::FindCommonOuterType(BfTypeDef* type, BfTypeDef* type2) { if ((type == NULL) || (type2 == NULL)) return NULL; - int curNestDepth = std::min(type->mNestDepth, type2->mNestDepth); + int curNestDepth = BF_MIN(type->mNestDepth, type2->mNestDepth); while (type->mNestDepth > curNestDepth) type = type->mOuterType; while (type2->mNestDepth > curNestDepth) @@ -12890,7 +13251,7 @@ BfTypeDef* BfModule::FindCommonOuterType(BfTypeDef* type, BfTypeDef* type2) { if ((!type->mIsPartial) && (!type2->mIsPartial)) { - if (type == type2) + if (type->GetDefinition() == type2->GetDefinition()) return type; } else @@ -12920,7 +13281,7 @@ bool BfModule::TypeIsSubTypeOf(BfTypeInstance* srcType, BfTypeInstance* wantType auto typeState = mContext->mCurTypeState; while (typeState != NULL) { - if ((typeState->mTypeInstance == srcType) && (typeState->mCurBaseType != NULL)) + if ((typeState->mType == srcType) && (typeState->mCurBaseType != NULL)) { return TypeIsSubTypeOf(typeState->mCurBaseType, wantType, checkAccessibility); } diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index 4575fb10..dce0e396 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -706,12 +706,18 @@ BfBlock* BfParser::ParseInlineBlock(int spaceIdx, int endIdx) mSrcIdx = spaceIdx; BfAstNode* startNode = NULL; int usedEndIdx = spaceIdx; + int usedLineNum = mLineNum; + int usedLineStart = mLineStart; + while (true) { NextToken(endIdx + 1); if (mSyntaxToken == BfSyntaxToken_HIT_END_IDX) { mSrcIdx = usedEndIdx; + mLineNum = usedLineNum; + mLineStart = usedLineStart; + auto lastNode = mSidechannelRootNode->GetLast(); if (lastNode != NULL) mSrcIdx = std::max(mSrcIdx, lastNode->GetSrcEnd()); @@ -719,6 +725,8 @@ BfBlock* BfParser::ParseInlineBlock(int spaceIdx, int endIdx) } usedEndIdx = mSrcIdx; + usedLineStart = mLineStart; + usedLineNum = mLineNum; auto childNode = CreateNode(); if (childNode == NULL) @@ -1549,6 +1557,21 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mToken = BfToken_AndEquals; mTokenEnd = ++mSrcIdx; } + else if (mSrc[mSrcIdx] == '+') + { + mToken = BfToken_AndPlus; + mTokenEnd = ++mSrcIdx; + } + else if (mSrc[mSrcIdx] == '-') + { + mToken = BfToken_AndMinus; + mTokenEnd = ++mSrcIdx; + } + else if (mSrc[mSrcIdx] == '*') + { + mToken = BfToken_AndStar; + mTokenEnd = ++mSrcIdx; + } else mToken = BfToken_Ampersand; mSyntaxToken = BfSyntaxToken_Token; @@ -2301,6 +2324,13 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mToken = BfToken_DotDotDot; mSyntaxToken = BfSyntaxToken_Token; } + else if (mSrc[mSrcIdx + 1] == '<') + { + mSrcIdx += 2; + mTokenEnd = mSrcIdx; + mToken = BfToken_DotDotLess; + mSyntaxToken = BfSyntaxToken_Token; + } else { mSrcIdx++; @@ -2511,8 +2541,15 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) bool endNumber = false; + bool hasDot = c == '.'; + if ((hasDot) && (mSrc[mSrcIdx] == '.')) + { + // Skip float parsing if we have a double-dot `1..` case + hasDot = false; + } + // The 'prevIsDot' helps tuple lookups like "tuple.0.0", interpreting those as two integers rather than a float - if (((c == '.') && (!prevIsDot)) || (hasExp)) + if (((hasDot) && (!prevIsDot)) || (hasExp)) { // Switch to floating point mode //double dVal = val; @@ -3029,6 +3066,10 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) else if (SrcPtrHasToken("nullable")) mToken = BfToken_Nullable; break; + case TOKEN_HASH('o', 'f', 'f', 's'): + if (SrcPtrHasToken("offsetof")) + mToken = BfToken_OffsetOf; + break; case TOKEN_HASH('o', 'p', 'e', 'r'): if (SrcPtrHasToken("operator")) mToken = BfToken_Operator; diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index 6e424020..ccc2d82e 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -1600,6 +1600,7 @@ void BfPrinter::Visit(BfVariableDeclaration* varDecl) { //Visit(varDecl->ToBase()); + VisitChild(varDecl->mAttributes); VisitChildWithProceedingSpace(varDecl->mModSpecifier); if (varDecl->mPrecedingComma != NULL) @@ -1652,6 +1653,17 @@ void BfPrinter::Visit(BfSizeOfExpression* sizeOfExpr) VisitChild(sizeOfExpr->mCloseParen); } +void BfPrinter::Visit(BfOffsetOfExpression* offsetOfExpr) +{ + VisitChild(offsetOfExpr->mToken); + VisitChild(offsetOfExpr->mOpenParen); + VisitChild(offsetOfExpr->mTypeRef); + VisitChild(offsetOfExpr->mCommaToken); + ExpectSpace(); + VisitChild(offsetOfExpr->mMemberName); + VisitChild(offsetOfExpr->mCloseParen); +} + void BfPrinter::Visit(BfDefaultExpression* defaultExpr) { Visit(defaultExpr->ToBase()); @@ -1816,8 +1828,11 @@ void BfPrinter::Visit(BfDeleteStatement* deleteStmt) Visit(deleteStmt->ToBase()); VisitChild(deleteStmt->mDeleteToken); + VisitChild(deleteStmt->mTargetTypeToken); + VisitChild(deleteStmt->mAllocExpr); ExpectSpace(); VisitChild(deleteStmt->mExpression); + VisitChild(deleteStmt->mTrailingSemicolon); } @@ -2283,7 +2298,7 @@ void BfPrinter::Visit(BfUnaryOperatorExpression* unaryOpExpr) { Visit(unaryOpExpr->ToBase()); - bool postOp = (unaryOpExpr->mOp == BfUnaryOp_PostIncrement) || (unaryOpExpr->mOp == BfUnaryOp_PostDecrement); + bool postOp = (unaryOpExpr->mOp == BfUnaryOp_PostIncrement) || (unaryOpExpr->mOp == BfUnaryOp_PostDecrement) || (unaryOpExpr->mOp == BfUnaryOp_PartialRangeFrom); if (!postOp) VisitChild(unaryOpExpr->mOpToken); if ((unaryOpExpr->mOp == BfUnaryOp_Ref) || (unaryOpExpr->mOp == BfUnaryOp_Mut) || (unaryOpExpr->mOp == BfUnaryOp_Out) || (unaryOpExpr->mOp == BfUnaryOp_Params) || (unaryOpExpr->mOp == BfUnaryOp_Cascade)) @@ -2530,6 +2545,8 @@ void BfPrinter::Visit(BfPropertyMethodDeclaration* propertyMethodDeclaration) ExpectSpace(); QueueVisitChild(propertyMethodDeclaration->mNameNode); ExpectSpace(); + QueueVisitChild(propertyMethodDeclaration->mSetRefSpecifier); + ExpectSpace(); QueueVisitChild(propertyMethodDeclaration->mMutSpecifier); ExpectSpace(); QueueVisitChild(propertyMethodDeclaration->mFatArrowToken); @@ -2569,8 +2586,8 @@ void BfPrinter::Visit(BfPropertyDeclaration* propertyDeclaration) if (indexerDeclaration != NULL) { - QueueVisitChild(indexerDeclaration->mThisToken); - QueueVisitChild(indexerDeclaration->mOpenBracket); + QueueVisitChild(indexerDeclaration->mThisToken); + QueueVisitChild(indexerDeclaration->mOpenBracket); for (int i = 0; i < (int)indexerDeclaration->mParams.size(); i++) { if (i > 0) @@ -2582,12 +2599,7 @@ void BfPrinter::Visit(BfPropertyDeclaration* propertyDeclaration) } QueueVisitChild(indexerDeclaration->mCloseBracket); ExpectSpace(); - } - - QueueVisitChild(propertyDeclaration->mEqualsNode); - ExpectSpace(); - QueueVisitChild(propertyDeclaration->mInitializer); - FlushVisitChild(); + } if (auto block = BfNodeDynCast(propertyDeclaration->mDefinitionBlock)) { @@ -2611,6 +2623,14 @@ void BfPrinter::Visit(BfPropertyDeclaration* propertyDeclaration) } } + ExpectSpace(); + QueueVisitChild(propertyDeclaration->mEqualsNode); + ExpectSpace(); + QueueVisitChild(propertyDeclaration->mInitializer); + ExpectSpace(); + QueueVisitChild(propertyDeclaration->mFieldDtor); + FlushVisitChild(); + //QueueVisitChild(propertyDeclaration->mTrailingSemicolon); // ??? QueueVisitErrorNodes(propertyDeclaration); diff --git a/IDEHelper/Compiler/BfPrinter.h b/IDEHelper/Compiler/BfPrinter.h index 7b270b1a..815222c5 100644 --- a/IDEHelper/Compiler/BfPrinter.h +++ b/IDEHelper/Compiler/BfPrinter.h @@ -168,6 +168,7 @@ public: virtual void Visit(BfParameterDeclaration* paramDecl) override; virtual void Visit(BfTypeOfExpression* typeOfExpr) override; virtual void Visit(BfSizeOfExpression* sizeOfExpr) override; + virtual void Visit(BfOffsetOfExpression* offsetOfExpr) override; virtual void Visit(BfDefaultExpression* defaultExpr) override; virtual void Visit(BfCheckTypeExpression* checkTypeExpr) override; virtual void Visit(BfDynamicCastExpression* dynCastExpr) override; diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 3d04ba4c..efc75e6d 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -368,7 +368,7 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int* } int chevronDepth = 0; bool identifierExpected = true; - bool hadEndBracket = false; + int endBracket = -1; int bracketDepth = 0; int parenDepth = 0; bool hadTupleComma = false; @@ -388,8 +388,12 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int* auto checkTokenNode = BfNodeDynCast(checkNode); if (checkTokenNode != NULL) { - if (hadEndBracket) + if (endBracket != -1) + { + if (outEndNode) + *outEndNode = endBracket; return false; + } BfToken checkToken = checkTokenNode->GetToken(); if (bracketDepth > 0) @@ -650,7 +654,7 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int* } else if (checkToken == BfToken_RBracket) { - hadEndBracket = true; + endBracket = checkIdx; } else if ((checkToken == BfToken_Star) || (checkToken == BfToken_Question)) { @@ -1867,6 +1871,26 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat MEMBER_SET_CHECKED(typeAttrExpr, mCloseParen, tokenNode); exprLeft = typeAttrExpr; } + else if (token == BfToken_OffsetOf) + { + BfOffsetOfExpression* typeAttrExpr = mAlloc->Alloc(); + ReplaceNode(tokenNode, typeAttrExpr); + typeAttrExpr->mToken = tokenNode; + tokenNode = ExpectTokenAfter(typeAttrExpr, BfToken_LParen); + MEMBER_SET_CHECKED(typeAttrExpr, mOpenParen, tokenNode); + auto typeRef = CreateTypeRefAfter(typeAttrExpr); + MEMBER_SET_CHECKED(typeAttrExpr, mTypeRef, typeRef); + + tokenNode = ExpectTokenAfter(typeAttrExpr, BfToken_Comma); + MEMBER_SET_CHECKED(typeAttrExpr, mCommaToken, tokenNode); + + auto nameNode = ExpectIdentifierAfter(typeAttrExpr); + MEMBER_SET_CHECKED(typeAttrExpr, mMemberName, nameNode); + + tokenNode = ExpectTokenAfter(typeAttrExpr, BfToken_RParen); + MEMBER_SET_CHECKED(typeAttrExpr, mCloseParen, tokenNode); + exprLeft = typeAttrExpr; + } else if (token == BfToken_Default) { auto defaultExpr = mAlloc->Alloc(); @@ -2253,14 +2277,15 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat CreateExprFlags innerFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_EarlyExit); if (unaryOp == BfUnaryOp_Cascade) - { innerFlags = (CreateExprFlags)(innerFlags | (createExprFlags & CreateExprFlags_AllowVariableDecl)); - } + + if (unaryOp == BfUnaryOp_PartialRangeThrough) // This allows for just a naked '...' + innerFlags = (CreateExprFlags)(innerFlags | CreateExprFlags_AllowEmpty); // Don't attempt binary or unary operations- they will always be lower precedence unaryOpExpr->mExpression = CreateExpressionAfter(unaryOpExpr, innerFlags); if (unaryOpExpr->mExpression == NULL) - return NULL; + return unaryOpExpr; MoveNode(unaryOpExpr->mExpression, unaryOpExpr); } @@ -2332,7 +2357,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat if (exprLeft == NULL) { - Fail("Expected expression", node); + if ((createExprFlags & CreateExprFlags_AllowEmpty) == 0) + Fail("Expected expression", node); return NULL; } @@ -2349,12 +2375,22 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat ((token == BfToken_RChevron) || (token == BfToken_RDblChevron))) return exprLeft; - if ((token == BfToken_DblPlus) || (token == BfToken_DblMinus)) - { - // Post-increment, post-decrement + BfUnaryOp postUnaryOp = BfUnaryOp_None; + if (token == BfToken_DblPlus) + postUnaryOp = BfUnaryOp_PostIncrement; + if (token == BfToken_DblMinus) + postUnaryOp = BfUnaryOp_PostDecrement; + + if (token == BfToken_DotDotDot) + { + //TODO: Detect if this is a BfUnaryOp_PartialRangeFrom + } + + if (postUnaryOp != BfUnaryOp_None) + { auto unaryOpExpr = mAlloc->Alloc(); ReplaceNode(exprLeft, unaryOpExpr); - unaryOpExpr->mOp = (token == BfToken_DblPlus) ? BfUnaryOp_PostIncrement : BfUnaryOp_PostDecrement; + unaryOpExpr->mOp = postUnaryOp; MEMBER_SET(unaryOpExpr, mOpToken, tokenNode); MEMBER_SET(unaryOpExpr, mExpression, exprLeft); exprLeft = unaryOpExpr; @@ -2495,7 +2531,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat if (auto prevToken = BfNodeDynCast(prevNode)) { // This handles such as "dlg = stack => obj.method;" - if (prevToken->GetToken() == BfToken_RChevron) + if ((prevToken->GetToken() == BfToken_RChevron) || (prevToken->GetToken() == BfToken_RDblChevron)) hadEndingToken = true; } } @@ -2666,17 +2702,36 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat { if ((createExprFlags & CreateExprFlags_EarlyExit) != 0) return exprLeft; - auto binOpExpression = mAlloc->Alloc(); - ReplaceNode(exprLeft, binOpExpression); - binOpExpression->mLeft = exprLeft; - binOpExpression->mOp = binOp; - MEMBER_SET(binOpExpression, mOpToken, tokenNode); + mVisitorPos.MoveNext(); // We only need to check binary operator precedence at the "top level" binary operator rhsCreateExprFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_NoCheckBinOpPrecedence); - auto exprRight = CreateExpressionAfter(binOpExpression, rhsCreateExprFlags); + if (tokenNode->mToken == BfToken_DotDotDot) + rhsCreateExprFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_AllowEmpty); + + BfExpression* exprRight = CreateExpressionAfter(tokenNode, rhsCreateExprFlags); + + if (exprRight == NULL) + { + if (tokenNode->mToken == BfToken_DotDotDot) + { + auto unaryOpExpression = mAlloc->Alloc(); + ReplaceNode(exprLeft, unaryOpExpression); + unaryOpExpression->mExpression = exprLeft; + unaryOpExpression->mOp = BfUnaryOp_PartialRangeFrom; + MEMBER_SET(unaryOpExpression, mOpToken, tokenNode); + return unaryOpExpression; + } + } + + auto binOpExpression = mAlloc->Alloc(); + ReplaceNode(exprLeft, binOpExpression); + binOpExpression->mLeft = exprLeft; + binOpExpression->mOp = binOp; + MEMBER_SET(binOpExpression, mOpToken, tokenNode); + if (exprRight == NULL) return binOpExpression; MEMBER_SET(binOpExpression, mRight, exprRight); @@ -4096,6 +4151,8 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS } } } + else if (afterTypeRefNode == NULL) + isLocalVariable = false; } } @@ -4451,8 +4508,15 @@ BfAstNode* BfReducer::CreateStatement(BfAstNode* node, CreateStmtFlags createStm { if (!IsSemicolon(nextNode)) { + bool doWarn = false; + // Why did we have this BfIdentifierNode check? It failed to throw an error on just things like "{ a }" - if (/*(origStmtNode->IsA()) || */(origStmtNode->IsA()) || (origStmtNode->IsA())) + if (origStmtNode->IsA()) + { + // These do require a semicolon + doWarn = true; + } + else if (/*(origStmtNode->IsA()) || */(origStmtNode->IsA()) || (origStmtNode->IsA())) return stmt; if (origStmtNode->IsA()) { @@ -4465,7 +4529,12 @@ BfAstNode* BfReducer::CreateStatement(BfAstNode* node, CreateStmtFlags createStm if (((createStmtFlags & CreateStmtFlags_AllowUnterminatedExpression) != 0) && (origStmtNode->IsA()) && (nextNode == NULL)) return stmt; - auto error = mPassInstance->FailAfterAt("Semicolon expected", node->GetSourceData(), stmt->GetSrcEnd() - 1); + BfError* error; + if (doWarn) + error = mPassInstance->WarnAfterAt(0, "Semicolon expected", node->GetSourceData(), stmt->GetSrcEnd() - 1); + else + error = mPassInstance->FailAfterAt("Semicolon expected", node->GetSourceData(), stmt->GetSrcEnd() - 1); + if ((error != NULL) && (mSource != NULL)) error->mProject = mSource->mProject; mPrevStmtHadError = true; @@ -5015,12 +5084,20 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF if (tokenNode == NULL) { + if ((!params.IsEmpty()) && (!BfNodeIsExact(params.back()))) + { + FailAfter("Expected ','", params.back()); + hasFailed = true; + break; + } + BfExpression* sizeExpr = CreateExpressionAfter(arrayType); if (sizeExpr == NULL) { hasFailed = true; break; } + MoveNode(sizeExpr, arrayType); params.push_back(sizeExpr); } @@ -5751,11 +5828,17 @@ BfFieldDeclaration* BfReducer::CreateFieldDeclaration(BfTokenNode* tokenNode, Bf return fieldDeclaration; } + bool hasSemicolon = false; + auto fieldDtor = CreateFieldDtorDeclaration(fieldDeclaration); if (fieldDtor != NULL) + { fieldDeclaration->mFieldDtor = fieldDtor; + if (fieldDtor->mBody != NULL) + hasSemicolon = !fieldDtor->mBody->IsMissingSemicolon(); + } - if (ExpectTokenAfter(fieldDeclaration, BfToken_Semicolon, BfToken_Comma) != NULL) + if ((!hasSemicolon) && (ExpectTokenAfter(fieldDeclaration, BfToken_Semicolon, BfToken_Comma) != NULL)) { // This gets taken later mVisitorPos.mReadPos--; @@ -5766,7 +5849,7 @@ BfFieldDeclaration* BfReducer::CreateFieldDeclaration(BfTokenNode* tokenNode, Bf return fieldDeclaration; } -BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, int depth, BfAstNode* deferredHeadNode) +BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, int depth, BfAstNode* deferredHeadNode) { BfToken token = tokenNode->GetToken(); @@ -5786,8 +5869,9 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, int depth, BfAstNod return NULL; } + SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, attributes, !declStarted); mVisitorPos.MoveNext(); - auto memberNode = ReadTypeMember(nextNode, 0, (deferredHeadNode != NULL) ? deferredHeadNode : attributes); + auto memberNode = ReadTypeMember(nextNode, true, depth, (deferredHeadNode != NULL) ? deferredHeadNode : attributes); if (memberNode == NULL) return NULL; @@ -6070,7 +6154,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, int depth, BfAstNod if (nextNode != NULL) { mVisitorPos.MoveNext(); - typeMember = ReadTypeMember(nextNode, depth + 1); + typeMember = ReadTypeMember(nextNode, true, depth + 1); } auto memberDecl = BfNodeDynCast(typeMember); @@ -6310,6 +6394,7 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf String accessorName; BfTokenNode* mutSpecifier = NULL; + BfTokenNode* refSpecifier = NULL; while (true) { @@ -6377,8 +6462,18 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf BfAstNode* bodyAfterNode = accessorIdentifier; BfAstNode* body = NULL; - + auto tokenNode = BfNodeDynCast(child); + if ((tokenNode != NULL) && (tokenNode->GetToken() == BfToken_Ref) && (accessorName == "set")) + { + refSpecifier = tokenNode; + bodyAfterNode = tokenNode; + + mVisitorPos.MoveNext(); + child = mVisitorPos.GetNext(); + tokenNode = BfNodeDynCast(child); + } + if ((tokenNode != NULL) && (tokenNode->GetToken() == BfToken_Mut)) { if (mutSpecifier != NULL) @@ -6390,8 +6485,8 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf mVisitorPos.MoveNext(); child = mVisitorPos.GetNext(); - } - + } + bool handled = false; BfTokenNode* fatArrowToken = NULL; BfAstNode* endSemicolon = NULL; @@ -6461,6 +6556,8 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf AddErrorNode(accessorIdentifier); if (mutSpecifier != NULL) AddErrorNode(mutSpecifier); + if (refSpecifier != NULL) + AddErrorNode(refSpecifier); continue; } @@ -6479,6 +6576,8 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf { MEMBER_SET(method, mBody, body); } + if (refSpecifier != NULL) + MEMBER_SET(method, mSetRefSpecifier, refSpecifier); if (mutSpecifier != NULL) MEMBER_SET(method, mMutSpecifier, mutSpecifier); // if ((accessorBlock != NULL) && (IsNodeRelevant(propertyDeclaration))) @@ -6490,7 +6589,7 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf } } -BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth, BfAstNode* deferredHeadNode) +BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int depth, BfAstNode* deferredHeadNode) { // SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, node, false); // if (depth == 0) @@ -6537,10 +6636,8 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth, BfAstNode* defe } else { - SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, tokenNode, false); - if (depth == 0) - prevTypeMemberNodeStart.Set(); - return ReadTypeMember(tokenNode, depth, deferredHeadNode); + SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, tokenNode, !declStarted); + return ReadTypeMember(tokenNode, declStarted, depth, deferredHeadNode); } } else if (auto block = BfNodeDynCast(node)) @@ -6875,6 +6972,29 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth, BfAstNode* defe { MEMBER_SET(propertyDeclaration, mDefinitionBlock, block); ReadPropertyBlock(propertyDeclaration, block); + + if (auto tokenNode = BfNodeDynCast(mVisitorPos.GetNext())) + { + if (tokenNode->mToken == BfToken_AssignEquals) + { + MEMBER_SET(propertyDeclaration, mEqualsNode, tokenNode); + mVisitorPos.MoveNext(); + auto initExpr = CreateExpressionAfter(propertyDeclaration); + if (initExpr != NULL) + { + MEMBER_SET(propertyDeclaration, mInitializer, initExpr); + } + } + } + + if (auto tokenNode = BfNodeDynCast(mVisitorPos.GetNext())) + { + if (tokenNode->mToken == BfToken_Tilde) + { + auto fieldDtor = CreateFieldDtorDeclaration(propertyDeclaration); + MEMBER_SET(propertyDeclaration, mFieldDtor, fieldDtor); + } + } } else if (isExprBodyProp) { @@ -7934,6 +8054,7 @@ BfAstNode* BfReducer::HandleTopLevel(BfBlock* node) mVisitorPos.Write(child); // Just keep it... continue; } + SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, tokenNode); auto newNode = CreateTopLevelObject(tokenNode, NULL); hadPrevFail = newNode == NULL; @@ -8150,7 +8271,7 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi FailAfter("Expected type declaration", tokenNode); return NULL; } - + mVisitorPos.MoveNext(); auto topLevelObject = CreateTopLevelObject(nextToken, attributes); if (topLevelObject == NULL) @@ -8204,7 +8325,7 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi return NULL; } mVisitorPos.MoveNext(); - + auto topLevelObject = CreateTopLevelObject(nextToken, attributes); if (topLevelObject == NULL) { @@ -8400,7 +8521,7 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi typeDeclaration->mNameNode = identifierNode; ReplaceNode(tokenNode, typeDeclaration); MoveNode(identifierNode, typeDeclaration); - typeDeclaration->mDocumentation = FindDocumentation(typeDeclaration); + typeDeclaration->mDocumentation = FindDocumentation(mTypeMemberNodeStart); auto nextNode = mVisitorPos.GetNext(); auto chevronToken = BfNodeDynCast(nextNode); @@ -8919,6 +9040,7 @@ BfTokenNode* BfReducer::ParseMethodParams(BfAstNode* node, SizedArrayImplGetToken(); if ((token == BfToken_Var) || (token == BfToken_LParen) || (token == BfToken_Delegate) || (token == BfToken_Function) || + (token == BfToken_Comptype) || (token == BfToken_Decltype) || (token == BfToken_DotDotDot)) { mVisitorPos.MoveNext(); @@ -8984,6 +9107,16 @@ BfTokenNode* BfReducer::ParseMethodParams(BfAstNode* node, SizedArrayImplAlloc(); + ReplaceNode(attributes, paramDecl); + MoveNode(paramDecl, node); + params->push_back(paramDecl); + MEMBER_SET(paramDecl, mAttributes, attributes); + attributes = NULL; + } + Fail("Invalid token", tokenNode); return NULL; } @@ -9379,6 +9512,8 @@ bool BfReducer::ParseMethod(BfMethodDeclaration* methodDeclaration, SizedArrayIm { // In process of typing - just eat identifier so we don't error out on whole method MoveNode(identifierAfter, ctorDecl); + mVisitorPos.MoveNext(); + AddErrorNode(identifierAfter); } if (attributeDirective != NULL) @@ -9608,6 +9743,15 @@ BfGenericConstraintsDeclaration* BfReducer::CreateGenericConstraintsDeclaration( break; } + if (auto tokenNode = BfNodeDynCast(nextNode)) + { + if (tokenNode->mToken == BfToken_FatArrow) + { + isDone = true; + break; + } + } + tokenNode = ExpectTokenAfter(genericConstraint, BfToken_Comma, BfToken_LBrace, BfToken_Where, BfToken_Semicolon); if (tokenNode == NULL) { diff --git a/IDEHelper/Compiler/BfReducer.h b/IDEHelper/Compiler/BfReducer.h index d233fedb..176536ba 100644 --- a/IDEHelper/Compiler/BfReducer.h +++ b/IDEHelper/Compiler/BfReducer.h @@ -26,7 +26,8 @@ public: CreateExprFlags_ExitOnParenExpr = 0x80, CreateExprFlags_NoCheckBinOpPrecedence = 0x100, CreateExprFlags_BreakOnCascade = 0x200, - CreateExprFlags_EarlyExit = 0x400 // Don't attempt binary or ternary operations + CreateExprFlags_EarlyExit = 0x400, // Don't attempt binary or ternary operations + CreateExprFlags_AllowEmpty = 0x800 }; enum CreateStmtFlags @@ -186,8 +187,8 @@ public: BfTokenNode* ParseMethodParams(BfAstNode* node, SizedArrayImpl* params, SizedArrayImpl* commas, BfToken endToken, bool requireNames); BfTokenNode* ReadArguments(BfAstNode* parentNode, BfAstNode* afterNode, SizedArrayImpl* arguments, SizedArrayImpl* commas, BfToken endToken, bool allowSkippedArgs = false, CreateExprFlags createExprFlags = CreateExprFlags_None); void ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, BfBlock* block); - BfAstNode* ReadTypeMember(BfTokenNode* node, int depth = 0, BfAstNode* deferredHeadNode = NULL); - BfAstNode* ReadTypeMember(BfAstNode* node, int depth = 0, BfAstNode* deferredHeadNode = NULL); + BfAstNode* ReadTypeMember(BfTokenNode* node, bool declStarted = false, int depth = 0, BfAstNode* deferredHeadNode = NULL); + BfAstNode* ReadTypeMember(BfAstNode* node, bool declStarted = false, int depth = 0, BfAstNode* deferredHeadNode = NULL); BfIdentifierNode* CompactQualifiedName(BfAstNode* leftNode); void TryIdentifierConvert(int readPos); void CreateQualifiedNames(BfAstNode* node); diff --git a/IDEHelper/Compiler/BfResolvePass.cpp b/IDEHelper/Compiler/BfResolvePass.cpp index 8431a180..e6ec65cc 100644 --- a/IDEHelper/Compiler/BfResolvePass.cpp +++ b/IDEHelper/Compiler/BfResolvePass.cpp @@ -55,44 +55,50 @@ void BfResolvePassData::RecordReplaceNode(BfAstNode* node) } void BfResolvePassData::HandleMethodReference(BfAstNode* node, BfTypeDef* typeDef, BfMethodDef* methodDef) -{ - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Method) && (mSymbolReferenceTypeDef == typeDef) && (mSymbolReferenceMethodIdx == methodDef->mIdx)) +{ + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Method) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && + (mSymbolReferenceMethodIdx == methodDef->mIdx)) RecordReplaceNode(node); } void BfResolvePassData::HandleFieldReference(BfAstNode* node, BfTypeDef* typeDef, BfFieldDef* fieldDef) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Field) && (mSymbolReferenceTypeDef == typeDef) && (mSymbolReferenceFieldIdx == fieldDef->mIdx)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Field) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && + (mSymbolReferenceFieldIdx == fieldDef->mIdx)) RecordReplaceNode(node); } void BfResolvePassData::HandlePropertyReference(BfAstNode* node, BfTypeDef* typeDef, BfPropertyDef* propDef) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Property) && (mSymbolReferenceTypeDef == typeDef) && (mSymbolReferencePropertyIdx == propDef->mIdx)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Property) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && + (mSymbolReferencePropertyIdx == propDef->mIdx)) RecordReplaceNode(node); } void BfResolvePassData::HandleLocalReference(BfIdentifierNode* identifier, BfTypeDef* typeDef, BfMethodDef* methodDef, int localVarIdx) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Local) && (mSymbolReferenceTypeDef == typeDef) && (mSymbolReferenceMethodIdx == methodDef->mIdx) && (localVarIdx == mSymbolReferenceLocalIdx)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Local) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && + (mSymbolReferenceMethodIdx == methodDef->mIdx) && (localVarIdx == mSymbolReferenceLocalIdx)) RecordReplaceNode(identifier); } void BfResolvePassData::HandleTypeGenericParam(BfAstNode* node, BfTypeDef* typeDef, int genericParamIdx) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_TypeGenericParam) && (mSymbolReferenceTypeDef == typeDef) && (genericParamIdx == mSymbolTypeGenericParamIdx)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_TypeGenericParam) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && (genericParamIdx == mSymbolTypeGenericParamIdx)) RecordReplaceNode(node); } void BfResolvePassData::HandleMethodGenericParam(BfAstNode* node, BfTypeDef* typeDef, BfMethodDef* methodDef, int genericParamIdx) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_MethodGenericParam) && (mSymbolReferenceTypeDef == typeDef) && (mSymbolReferenceMethodIdx == methodDef->mIdx) && (genericParamIdx == mSymbolMethodGenericParamIdx)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_MethodGenericParam) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && + (mSymbolReferenceMethodIdx == methodDef->mIdx) && (genericParamIdx == mSymbolMethodGenericParamIdx)) RecordReplaceNode(node); } void BfResolvePassData::HandleLocalReference(BfIdentifierNode* identifier, BfIdentifierNode* origNameNode, BfTypeDef* typeDef, BfMethodDef* methodDef, int localVarIdx) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Local) && (mSymbolReferenceTypeDef == typeDef) && (mSymbolReferenceMethodIdx == methodDef->mIdx) && (localVarIdx == mSymbolReferenceLocalIdx)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Local) && (mSymbolReferenceTypeDef == typeDef->GetDefinition()) && + (mSymbolReferenceMethodIdx == methodDef->mIdx) && (localVarIdx == mSymbolReferenceLocalIdx)) { if (origNameNode == NULL) origNameNode = identifier; @@ -139,7 +145,7 @@ BfAstNode* BfResolvePassData::FindBaseNode(BfAstNode* node) void BfResolvePassData::HandleTypeReference(BfAstNode* node, BfTypeDef* typeDef) { - if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Type) && (mSymbolReferenceTypeDef == typeDef)) + if ((mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Type) && (mSymbolReferenceTypeDef == typeDef->GetDefinition())) { auto baseNode = FindBaseNode(node); if (baseNode != NULL) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index b8253306..7b85a000 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -758,7 +758,7 @@ bool BfMethodInstance::HasParamsArray() int BfMethodInstance::GetStructRetIdx(bool forceStatic) { - if ((mReturnType->IsComposite()) && (!mReturnType->IsValuelessType()) && (!GetLoweredReturnType()) && (!mIsIntrinsic)) + if ((mReturnType->IsComposite()) && (!mReturnType->IsValuelessType()) && (!GetLoweredReturnType(NULL, NULL, forceStatic)) && (!mIsIntrinsic)) { auto returnTypeInst = mReturnType->ToTypeInstance(); if ((returnTypeInst != NULL) && (returnTypeInst->mHasUnderlyingArray)) @@ -792,10 +792,11 @@ bool BfMethodInstance::HasSelf() return false; } -bool BfMethodInstance::GetLoweredReturnType(BfTypeCode* loweredTypeCode, BfTypeCode* loweredTypeCode2) +bool BfMethodInstance::GetLoweredReturnType(BfTypeCode* loweredTypeCode, BfTypeCode* loweredTypeCode2, bool forceStatic) { // Win32 handler - if ((mMethodDef->mIsStatic) && (mReturnType->IsComposite()) && + if (((mMethodDef->mIsStatic) || (forceStatic)) && + (mReturnType->IsComposite()) && ((mReturnType->mSize == 4) || (mReturnType->mSize == 8))) { auto returnTypeInst = mReturnType->ToTypeInstance(); @@ -816,16 +817,18 @@ bool BfMethodInstance::GetLoweredReturnType(BfTypeCode* loweredTypeCode, BfTypeC } } - return mReturnType->GetLoweredType(mMethodDef->mIsStatic ? BfTypeUsage_Return_Static : BfTypeUsage_Return_NonStatic, loweredTypeCode, loweredTypeCode2); + return mReturnType->GetLoweredType((mMethodDef->mIsStatic || forceStatic) ? BfTypeUsage_Return_Static : BfTypeUsage_Return_NonStatic, loweredTypeCode, loweredTypeCode2); } -bool BfMethodInstance::WantsStructsAttribByVal() +bool BfMethodInstance::WantsStructsAttribByVal(BfType* paramType) { auto owner = GetOwner(); if ((owner->mModule->mCompiler->mOptions.mPlatformType == BfPlatformType_Windows) && (owner->mModule->mCompiler->mOptions.mMachineType == BfMachineType_x64)) return false; - return true; + + auto typeInst = paramType->ToTypeInstance(); + return (typeInst != NULL) && (typeInst->mIsCRepr); } bool BfMethodInstance::IsSkipCall(bool bypassVirtual) @@ -943,7 +946,7 @@ int BfMethodInstance::GetImplicitParamCount() return 0; } -void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name) +void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name, int& namePrefixCount) { if (paramIdx == -1) { @@ -969,16 +972,25 @@ void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name) if (methodParam->mDelegateParamNameCombine) name = paramDef->mName + "__" + invokeMethodInstance->GetParamName(methodParam->mDelegateParamIdx); else - invokeMethodInstance->GetParamName(methodParam->mDelegateParamIdx, name); + invokeMethodInstance->GetParamName(methodParam->mDelegateParamIdx, name, namePrefixCount); return; } name = paramDef->mName; + namePrefixCount = paramDef->mNamePrefixCount; } String BfMethodInstance::GetParamName(int paramIdx) +{ + String paramName; + int namePrefixCount = 0; + GetParamName(paramIdx, paramName, namePrefixCount); + return paramName; +} + +String BfMethodInstance::GetParamName(int paramIdx, int& namePrefixCount) { String paramName; - GetParamName(paramIdx, paramName); + GetParamName(paramIdx, paramName, namePrefixCount); return paramName; } @@ -1172,14 +1184,9 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, { module->PopulateType(mReturnType); - if (mMethodDef->mName.Contains("GroupBy$")) - { - NOP; - } - BfTypeCode loweredReturnTypeCode = BfTypeCode_None; BfTypeCode loweredReturnTypeCode2 = BfTypeCode_None; - if ((!module->mIsComptimeModule) && (GetLoweredReturnType(&loweredReturnTypeCode, &loweredReturnTypeCode2))) + if ((!module->mIsComptimeModule) && (GetLoweredReturnType(&loweredReturnTypeCode, &loweredReturnTypeCode2, forceStatic))) { auto irReturnType = module->GetIRLoweredType(loweredReturnTypeCode, loweredReturnTypeCode2); returnType = irReturnType; @@ -1208,7 +1215,7 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, { returnType = module->mBfIRBuilder->MapType(mReturnType); } - + for (int paramIdx = -1; paramIdx < GetParamCount(); paramIdx++) { BfType* checkType = NULL; @@ -1230,6 +1237,9 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, } else { + if ((paramIdx == 0) && (mMethodDef->mHasExplicitThis)) + continue; // Skip over the explicit 'this' + checkType = GetParamType(paramIdx); } @@ -1254,7 +1264,10 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, checkLowered = true; } else - { + { + if ((checkType->IsComposite()) && (checkType->IsIncomplete())) + module->PopulateType(checkType, BfPopulateType_Data); + if (checkType->IsMethodRef()) { doSplat = true; @@ -1342,9 +1355,6 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, if (checkType2 != NULL) _AddType(checkType2); - - if ((paramIdx == -1) && (mMethodDef->mHasExplicitThis)) - paramIdx++; // Skip over the explicit 'this' } if ((!module->mIsComptimeModule) && (GetStructRetIdx(forceStatic) == 1)) @@ -1550,6 +1560,11 @@ BfTypeInstance::~BfTypeInstance() delete localMethod; delete mHotTypeData; delete mConstHolder; + if ((mTypeDef != NULL) && (mTypeDef->mEmitParent != NULL)) + { + mMethodInstanceGroups.Clear(); + delete mTypeDef; + } } void BfTypeInstance::ReleaseData() @@ -1563,6 +1578,13 @@ void BfTypeInstance::ReleaseData() mInternalAccessMap.Clear(); } +void BfTypeInstance::Dispose() +{ + delete mGenericTypeInfo; + mGenericTypeInfo = NULL; + mTypeDef = NULL; +} + int BfTypeInstance::GetSplatCount() { if (IsValuelessType()) @@ -1576,7 +1598,7 @@ int BfTypeInstance::GetSplatCount() bool BfTypeInstance::IsString() { - return mTypeDef == mContext->mCompiler->mStringTypeDef; + return IsInstanceOf(mContext->mCompiler->mStringTypeDef); } int BfTypeInstance::GetOrigVTableSize() @@ -2276,7 +2298,7 @@ bool BfTypeInstance::IsSpecializedByAutoCompleteMethod() bool BfTypeInstance::IsNullable() { - return (mTypeDef == mContext->mCompiler->mNullableTypeDef); + return IsInstanceOf(mContext->mCompiler->mNullableTypeDef); } bool BfTypeInstance::HasVarConstraints() @@ -2462,7 +2484,10 @@ BfClosureType::~BfClosureType() { mMethodInstanceGroups.Clear(); if (mCreatedTypeDef) + { delete mTypeDef; + mTypeDef = NULL; + } for (auto directAllocNode : mDirectAllocNodes) delete directAllocNode; } @@ -2541,6 +2566,7 @@ BfDelegateType::~BfDelegateType() { mMethodInstanceGroups.Clear(); delete mTypeDef; + mTypeDef = NULL; } ////////////////////////////////////////////////////////////////////////// @@ -2558,7 +2584,10 @@ BfTupleType::~BfTupleType() { mMethodInstanceGroups.Clear(); if (mCreatedTypeDef) + { delete mTypeDef; + mTypeDef = NULL; + } delete mSource; } @@ -2601,6 +2630,7 @@ void BfTupleType::Finish() BfDefBuilder bfDefBuilder(bfSystem); bfDefBuilder.mCurTypeDef = mTypeDef; + bfDefBuilder.mCurDeclaringTypeDef = mTypeDef; bfDefBuilder.FinishTypeDef(true); } @@ -2693,7 +2723,7 @@ bool BfTypeVectorEquals::operator()(const BfTypeVector& lhs, const BfTypeVector& bool BfCustomAttributes::Contains(BfTypeDef* typeDef) { for (auto& customAttr : mAttributes) - if (customAttr.mType->mTypeDef == typeDef) + if (customAttr.mType->mTypeDef->GetDefinition() == typeDef) return true; return false; } @@ -2701,7 +2731,7 @@ bool BfCustomAttributes::Contains(BfTypeDef* typeDef) BfCustomAttribute* BfCustomAttributes::Get(BfTypeDef * typeDef) { for (auto& customAttr : mAttributes) - if (customAttr.mType->mTypeDef == typeDef) + if (customAttr.mType->mTypeDef->GetDefinition() == typeDef) return &customAttr; return NULL; } @@ -3258,10 +3288,18 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa else if (constant->mConstType == BfConstType_Undef) { elementCount = -1; // Marker for undef + if ((arrayType->IsInferredSize()) && ((ctx->mResolveFlags & BfResolveTypeRefFlag_AllowInferredSizedArray) == 0)) + { + ctx->mModule->Fail("Invalid use of inferred-sized array", sizeExpr); + } + } + else if (!BfIRBuilder::IsInt(constant->mTypeCode)) + { + ctx->mFailed = true; + ctx->mModule->Fail("Array size not a constant value", arrayType->mParams[0]); } else { - BF_ASSERT(BfIRBuilder::IsInt(constant->mTypeCode)); elementCount = (intptr)constant->mInt64; if (elementCount < 0) { @@ -3395,6 +3433,8 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa if (ctx->mRootTypeRef != retTypeTypeRef) { auto type = ctx->mModule->ResolveTypeRef(retTypeTypeRef, BfPopulateType_Identity, ctx->mResolveFlags); + if ((type != NULL) && (type->IsRef())) + type = type->GetUnderlyingType(); return Hash(type, ctx, flags, hashSeed); } @@ -3447,7 +3487,8 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa } } - hashVal = ((hashVal ^ (Hash(fieldType, ctx, (BfHashFlags)(BfHashFlag_AllowRef), hashSeed))) << 5) - hashVal; + if (fieldType != NULL) + hashVal = ((hashVal ^ (Hash(fieldType, ctx, (BfHashFlags)(BfHashFlag_AllowRef), hashSeed))) << 5) - hashVal; hashVal = ((hashVal ^ (HashNode(param->mNameNode))) << 5) - hashVal; isFirstParam = true; } @@ -3487,7 +3528,7 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa else { result = ctx->mModule->CreateValueFromExpression(exprModTypeRef->mTarget, NULL, BfEvalExprFlags_DeclType); - } + } } if ((result) && (exprModTypeRef->mToken->mToken == BfToken_Comptype)) @@ -3513,6 +3554,9 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa else cachedResolvedType = result.mType; + if ((cachedResolvedType != NULL) && (cachedResolvedType->IsRef())) + cachedResolvedType = cachedResolvedType->GetUnderlyingType(); + if (cachedResolvedType != NULL) ctx->SetCachedResolvedType(typeRef, cachedResolvedType); } @@ -3691,7 +3735,7 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfType* rhs, LookupContext* ctx) BfTypeInstance* rhsGenericType = (BfTypeInstance*)rhs; if (lhsGenericType->mGenericTypeInfo->mTypeGenericArguments.size() != rhsGenericType->mGenericTypeInfo->mTypeGenericArguments.size()) return false; - if (lhsGenericType->mTypeDef != rhsGenericType->mTypeDef) + if (lhsGenericType->mTypeDef->GetDefinition() != rhsGenericType->mTypeDef->GetDefinition()) return false; for (int i = 0; i < (int)lhsGenericType->mGenericTypeInfo->mTypeGenericArguments.size(); i++) { @@ -3700,7 +3744,7 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfType* rhs, LookupContext* ctx) } } - return lhsInst->mTypeDef == rhsInst->mTypeDef; + return lhsInst->mTypeDef->GetDefinition() == rhsInst->mTypeDef->GetDefinition(); } else if (lhs->IsPrimitiveType()) { @@ -3845,7 +3889,7 @@ bool BfResolvedTypeSet::GenericTypeEquals(BfTypeInstance* lhsGenericType, BfType if ((rhsTypeDef != NULL) && (rootOuterTypeInstance != NULL)) { // See if we're referring to an non-generic inner type where the outer type is generic - if (lhsGenericType->mTypeDef != rhsTypeDef) + if (lhsGenericType->mTypeDef->GetDefinition() != rhsTypeDef->GetDefinition()) return false; BfTypeDef* commonOuterType = ctx->mModule->FindCommonOuterType(rootOuterTypeInstance->mTypeDef, rhsTypeDef->mOuterType); @@ -3876,7 +3920,7 @@ bool BfResolvedTypeSet::GenericTypeEquals(BfTypeInstance* lhsGenericType, BfType } BfTypeDef* elementTypeDef = ctx->mModule->ResolveGenericInstanceDef(rhsGenericTypeInstRef); - if (elementTypeDef != lhsGenericType->mTypeDef) + if (elementTypeDef->GetDefinition() != lhsGenericType->mTypeDef->GetDefinition()) return false; int genericParamOffset = 0; @@ -3950,7 +3994,7 @@ BfTypeDef* BfResolvedTypeSet::LookupContext::ResolveToTypeDef(BfTypeReference* t auto typeInst = type->ToTypeInstance(); if (typeInst == NULL) return NULL; - return typeInst->mTypeDef; + return typeInst->mTypeDef->GetDefinition(); } bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, BfTypeDef* rhsTypeDef, LookupContext* ctx) @@ -3974,6 +4018,8 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* if (auto retTypeRef = BfNodeDynCastExact(rhs)) { auto resolvedType = ctx->mModule->ResolveTypeRef(rhs); + if ((resolvedType != NULL) && (resolvedType->IsRef())) + resolvedType = resolvedType->GetUnderlyingType(); return lhs == resolvedType; } } @@ -4185,7 +4231,7 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* if (rhsTypeDef == NULL) return false; - return lhsInst->mTypeDef == rhsTypeDef; + return lhsInst->IsInstanceOf(rhsTypeDef); } } else if (lhs->IsPrimitiveType()) @@ -4320,13 +4366,12 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* return false; auto constant = ctx->mModule->mBfIRBuilder->GetConstant(typedVal.mValue); - if (constant->mConstType == BfConstType_Undef) + if ((constant->mConstType == BfConstType_Undef) || (!BfIRBuilder::IsInt(constant->mTypeCode))) { elementCount = -1; // Marker for undef } else { - BF_ASSERT(BfIRBuilder::IsInt(constant->mTypeCode)); elementCount = (intptr)constant->mInt64; BF_ASSERT(elementCount >= 0); // Should have been caught in hash } diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 7a02ee55..0f7a6fa9 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -33,7 +33,10 @@ enum BfResolveTypeRefFlags BfResolveTypeRefFlag_NoCreate = 0x400, BfResolveTypeRefFlag_NoWarnOnMut = 0x800, BfResolveTypeRefFlag_DisallowComptime = 0x1000, - BfResolveTypeRefFlag_AllowDotDotDot = 0x2000 + BfResolveTypeRefFlag_AllowDotDotDot = 0x2000, + BfResolveTypeRefFlag_AllowGlobalContainer = 0x4000, + BfResolveTypeRefFlag_AllowInferredSizedArray = 0x8000, + BfResolveTypeRefFlag_AllowGlobalsSelf = 0x10000, }; enum BfTypeNameFlags : uint16 @@ -113,6 +116,7 @@ public: DependencyFlag_NameReference = 0x1000000, DependencyFlag_VirtualCall = 0x2000000, DependencyFlag_WeakReference = 0x4000000, // Keeps alive but won't rebuild + DependencyFlag_ValueTypeSizeDep = 0x8000000, // IE: int32[DepType.cVal] DependencyFlag_DependentUsageMask = ~(DependencyFlag_UnspecializedType | DependencyFlag_MethodGenericArg | DependencyFlag_GenericArgRef) }; @@ -427,6 +431,7 @@ class BfTypeDIReplaceCallback; enum BfTypeDefineState : uint8 { BfTypeDefineState_Undefined, + BfTypeDefineState_Declaring, BfTypeDefineState_Declared, BfTypeDefineState_ResolvingBaseType, BfTypeDefineState_HasInterfaces, @@ -434,6 +439,7 @@ enum BfTypeDefineState : uint8 BfTypeDefineState_CEPostTypeInit, BfTypeDefineState_Defined, BfTypeDefineState_CEAfterFields, + BfTypeDefineState_DefinedAndMethodsSlotting, BfTypeDefineState_DefinedAndMethodsSlotted, }; @@ -929,8 +935,8 @@ public: bool HasParamsArray(); int GetStructRetIdx(bool forceStatic = false); bool HasSelf(); - bool GetLoweredReturnType(BfTypeCode* loweredTypeCode = NULL, BfTypeCode* loweredTypeCode2 = NULL); - bool WantsStructsAttribByVal(); + bool GetLoweredReturnType(BfTypeCode* loweredTypeCode = NULL, BfTypeCode* loweredTypeCode2 = NULL, bool forceStatic = false); + bool WantsStructsAttribByVal(BfType* paramType); bool IsAutocompleteMethod() { /*return mIdHash == -1;*/ return mIsAutocompleteMethod; } bool IsSkipCall(bool bypassVirtual = false); bool IsVarArgs(); @@ -940,8 +946,9 @@ public: bool AllowsSplatting(int paramIdx); int GetParamCount(); int GetImplicitParamCount(); - void GetParamName(int paramIdx, StringImpl& name); - String GetParamName(int paramIdx); + void GetParamName(int paramIdx, StringImpl& name, int& namePrefixCount); + String GetParamName(int paramIdx); + String GetParamName(int paramIdx, int& namePrefixCount); BfType* GetParamType(int paramIdx, bool returnUnderlyingParamsType = false); bool GetParamIsSplat(int paramIdx); BfParamKind GetParamKind(int paramIdx); @@ -1113,7 +1120,7 @@ public: class BfGenericParamInstance { public: - int mGenericParamFlags; + BfGenericParamFlags mGenericParamFlags; BfType* mExternType; Array mInterfaceConstraints; Array mOperatorConstraints; @@ -1124,7 +1131,7 @@ public: BfGenericParamInstance() { mExternType = NULL; - mGenericParamFlags = 0; + mGenericParamFlags = BfGenericParamFlag_None; mTypeConstraint = NULL; mRefCount = 1; } @@ -1817,7 +1824,7 @@ public: class BfCeTypeInfo { -public: +public: Dictionary mOnCompileMap; Dictionary mTypeIFaceMap; Val128 mHash; @@ -1828,7 +1835,7 @@ public: BfCeTypeInfo() { mFailed = false; - mNext = NULL; + mNext = NULL; } }; @@ -1958,9 +1965,10 @@ public: ~BfTypeInstance(); - void ReleaseData(); + void Dispose(); + void ReleaseData(); - virtual bool IsInstanceOf(BfTypeDef* typeDef) override { return typeDef == mTypeDef; } + virtual bool IsInstanceOf(BfTypeDef* typeDef) override { if (typeDef == NULL) return false; return typeDef->GetDefinition() == mTypeDef->GetDefinition(); } virtual BfModule* GetModule() override { return mModule; } virtual BfTypeInstance* ToTypeInstance() override { return this; } virtual bool IsDependentOnUnderlyingType() override { return true; } diff --git a/IDEHelper/Compiler/BfSourceClassifier.cpp b/IDEHelper/Compiler/BfSourceClassifier.cpp index 0a43776d..31858dba 100644 --- a/IDEHelper/Compiler/BfSourceClassifier.cpp +++ b/IDEHelper/Compiler/BfSourceClassifier.cpp @@ -572,7 +572,12 @@ void BfSourceClassifier::Visit(BfMethodDeclaration* methodDeclaration) { BfTypeReference* typeRef = genericConstraint->mTypeRef; if (typeRef != NULL) - SetElementType(typeRef, BfSourceElementType_GenericParam); + { + if (auto namedTypeRef = BfNodeDynCast(typeRef)) + SetElementType(typeRef, BfSourceElementType_GenericParam); + else + VisitChild(typeRef); + } } } } diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index dfce9116..a3bda91d 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -72,23 +72,34 @@ bool BfModule::AddDeferredCallEntry(BfDeferredCallEntry* deferredCallEntry, BfSc SizedArray origParamTypes; BfIRType origReturnType; deferredCallEntry->mModuleMethodInstance.mMethodInstance->GetIRFunctionInfo(this, origReturnType, origParamTypes); - BF_ASSERT(origParamTypes.size() == deferredCallEntry->mScopeArgs.size()); + + int sretIdx = deferredCallEntry->mModuleMethodInstance.mMethodInstance->GetStructRetIdx(); + BF_ASSERT(origParamTypes.size() == deferredCallEntry->mScopeArgs.size() + ((sretIdx != -1) ? 1 : 0)); - for (int paramIdx = 0; paramIdx < (int)deferredCallEntry->mScopeArgs.size(); paramIdx++) + int argIdx = 0; + int paramIdx = 0; + for (int argIdx = 0; argIdx < (int)deferredCallEntry->mScopeArgs.size(); argIdx++, paramIdx++) { - auto scopeArg = deferredCallEntry->mScopeArgs[paramIdx]; - if ((scopeArg.IsConst()) || (scopeArg.IsFake())) + if (argIdx == sretIdx) + paramIdx++; + + auto scopeArg = deferredCallEntry->mScopeArgs[argIdx]; + if ((scopeArg.IsConst()) || (scopeArg.IsFake())) continue; auto prevInsertBlock = mBfIRBuilder->GetInsertBlock(); mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock); auto allocaInst = mBfIRBuilder->CreateAlloca(origParamTypes[paramIdx]); - mBfIRBuilder->ClearDebugLocation(allocaInst); + mBfIRBuilder->ClearDebugLocation_Last(); mBfIRBuilder->SetInsertPoint(prevInsertBlock); if (WantsLifetimes()) + { mBfIRBuilder->CreateLifetimeStart(allocaInst); + mBfIRBuilder->ClearDebugLocation_Last(); + } mBfIRBuilder->CreateStore(scopeArg, allocaInst); - deferredCallEntry->mScopeArgs[paramIdx] = allocaInst; + mBfIRBuilder->ClearDebugLocation_Last(); + deferredCallEntry->mScopeArgs[argIdx] = allocaInst; if (WantsLifetimes()) scopeData->mDeferredLifetimeEnds.push_back(allocaInst); } @@ -1241,7 +1252,8 @@ void BfModule::TryInitVar(BfAstNode* checkNode, BfLocalVariable* localVar, BfTyp if (localVar->mAddr) { initValue = LoadValue(initValue); - mBfIRBuilder->CreateAlignedStore(initValue.mValue, localVar->mAddr, initValue.mType->mAlign); + if (!initValue.mType->IsVar()) + mBfIRBuilder->CreateAlignedStore(initValue.mValue, localVar->mAddr, initValue.mType->mAlign); } if ((varType->IsPointer()) || (varType->IsObjectOrInterface())) @@ -1570,7 +1582,11 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD typeState.mCurVarInitializer = varDecl->mInitializer; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); - unresolvedType = ResolveTypeRef(varDecl->mTypeRef, BfPopulateType_Data, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowRef)); + BfResolveTypeRefFlags flags = (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowRef); + if (varDecl->mInitializer != NULL) + flags = (BfResolveTypeRefFlags)(flags | BfResolveTypeRefFlag_AllowInferredSizedArray); + + unresolvedType = ResolveTypeRef(varDecl->mTypeRef, BfPopulateType_Data, flags); if (unresolvedType == NULL) unresolvedType = GetPrimitiveType(BfTypeCode_Var); resolvedType = unresolvedType; @@ -2200,7 +2216,7 @@ void BfModule::HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl) AssertErrorState(); } -void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArray& arguments, BfAstNode* tooFewRef, BfIRValue phiVal, BfIRBlock& matchedBlock, BfIRBlock falseBlock, bool& hadConditional, bool clearOutOnMismatch) +void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArray& arguments, BfAstNode* tooFewRef, BfIRValue phiVal, BfIRBlock& matchedBlockStart, BfIRBlock& matchedBlockEnd, BfIRBlock& falseBlockStart, BfIRBlock& falseBlockEnd, bool& hadConditional, bool clearOutOnMismatch) { SetAndRestoreValue prevInCondBlock(mCurMethodState->mInConditionalBlock); @@ -2281,7 +2297,7 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr tooFewRef = tupleExpr->mValues[tupleExpr->mValues.size() - 1]; if (tooFewRef == NULL) tooFewRef = tupleExpr->mOpenParen; - HandleCaseEnumMatch_Tuple(tupleElement, tupleExpr->mValues, tooFewRef, phiVal, matchedBlock, falseBlock, hadConditional, clearOutOnMismatch); + HandleCaseEnumMatch_Tuple(tupleElement, tupleExpr->mValues, tooFewRef, phiVal, matchedBlockStart, matchedBlockEnd, falseBlockStart, falseBlockEnd, hadConditional, clearOutOnMismatch); continue; } } @@ -2329,10 +2345,17 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr { tupleElement = LoadValue(tupleElement); + bool isMatchedBlockEnd = matchedBlockEnd == mBfIRBuilder->GetInsertBlock(); + bool isFalseBlockEnd = falseBlockEnd == mBfIRBuilder->GetInsertBlock(); + BfExprEvaluator exprEvaluator(this); exprEvaluator.mExpectingType = tupleFieldInstance->GetResolvedType(); exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_AllowOutExpr; exprEvaluator.Evaluate(expr); + if (isMatchedBlockEnd) + matchedBlockEnd = mBfIRBuilder->GetInsertBlock(); + if (isFalseBlockEnd) + falseBlockEnd = mBfIRBuilder->GetInsertBlock(); auto argValue = exprEvaluator.mResult; if (!argValue) continue; @@ -2372,17 +2395,18 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr auto insertBlock = mBfIRBuilder->GetInsertBlock(); mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), insertBlock); } - matchedBlock = mBfIRBuilder->CreateBlock("match", false); - mBfIRBuilder->CreateCondBr(exprResult.mValue, matchedBlock, falseBlock); - mBfIRBuilder->AddBlock(matchedBlock); - mBfIRBuilder->SetInsertPoint(matchedBlock); + matchedBlockStart = matchedBlockEnd = mBfIRBuilder->CreateBlock("match", false); + + mBfIRBuilder->CreateCondBr(exprResult.mValue, matchedBlockStart, falseBlockStart); + mBfIRBuilder->AddBlock(matchedBlockStart); + mBfIRBuilder->SetInsertPoint(matchedBlockStart); } } } if (!deferredAssigns.empty()) - mBfIRBuilder->SetInsertPoint(matchedBlock); + mBfIRBuilder->SetInsertPoint(matchedBlockEnd); // We assign these only after the value checks succeed for (auto& deferredAssign : deferredAssigns) @@ -2397,7 +2421,7 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr if ((clearOutOnMismatch) && (!deferredAssigns.IsEmpty())) { auto curInsertPoint = mBfIRBuilder->GetInsertBlock(); - mBfIRBuilder->SetInsertPoint(falseBlock); + mBfIRBuilder->SetInsertPoint(falseBlockEnd); for (auto& deferredAssign : deferredAssigns) { auto tupleFieldInstance = &tupleType->mFieldInstances[deferredAssign.mFieldIdx]; @@ -2412,6 +2436,7 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr continue; mBfIRBuilder->CreateMemSet(argValue.mValue, GetConstValue8(0), GetConstValue(argValue.mType->mSize), GetConstValue(argValue.mType->mAlign)); } + falseBlockEnd = mBfIRBuilder->GetInsertBlock(); mBfIRBuilder->SetInsertPoint(curInsertPoint); } @@ -2528,63 +2553,61 @@ BfTypedValue BfModule::TryCaseTupleMatch(BfTypedValue tupleVal, BfTupleExpressio //auto dscrType = enumType->GetDiscriminatorType(); //BfIRValue eqResult = mBfIRBuilder->CreateCmpEQ(tagVal.mValue, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, tagId)); - BfIRBlock falseBlock; - BfIRBlock doneBlock; + BfIRBlock falseBlockStart; + BfIRBlock falseBlockEnd; + BfIRBlock doneBlockStart; if (notEqBlock != NULL) - doneBlock = *notEqBlock; + doneBlockStart = *notEqBlock; else - doneBlock = mBfIRBuilder->CreateBlock("caseDone", false); + doneBlockStart = mBfIRBuilder->CreateBlock("caseDone", false); + BfIRBlock doneBlockEnd = doneBlockStart; if (clearOutOnMismatch) { - falseBlock = mBfIRBuilder->CreateBlock("caseNotEq", false); - mBfIRBuilder->AddBlock(falseBlock); + falseBlockStart = falseBlockEnd = mBfIRBuilder->CreateBlock("caseNotEq", false); + mBfIRBuilder->AddBlock(falseBlockStart); } - BfIRBlock matchedBlock = mBfIRBuilder->CreateBlock("caseMatch", false); + BfIRBlock matchedBlockStart = mBfIRBuilder->CreateBlock("caseMatch", false); if (matchBlock != NULL) - *matchBlock = matchedBlock; - mBfIRBuilder->CreateBr(matchedBlock); - mBfIRBuilder->AddBlock(matchedBlock); + *matchBlock = matchedBlockStart; + mBfIRBuilder->CreateBr(matchedBlockStart); + mBfIRBuilder->AddBlock(matchedBlockStart); - mBfIRBuilder->SetInsertPoint(doneBlock); + mBfIRBuilder->SetInsertPoint(doneBlockEnd); BfIRValue phiVal; if (eqBlock == NULL) phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 2); - if (phiVal) - { - auto falseVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0); - - if (falseBlock) - mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, falseBlock); -// else -// mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, startBlock); - } - - mBfIRBuilder->SetInsertPoint(matchedBlock); - - HandleCaseEnumMatch_Tuple(tupleVal, tupleExpr->mValues, tooFewRef, falseBlock ? BfIRValue() : phiVal, matchedBlock, falseBlock ? falseBlock : doneBlock, hadConditional, clearOutOnMismatch); + + mBfIRBuilder->SetInsertPoint(matchedBlockStart); + BfIRBlock matchedBlockEnd = matchedBlockStart; + HandleCaseEnumMatch_Tuple(tupleVal, tupleExpr->mValues, tooFewRef, falseBlockStart ? BfIRValue() : phiVal, matchedBlockStart, matchedBlockEnd, + falseBlockStart ? falseBlockStart : doneBlockStart, falseBlockEnd ? falseBlockEnd : doneBlockEnd, hadConditional, clearOutOnMismatch); if (phiVal) { + auto falseVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0); + if (falseBlockEnd) + mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, falseBlockEnd); + auto trueVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1); - mBfIRBuilder->AddPhiIncoming(phiVal, trueVal, matchedBlock); + mBfIRBuilder->AddPhiIncoming(phiVal, trueVal, matchedBlockEnd); } if (eqBlock != NULL) mBfIRBuilder->CreateBr(*eqBlock); else - mBfIRBuilder->CreateBr(doneBlock); + mBfIRBuilder->CreateBr(doneBlockStart); - if (falseBlock) + if (falseBlockEnd) { - mBfIRBuilder->SetInsertPoint(falseBlock); - mBfIRBuilder->CreateBr(doneBlock); + mBfIRBuilder->SetInsertPoint(falseBlockEnd); + mBfIRBuilder->CreateBr(doneBlockStart); //mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), falseBlock); } - mBfIRBuilder->AddBlock(doneBlock); - mBfIRBuilder->SetInsertPoint(doneBlock); + mBfIRBuilder->AddBlock(doneBlockStart); + mBfIRBuilder->SetInsertPoint(doneBlockEnd); if (phiVal) return BfTypedValue(phiVal, boolType); @@ -2693,41 +2716,35 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa auto dscrType = enumType->GetDiscriminatorType(); BfIRValue eqResult = mBfIRBuilder->CreateCmpEQ(tagVal.mValue, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, tagId)); - BfIRBlock falseBlock; - BfIRBlock doneBlock; + BfIRBlock falseBlockStart; + BfIRBlock falseBlockEnd; + BfIRBlock doneBlockStart; + BfIRBlock doneBlockEnd; if (notEqBlock != NULL) - doneBlock = *notEqBlock; + doneBlockStart = doneBlockEnd = *notEqBlock; else - doneBlock = mBfIRBuilder->CreateBlock("caseDone", false); + doneBlockStart = doneBlockEnd = mBfIRBuilder->CreateBlock("caseDone", false); if (clearOutOnMismatch) { - falseBlock = mBfIRBuilder->CreateBlock("caseNotEq", false); - mBfIRBuilder->AddBlock(falseBlock); + falseBlockStart = falseBlockEnd = mBfIRBuilder->CreateBlock("caseNotEq", false); + mBfIRBuilder->AddBlock(falseBlockStart); } - BfIRBlock matchedBlock = mBfIRBuilder->CreateBlock("caseMatch", false); + BfIRBlock matchedBlockStart = mBfIRBuilder->CreateBlock("caseMatch", false); + BfIRBlock matchedBlockEnd = matchedBlockStart; if (matchBlock != NULL) - *matchBlock = matchedBlock; - mBfIRBuilder->CreateCondBr(eqResult, matchedBlock, falseBlock ? falseBlock : doneBlock); + *matchBlock = matchedBlockStart; + mBfIRBuilder->CreateCondBr(eqResult, matchedBlockStart, falseBlockStart ? falseBlockStart : doneBlockStart); - mBfIRBuilder->AddBlock(matchedBlock); + mBfIRBuilder->AddBlock(matchedBlockStart); - mBfIRBuilder->SetInsertPoint(doneBlock); + mBfIRBuilder->SetInsertPoint(doneBlockEnd); BfIRValue phiVal; if (eqBlock == NULL) phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 1 + (int)tupleType->mFieldInstances.size()); - if (phiVal) - { - auto falseVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0); - - if (falseBlock) - mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, falseBlock); - else - mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, startBlock); - } - - mBfIRBuilder->SetInsertPoint(matchedBlock); + + mBfIRBuilder->SetInsertPoint(matchedBlockEnd); BfTypedValue tupleVal; if (!enumVal.IsAddr()) @@ -2832,30 +2849,37 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa /// - HandleCaseEnumMatch_Tuple(tupleVal, invocationExpr->mArguments, tooFewRef, falseBlock ? BfIRValue() : phiVal, matchedBlock, falseBlock ? falseBlock : doneBlock, hadConditional, clearOutOnMismatch); + HandleCaseEnumMatch_Tuple(tupleVal, invocationExpr->mArguments, tooFewRef, falseBlockStart ? BfIRValue() : phiVal, matchedBlockStart, matchedBlockEnd, + falseBlockStart ? falseBlockStart : doneBlockStart, falseBlockEnd ? falseBlockEnd : doneBlockEnd, + hadConditional, clearOutOnMismatch); /////// if (phiVal) { + auto falseVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0); + if (falseBlockEnd) + mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, falseBlockEnd); + else + mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, startBlock); auto trueVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1); - mBfIRBuilder->AddPhiIncoming(phiVal, trueVal, matchedBlock); + mBfIRBuilder->AddPhiIncoming(phiVal, trueVal, matchedBlockEnd); } if (eqBlock != NULL) mBfIRBuilder->CreateBr(*eqBlock); else - mBfIRBuilder->CreateBr(doneBlock); + mBfIRBuilder->CreateBr(doneBlockStart); - if (falseBlock) + if (falseBlockEnd) { - mBfIRBuilder->SetInsertPoint(falseBlock); - mBfIRBuilder->CreateBr(doneBlock); + mBfIRBuilder->SetInsertPoint(falseBlockEnd); + mBfIRBuilder->CreateBr(doneBlockStart); //mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), falseBlock); } - mBfIRBuilder->AddBlock(doneBlock); - mBfIRBuilder->SetInsertPoint(doneBlock); + mBfIRBuilder->AddBlock(doneBlockStart); + mBfIRBuilder->SetInsertPoint(doneBlockEnd); if (phiVal) return BfTypedValue(phiVal, boolType); @@ -3210,6 +3234,8 @@ void BfModule::VisitCodeBlock(BfBlock* block) int startLocalMethod = 0; // was -1 auto rootMethodState = mCurMethodState->GetRootMethodState(); + BfIRBlock startInsertBlock = mBfIRBuilder->GetInsertBlock(); + bool allowLocalMethods = mCurMethodInstance != NULL; //int startDeferredLocalIdx = (int)rootMethodState->mDeferredLocalMethods.size(); @@ -3385,9 +3411,39 @@ void BfModule::VisitCodeBlock(BfBlock* block) } else { + auto exprEvaluator = mCurMethodState->mCurScope->mExprEvaluator; + // Evaluate last child as an expression - mCurMethodState->mCurScope->mExprEvaluator->VisitChild(expr); - mCurMethodState->mCurScope->mExprEvaluator->FinishExpressionResult(); + exprEvaluator->VisitChild(expr); + exprEvaluator->FinishExpressionResult(); + + if ((exprEvaluator->mResult) && (!exprEvaluator->mResult.mType->IsValuelessType()) && (!exprEvaluator->mResult.IsAddr()) && (!exprEvaluator->mResult.mValue.IsFake())) + { + if ((mCurMethodState->mCurScope != NULL) && (mCurMethodState->mCurScope->mPrevScope != NULL)) + { + // We need to make sure we don't retain any values through the scope's ValueScopeHardEnd - and extend alloca through previous scope + + bool wasReadOnly = exprEvaluator->mResult.IsReadOnly(); + FixIntUnknown(exprEvaluator->mResult, exprEvaluator->mExpectingType); + auto prevInsertBlock = mBfIRBuilder->GetInsertBlock(); + auto tempVar = CreateAlloca(exprEvaluator->mResult.mType, false, "blockExpr"); + mBfIRBuilder->SetInsertPointAtStart(startInsertBlock); + auto lifetimeStart = mBfIRBuilder->CreateLifetimeStart(tempVar); + mBfIRBuilder->ClearDebugLocation(lifetimeStart); + + if (!mBfIRBuilder->mIgnoreWrites) + mCurMethodState->mCurScope->mPrevScope->mDeferredLifetimeEnds.push_back(tempVar); + mBfIRBuilder->SetInsertPoint(prevInsertBlock); + if (exprEvaluator->mResult.IsSplat()) + AggregateSplatIntoAddr(exprEvaluator->mResult, tempVar); + else + mBfIRBuilder->CreateAlignedStore(exprEvaluator->mResult.mValue, tempVar, exprEvaluator->mResult.mType->mAlign); + exprEvaluator->mResult = BfTypedValue(tempVar, exprEvaluator->mResult.mType, + exprEvaluator->mResult.IsThis() ? + (wasReadOnly ? BfTypedValueKind_ReadOnlyThisAddr : BfTypedValueKind_ThisAddr) : + (wasReadOnly ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr)); + } + } } break; @@ -3621,6 +3677,8 @@ void BfModule::DoIfStatement(BfIfStatement* ifStmt, bool includeTrueStmt, bool i VisitEmbeddedStatement(ifStmt->mTrueStatement); } prevDLA.Restore(); + if (mCurMethodState->mDeferredLocalAssignData != NULL) + mCurMethodState->mDeferredLocalAssignData->mHadBreak |= deferredLocalAssignData.mHadBreak; bool trueHadReturn = mCurMethodState->mHadReturn; @@ -3858,8 +3916,8 @@ void BfModule::Visit(BfDeleteStatement* deleteStmt) bool canAlwaysDelete = checkType->IsDelegate() || checkType->IsFunction() || checkType->IsArray(); if (auto checkTypeInst = checkType->ToTypeInstance()) { - if ((checkTypeInst->mTypeDef == mCompiler->mDelegateTypeDef) || - (checkTypeInst->mTypeDef == mCompiler->mFunctionTypeDef)) + if ((checkTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || + (checkTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef))) canAlwaysDelete = true; } @@ -4285,6 +4343,7 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) mBfIRBuilder->SetInsertPoint(noSwitchBlock); bool isPayloadEnum = switchValue.mType->IsPayloadEnum(); + bool isTuple = switchValue.mType->IsTuple(); bool isIntegralSwitch = switchValue.mType->IsIntegral() || (intCoercibleType != NULL) || ((switchValue.mType->IsEnum()) && (!isPayloadEnum)); auto _ShowCaseError = [&] (int64 id, BfAstNode* errNode) @@ -4365,8 +4424,18 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) hadWhen = true; whenExpr = checkWhenExpr; } + + if (auto invocationExpr = BfNodeDynCast(caseExpr)) + { + if (prevHadFallthrough) + { + Fail("Destructuring cannot be used when the previous case contains a fallthrough", caseExpr); + } + } } + bool wantsOpenedScope = isPayloadEnum || isTuple; + BfIRBlock lastNotEqBlock; for (BfExpression* caseExpr : switchCase->mCaseExpressions) { @@ -4375,7 +4444,7 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) if (auto checkWhenExpr = BfNodeDynCast(caseExpr)) continue; - if ((!openedScope) && (isPayloadEnum)) + if ((!openedScope) && (wantsOpenedScope)) { openedScope = true; @@ -4664,7 +4733,8 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) openedScope = false; deferredLocalAssignDataVec[blockIdx].mHadReturn = hadReturn; caseCount++; - if ((!hadReturn) && (!mCurMethodState->mDeferredLocalAssignData->mHadFallthrough)) + if ((!hadReturn) && + ((!mCurMethodState->mDeferredLocalAssignData->mHadFallthrough) || (mCurMethodState->mDeferredLocalAssignData->mHadBreak))) allHadReturns = false; if (auto block = BfNodeDynCast(switchCase->mCodeBlock)) @@ -4708,31 +4778,38 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) { if (switchValue.mType->IsEnum()) { - auto enumType = switchValue.mType->ToTypeInstance(); - if (enumType->IsPayloadEnum()) + if (hadConstMatch) { - int lastTagId = -1; - for (auto& field : enumType->mFieldInstances) - { - auto fieldDef = field.GetFieldDef(); - if (fieldDef == NULL) - continue; - if (field.mDataIdx < 0) - lastTagId = -field.mDataIdx - 1; - } - isComprehensive = lastTagId == (int)handledCases.size() - 1; + // Already handled } else { - for (auto& field : enumType->mFieldInstances) + auto enumType = switchValue.mType->ToTypeInstance(); + if (enumType->IsPayloadEnum()) { - auto fieldDef = field.GetFieldDef(); - if ((fieldDef != NULL) && (fieldDef->mFieldDeclaration != NULL) && (fieldDef->mFieldDeclaration->mTypeRef == NULL)) + int lastTagId = -1; + for (auto& field : enumType->mFieldInstances) { - if (field.mConstIdx != -1) + auto fieldDef = field.GetFieldDef(); + if (fieldDef == NULL) + continue; + if (field.mDataIdx < 0) + lastTagId = -field.mDataIdx - 1; + } + isComprehensive = lastTagId == (int)handledCases.size() - 1; + } + else + { + for (auto& field : enumType->mFieldInstances) + { + auto fieldDef = field.GetFieldDef(); + if ((fieldDef != NULL) && (fieldDef->mFieldDeclaration != NULL) && (fieldDef->mFieldDeclaration->mTypeRef == NULL)) { - auto constant = enumType->mConstHolder->GetConstantById(field.mConstIdx); - isComprehensive &= handledCases.ContainsKey(constant->mInt64); + if (field.mConstIdx != -1) + { + auto constant = enumType->mConstHolder->GetConstantById(field.mConstIdx); + isComprehensive &= handledCases.ContainsKey(constant->mInt64); + } } } } @@ -5115,7 +5192,10 @@ void BfModule::Visit(BfBreakStatement* breakStmt) while (checkLocalAssignData != NULL) { if ((checkLocalAssignData->mScopeData != NULL) && (checkLocalAssignData->mScopeData->mScopeDepth >= breakData->mScope->mScopeDepth)) + { checkLocalAssignData->mLeftBlock = true; + checkLocalAssignData->mHadBreak = true; + } checkLocalAssignData = checkLocalAssignData->mChainedAssignData; } @@ -5706,9 +5786,13 @@ void BfModule::Visit(BfForStatement* forStmt) } void BfModule::DoForLess(BfForEachStatement* forEachStmt) -{ +{ UpdateSrcPos(forEachStmt); + auto startBB = mBfIRBuilder->GetInsertBlock(); + auto condBB = mBfIRBuilder->CreateBlock("forless.cond", true); + mBfIRBuilder->SetInsertPoint(condBB); + BfScopeData scopeData; // We set mIsLoop later if (forEachStmt->mLabelNode != NULL) @@ -5771,6 +5855,9 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt) target = GetDefaultTypedValue(varType); } PopulateType(varType, BfPopulateType_Data); + + auto condEndBB = mBfIRBuilder->GetInsertBlock(); + mBfIRBuilder->SetInsertPoint(startBB); BfLocalVariable* localDef = new BfLocalVariable(); localDef->mNameNode = BfNodeDynCast(forEachStmt->mVariableName); @@ -5793,8 +5880,7 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt) localDef->Init(); UpdateExprSrcPos(forEachStmt->mVariableName); AddLocalVariableDef(localDef, true); - - auto condBB = mBfIRBuilder->CreateBlock("forless.cond", true); + auto bodyBB = mBfIRBuilder->CreateBlock("forless.body"); auto incBB = mBfIRBuilder->CreateBlock("forless.inc"); auto endBB = mBfIRBuilder->CreateBlock("forless.end"); @@ -5808,7 +5894,7 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt) SetAndRestoreValue prevBreakData(mCurMethodState->mBreakData, &breakData); mBfIRBuilder->CreateBr(condBB); - mBfIRBuilder->SetInsertPoint(condBB); + mBfIRBuilder->SetInsertPoint(condEndBB); if (forEachStmt->mCollectionExpression != NULL) UpdateExprSrcPos(forEachStmt->mCollectionExpression); BfIRValue conditionValue; @@ -5820,8 +5906,6 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt) // Cond auto valueScopeStart = ValueScopeStart(); auto localVal = mBfIRBuilder->CreateLoad(localDef->mAddr); - if ((forEachStmt->mCollectionExpression != NULL) && (!didInference)) - target = CreateValueFromExpression(forEachStmt->mCollectionExpression, varType); if (!target) { // Soldier on @@ -5889,7 +5973,7 @@ void BfModule::DoForLess(BfForEachStatement* forEachStmt) } void BfModule::Visit(BfForEachStatement* forEachStmt) -{ +{ if ((forEachStmt->mInToken != NULL) && ((forEachStmt->mInToken->GetToken() == BfToken_LChevron) || (forEachStmt->mInToken->GetToken() == BfToken_LessEquals))) { @@ -5900,10 +5984,8 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) auto autoComplete = mCompiler->GetAutoComplete(); UpdateSrcPos(forEachStmt); - BfScopeData scopeData; - // We set mIsLoop after the non-looped initializations - if (forEachStmt->mLabelNode != NULL) - scopeData.mLabelNode = forEachStmt->mLabelNode->mLabel; + BfScopeData scopeData; + // We set mIsLoop after the non-looped initializations scopeData.mValueScopeStart = ValueScopeStart(); mCurMethodState->AddScope(&scopeData); NewScopeState(); @@ -5920,7 +6002,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) } BfTypedValue target; - if (forEachStmt->mCollectionExpression != NULL) + if (collectionExpr != NULL) target = CreateValueFromExpression(collectionExpr); if (!target) { @@ -5993,7 +6075,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) if (genericParamInst->mTypeConstraint->IsGenericTypeInstance()) { auto genericConstraintType = (BfTypeInstance*)genericParamInst->mTypeConstraint; - if (genericConstraintType->mTypeDef == mCompiler->mSizedArrayTypeDef) + if (genericConstraintType->IsInstanceOf(mCompiler->mSizedArrayTypeDef)) { varType = genericConstraintType->mGenericTypeInfo->mTypeGenericArguments[0]; isVarEnumerator = true; @@ -6089,7 +6171,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) auto _CheckInterface = [&](BfTypeInstance* interface) { - if (interface->mTypeDef == (isRefExpression ? mCompiler->mGenericIRefEnumeratorTypeDef : mCompiler->mGenericIEnumeratorTypeDef)) + if (interface->IsInstanceOf(isRefExpression ? mCompiler->mGenericIRefEnumeratorTypeDef : mCompiler->mGenericIEnumeratorTypeDef)) { if (genericItrInterface != NULL) { @@ -6121,7 +6203,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) _CheckInterface(interface); } - if (enumeratorTypeInst->mTypeDef == (isRefExpression ? mCompiler->mGenericIRefEnumeratorTypeDef : mCompiler->mGenericIEnumeratorTypeDef)) + if (enumeratorTypeInst->IsInstanceOf(isRefExpression ? mCompiler->mGenericIRefEnumeratorTypeDef : mCompiler->mGenericIEnumeratorTypeDef)) { itrInterface = enumeratorTypeInst; genericItrInterface = itrInterface->ToGenericTypeInstance(); @@ -6287,7 +6369,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) localDef->mAddr = itr.mValue; localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional; localDef->mReadFromId = 0; - localDef->Init(); + localDef->Init(); UpdateSrcPos(forEachStmt); CheckVariableDef(localDef); AddLocalVariableDef(localDef, true); @@ -6400,7 +6482,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) varTypedVal = BfTypedValue(varInst, varType, true); } } - + // Iterator if (itr) { @@ -6422,9 +6504,15 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope); } } - } + } - scopeData.mIsLoop = true; + BfScopeData innerScopeData; + if (forEachStmt->mLabelNode != NULL) + innerScopeData.mLabelNode = forEachStmt->mLabelNode->mLabel; + innerScopeData.mValueScopeStart = ValueScopeStart(); + mCurMethodState->AddScope(&innerScopeData); + NewScopeState(true, false); + innerScopeData.mIsLoop = true; if ((autoComplete != NULL) && (forEachStmt->mVariableTypeRef != NULL)) autoComplete->CheckVarResolution(forEachStmt->mVariableTypeRef, varType); @@ -6432,7 +6520,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) if (isArray || isSizedArray) mBfIRBuilder->CreateStore(GetConstValue(0), itr.mValue); - auto valueScopeStartInner = ValueScopeStart(); + auto valueScopeStartInner = ValueScopeStart(); // We may have a call in the loop body mCurMethodState->mMayNeedThisAccessCheck = true; @@ -6445,7 +6533,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) BfBreakData breakData; breakData.mIRContinueBlock = incBB; breakData.mIRBreakBlock = endBB; - breakData.mScope = &scopeData; + breakData.mScope = &innerScopeData; breakData.mInnerValueScopeStart = valueScopeStartInner; breakData.mPrevBreakData = mCurMethodState->mBreakData; SetAndRestoreValue prevBreakData(mCurMethodState->mBreakData, &breakData); @@ -6679,6 +6767,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) mCurMethodState->mLeftBlockCond = false; RestoreScopeState(); + RestoreScopeState(); } void BfModule::Visit(BfDeferStatement* deferStmt) diff --git a/IDEHelper/Compiler/BfSystem.cpp b/IDEHelper/Compiler/BfSystem.cpp index 6b8b370f..4ff7e3d1 100644 --- a/IDEHelper/Compiler/BfSystem.cpp +++ b/IDEHelper/Compiler/BfSystem.cpp @@ -13,6 +13,9 @@ #include "BeefySysLib/util/AllocDebug.h" +#define STB_SPRINTF_DECORATE(name) BF_stbsp_##name +#include "../../third_party/stb/stb_sprintf.h" + USING_NS_BF; using namespace llvm; @@ -64,11 +67,8 @@ void Beefy::DoBfLog(int fileIdx, const char* fmt ...) va_list argList; va_start(argList, fmt); -#ifdef _WIN32 - int numChars = _vsnprintf(lineStr + strOfs, maxChars, fmt, argList); -#else - int numChars = vsnprintf(lineStr+ strOfs, maxChars, fmt, argList); -#endif + + int numChars = BF_stbsp_vsnprintf(lineStr + strOfs, maxChars, fmt, argList); if (numChars <= maxChars) { if (strOfs + numChars > 0) @@ -383,6 +383,30 @@ BfSizedAtomComposite::~BfSizedAtomComposite() ////////////////////////////////////////////////////////////////////////// +void BfMemberDef::SetName(BfAstNode* nameNode) +{ + StringView sv = nameNode->ToStringView(); + while ((!sv.IsEmpty()) && (sv[0] == '@')) + { + sv.RemoveFromStart(1); + mNamePrefixCount++; + } + mName = sv; +} + +void BfParameterDef::SetName(BfAstNode* nameNode) +{ + StringView sv = nameNode->ToStringView(); + while ((!sv.IsEmpty()) && (sv[0] == '@')) + { + sv.RemoveFromStart(1); + mNamePrefixCount++; + } + mName = sv; +} + +////////////////////////////////////////////////////////////////////////// + bool BfPropertyDef::IsVirtual() { if (((BfPropertyDeclaration*)mFieldDeclaration)->mVirtualSpecifier) @@ -526,6 +550,11 @@ bool BfMethodDef::IsDefaultCtor() return ((mMethodType == BfMethodType_Ctor) || (mMethodType == BfMethodType_CtorNoBody)) && (mParams.IsEmpty()); } +bool BfMethodDef::IsCtorOrInit() +{ + return (mMethodType >= BfMethodType_CtorCalcAppend) && (mMethodType <= BfMethodType_Init); +} + String BfMethodDef::ToString() { String methodText; @@ -611,7 +640,7 @@ void BfTypeDef::Reset() void BfTypeDef::FreeMembers() { - if (!mIsCombinedPartial) + if ((!mIsCombinedPartial) && (mEmitParent == NULL)) mSystem->RemoveNamespaceUsage(mNamespace, mProject); if (mName != NULL) @@ -679,53 +708,6 @@ void BfTypeDef::FreeMembers() mIsNextRevision = false; } -void BfTypeDef::ClearEmitted() -{ - for (auto& partial : mPartials) - partial->ClearEmitted(); - - if (mEmitParser != NULL) - { - mEmitParser->mRefCount--; - BF_ASSERT(mEmitParser->mRefCount >= 0); - mEmitParser = NULL; - } - - if (mHasEmitMembers) - { - for (int methodIdx = (int)mMethods.size() - 1; methodIdx >= 0; methodIdx--) - { - auto methodDef = mMethods[methodIdx]; - if ((methodDef->mAddedAfterEmit) || - ((methodDef->mMethodDeclaration != NULL) && (methodDef->mMethodDeclaration->IsEmitted()))) - { - delete methodDef; - mMethods.RemoveAt(methodIdx); - } - } - - for (int fieldIdx = (int)mFields.size() - 1; fieldIdx >= 0; fieldIdx--) - { - auto fieldDef = mFields[fieldIdx]; - if ((fieldDef->mFieldDeclaration != NULL) && (fieldDef->mFieldDeclaration->IsEmitted())) - { - delete fieldDef; - mFields.RemoveAt(fieldIdx); - } - } - - for (int propIdx = (int)mProperties.size() - 1; propIdx >= 0; propIdx--) - { - auto propDef = mProperties[propIdx]; - if ((propDef->mFieldDeclaration != NULL) && (propDef->mFieldDeclaration->IsEmitted())) - { - delete propDef; - mProperties.RemoveAt(propIdx); - } - } - } -} - void BfTypeDef::PopulateMemberSets() { if ((!mMethodSet.IsEmpty()) || (!mFieldSet.IsEmpty()) || (!mPropertySet.IsEmpty())) @@ -770,14 +752,52 @@ void BfTypeDef::PopulateMemberSets() void BfTypeDef::ClearMemberSets() { + for (auto entry : mMethodSet) + ((BfMethodDef*)entry.mMemberDef)->mNextWithSameName = NULL; mMethodSet.Clear(); + + for (auto entry : mFieldSet) + ((BfFieldDef*)entry.mMemberDef)->mNextWithSameName = NULL; mFieldSet.Clear(); + + for (auto entry : mPropertySet) + ((BfPropertyDef*)entry.mMemberDef)->mNextWithSameName = NULL; mPropertySet.Clear(); } +void BfTypeDef::ClearOldMemberSets() +{ + if ((mMethodSet.mCount > 0) && (mMethods.mSize > mMethodSet.mCount)) + { + for (auto entry : mMethodSet) + ((BfMethodDef*)entry.mMemberDef)->mNextWithSameName = NULL; + mMethodSet.Clear(); + } + + if ((mFieldSet.mCount > 0) && (mFields.mSize > mFieldSet.mCount)) + { + for (auto entry : mFieldSet) + ((BfFieldDef*)entry.mMemberDef)->mNextWithSameName = NULL; + mFieldSet.Clear(); + } + + if ((mPropertySet.mCount > 0) && (mProperties.mSize > mPropertySet.mCount)) + { + for (auto entry : mPropertySet) + ((BfPropertyDef*)entry.mMemberDef)->mNextWithSameName = NULL; + mPropertySet.Clear(); + } +} + BfTypeDef::~BfTypeDef() { - BfLogSysM("BfTypeDef::~BfTypeDef %08X\n", this); + BfLogSysM("BfTypeDef::~BfTypeDef %p\n", this); + + if ((mHash == -1330357811) && (IsEmitted())) + { + NOP; + } + delete mNextRevision; FreeMembers(); @@ -786,12 +806,6 @@ BfTypeDef::~BfTypeDef() mSource->mRefCount--; BF_ASSERT(mSource->mRefCount >= 0); } - - if (mEmitParser != NULL) - { - mEmitParser->mRefCount--; - BF_ASSERT(mEmitParser->mRefCount >= 0); - } } BfSource* BfTypeDef::GetLastSource() @@ -1604,7 +1618,7 @@ void BfPassInstance::SilentFail() mFailedIdx++; } -BfError* BfPassInstance::WarnAt(int warningNumber, const StringImpl& warning, BfSourceData* bfSource, int srcIdx, int srcLen) +BfError* BfPassInstance::WarnAt(int warningNumber, const StringImpl& warning, BfSourceData* bfSource, int srcIdx, int srcLen, bool isDeferred) { mLastWasAdded = false; if ((int) mErrors.size() >= sMaxErrors) @@ -1614,7 +1628,7 @@ BfError* BfPassInstance::WarnAt(int warningNumber, const StringImpl& warning, Bf if ((bfParser != NULL) && (warningNumber > 0) && (!bfParser->IsWarningEnabledAtSrcIndex(warningNumber, srcIdx))) return NULL; - if (!WantsRangeRecorded(bfParser, srcIdx, srcLen, true)) + if (!WantsRangeRecorded(bfParser, srcIdx, srcLen, true, isDeferred)) return NULL; mWarnIdx++; @@ -1629,21 +1643,25 @@ BfError* BfPassInstance::WarnAt(int warningNumber, const StringImpl& warning, Bf errorVal->mError = warning; errorVal->mSrcStart = srcIdx; errorVal->mSrcEnd = srcIdx + srcLen; + errorVal->mIsDeferred = isDeferred; FixSrcStartAndEnd(bfSource, errorVal->mSrcStart, errorVal->mSrcEnd); mErrorSet.Add(BfErrorEntry(errorVal)); mErrors.push_back(errorVal); ++mWarningCount; mLastWasAdded = true; - mLastWasDisplayed = WantsRangeDisplayed(bfParser, srcIdx, srcLen, true); - if (mLastWasDisplayed) + if (!isDeferred) { - String errorStart = "WARNING"; - if ((int)mErrors.size() > 1) - errorStart += StrFormat("(%d)", mErrors.size()); - if (warningNumber > 0) - errorStart += StrFormat(": BF%04d", warningNumber); - MessageAt(":warn", errorStart + ": " + warning, bfParser, srcIdx); + mLastWasDisplayed = WantsRangeDisplayed(bfParser, srcIdx, srcLen, true); + if (mLastWasDisplayed) + { + String errorStart = "WARNING"; + if ((int)mErrors.size() > 1) + errorStart += StrFormat("(%d)", mErrors.size()); + if (warningNumber > 0) + errorStart += StrFormat(": BF%04d", warningNumber); + MessageAt(":warn", errorStart + ": " + warning, bfParser, srcIdx); + } } return errorVal; } @@ -1660,7 +1678,7 @@ BfError* BfPassInstance::Warn(int warningNumber, const StringImpl& warning) return NULL; } -BfError* BfPassInstance::Warn(int warningNumber, const StringImpl& warning, BfAstNode* refNode) +BfError* BfPassInstance::Warn(int warningNumber, const StringImpl& warning, BfAstNode* refNode, bool isDeferred) { BP_ZONE("BfPassInstance::Warn"); @@ -1681,7 +1699,7 @@ BfError* BfPassInstance::Warn(int warningNumber, const StringImpl& warning, BfAs } if (refNode != NULL) - return WarnAt(warningNumber, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength()); + return WarnAt(warningNumber, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength(), isDeferred); else return Warn(warningNumber, warning); } @@ -1701,6 +1719,48 @@ BfError* BfPassInstance::WarnAfter(int warningNumber, const StringImpl& warning, return WarnAt(warningNumber, warning, refNode->GetSourceData(), refNode->GetSrcEnd()); } +BfError* BfPassInstance::WarnAfterAt(int warningNumber, const StringImpl& error, BfSourceData* bfSource, int srcIdx) +{ + BP_ZONE("BfPassInstance::FailAfterAt"); + + mFailedIdx++; + if ((int)mErrors.size() >= sMaxErrors) + return NULL; + + auto bfParser = bfSource->ToParserData(); + if ((bfParser != NULL) && (warningNumber > 0) && (!bfParser->IsWarningEnabledAtSrcIndex(warningNumber, srcIdx))) + return NULL; + + if (!WantsRangeRecorded(bfParser, srcIdx, 1, false)) + return NULL; + + // Go to start of UTF8 chunk +// int startIdx = srcIdx; +// int spanLenth = 0; +// UTF8GetGraphemeClusterSpan(bfParser->mSrc, bfParser->mOrigSrcLength, srcIdx, startIdx, spanLenth); + + BfError* errorVal = new BfError(); + errorVal->mIsWarning = true; + errorVal->SetSource(this, bfSource); + errorVal->mIsAfter = true; + errorVal->mError = error; + errorVal->mSrcStart = srcIdx; + errorVal->mSrcEnd = srcIdx + 1; + FixSrcStartAndEnd(bfSource, errorVal->mSrcStart, errorVal->mSrcEnd); + mErrorSet.Add(BfErrorEntry(errorVal)); + mErrors.push_back(errorVal); + + mLastWasDisplayed = WantsRangeDisplayed(bfParser, srcIdx - 1, 2, false); + if (mLastWasDisplayed) + { + String errorStart = "WARNING"; + /*if ((int)mErrors.size() > 1) + errorStart += StrFormat(" #%d", mErrors.size());*/ + MessageAt(":warn", errorStart + ": " + error, bfParser, srcIdx + 1, 1); + } + return errorVal; +} + BfMoreInfo* BfPassInstance::MoreInfoAt(const StringImpl& info, BfSourceData* bfSource, int srcIdx, int srcLen, BfFailFlags flags) { if (!mLastWasDisplayed) @@ -1771,15 +1831,39 @@ void BfPassInstance::TryFlushDeferredError() // This can happen in the case of an internal compiler error, where we believe we've satisfied // generic constraints but we generate an error on the specialization but not the unspecialized version bool hasDisplayedError = false; - for (int pass = 0; pass < 2; pass++) + bool hasDisplayedWarning = false; + for (int pass = 0; pass < 2; pass++) { for (auto& error : mErrors) { - if (!error->mIsWarning) + if (error->mIsWarning) + { + if (!error->mIsDeferred) + hasDisplayedWarning = true; + else if ((pass == 1) && (!hasDisplayedWarning)) + { + String errorText = "WARNING "; + if (error->mWarningNumber > 0) + errorText += StrFormat(": BF%04d", error->mWarningNumber); + errorText += ": "; + errorText += error->mError; + + MessageAt(":warning", errorText, error->mSource, error->mSrcStart, error->mSrcEnd - error->mSrcStart); + + for (auto moreInfo : error->mMoreInfo) + { + if (moreInfo->mSource != NULL) + MessageAt(":warning", " > " + moreInfo->mInfo, moreInfo->mSource, moreInfo->mSrcStart, moreInfo->mSrcEnd - moreInfo->mSrcStart); + else + OutputLine(":warning " + moreInfo->mInfo); + } + } + } + else { if (!error->mIsDeferred) hasDisplayedError = true; - else if (pass == 1) + else if ((pass == 1) && (!hasDisplayedError)) { MessageAt(":error", "ERROR: " + error->mError, error->mSource, error->mSrcStart, error->mSrcEnd - error->mSrcStart); @@ -1792,10 +1876,7 @@ void BfPassInstance::TryFlushDeferredError() } } } - } - - if ((pass == 0) && (hasDisplayedError)) - break; + } } } @@ -2089,18 +2170,21 @@ void BfSystem::SanityCheckAtomComposite(const BfAtomComposite& atomComposite) void BfSystem::TrackName(BfTypeDef* typeDef) { - for (int i = 0; i < (int)typeDef->mFullName.mSize - 1; i++) + if (!typeDef->IsEmitted()) { - auto prevAtom = typeDef->mFullName.mParts[i]; - auto atom = typeDef->mFullName.mParts[i + 1]; - int* countPtr; - if (atom->mPrevNamesMap.TryAdd(prevAtom, NULL, &countPtr)) + for (int i = 0; i < (int)typeDef->mFullName.mSize - 1; i++) { - *countPtr = 1; - } - else - { - (*countPtr)++; + auto prevAtom = typeDef->mFullName.mParts[i]; + auto atom = typeDef->mFullName.mParts[i + 1]; + int* countPtr; + if (atom->mPrevNamesMap.TryAdd(prevAtom, NULL, &countPtr)) + { + *countPtr = 1; + } + else + { + (*countPtr)++; + } } } } @@ -2113,7 +2197,7 @@ void BfSystem::UntrackName(BfTypeDef* typeDef) nameAtom->mAtomUpdateIdx = ++mAtomUpdateIdx; } - if (!typeDef->mIsCombinedPartial) + if ((!typeDef->mIsCombinedPartial) && (!typeDef->IsEmitted())) { for (int i = 0; i < (int)typeDef->mFullName.mSize - 1; i++) { @@ -2629,7 +2713,10 @@ void BfSystem::InjectNewRevision(BfTypeDef* typeDef) { BfLogSys(this, "InjectNewRevision from %p (decl:%p) into %p (decl:%p)\n", typeDef->mNextRevision, typeDef->mNextRevision->mTypeDeclaration, typeDef, typeDef->mTypeDeclaration); - typeDef->ClearEmitted(); + if (typeDef->mName->ToString() == "Zonk") + { + NOP; + } bool setDeclaringType = !typeDef->mIsCombinedPartial; @@ -2690,7 +2777,7 @@ void BfSystem::InjectNewRevision(BfTypeDef* typeDef) methodDef->mCodeChanged = true; nextMethodDef->mParams.Clear(); nextMethodDef->mGenericParams.Clear(); - } + } } else { @@ -2981,7 +3068,7 @@ void BfSystem::AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* co { BfPropertyDef* newProp = new BfPropertyDef(); *newProp = *prop; - + BF_ASSERT(newProp->mDeclaringType != NULL); for (int methodIdx = 0; methodIdx < (int)newProp->mMethods.size(); methodIdx++) newProp->mMethods[methodIdx] = typeDef->mMethods[startMethodIdx + newProp->mMethods[methodIdx]->mIdx]; typeDef->mProperties.push_back(newProp); @@ -3147,6 +3234,273 @@ void BfSystem::FinishCompositePartial(BfTypeDef* compositeTypeDef) VerifyTypeDef(nextRevision); } +void BfSystem::CopyTypeDef(BfTypeDef* typeDef, BfTypeDef* fromTypeDef) +{ + BfLogSys(this, "CopyTypeDef %p from %p Hash: %d\n", typeDef, fromTypeDef, fromTypeDef->mHash); + + for (auto fromMethodDef : fromTypeDef->mMethods) + { + BfMethodDef* methodDef; + if (fromMethodDef->mIsOperator) + { + auto fromOperatorDef = (BfOperatorDef*)fromMethodDef; + auto operatorDef = new BfOperatorDef(); + methodDef = operatorDef; + *operatorDef = *fromOperatorDef; + } + else + { + methodDef = new BfMethodDef(); + *methodDef = *fromMethodDef; + } + + if (methodDef->mDeclaringType == fromTypeDef) + methodDef->mDeclaringType = typeDef; + + for (int paramIdx = 0; paramIdx < fromMethodDef->mParams.mSize; paramIdx++) + { + auto fromParamDef = fromMethodDef->mParams[paramIdx]; + BfParameterDef* paramDef = new BfParameterDef(); + *paramDef = *fromParamDef; + methodDef->mParams[paramIdx] = paramDef; + } + + for (int genericParamIdx = 0; genericParamIdx < fromMethodDef->mGenericParams.mSize; genericParamIdx++) + { + auto fromGenericParam = fromMethodDef->mGenericParams[genericParamIdx]; + BfGenericParamDef* genericParam = new BfGenericParamDef(); + *genericParam = *fromGenericParam; + methodDef->mGenericParams[genericParamIdx] = genericParam; + } + + methodDef->mNextWithSameName = NULL; + typeDef->mMethods.Add(methodDef); + } + + for (auto operatorDef : fromTypeDef->mOperators) + { + auto methodDef = typeDef->mMethods[operatorDef->mIdx]; + BF_ASSERT(methodDef->mIsOperator); + if (methodDef->mIsOperator) + typeDef->mOperators.Add((BfOperatorDef*)methodDef); + } + + for (auto fromPropDef : fromTypeDef->mProperties) + { + BfPropertyDef* propDef = new BfPropertyDef(); + *propDef = *fromPropDef; + if (propDef->mDeclaringType == fromTypeDef) + propDef->mDeclaringType = typeDef; + for (auto& methodDef : propDef->mMethods) + methodDef = typeDef->mMethods[methodDef->mIdx]; + propDef->mNextWithSameName = NULL; + typeDef->mProperties.Add(propDef); + } + + for (auto fromField : fromTypeDef->mFields) + { + BfFieldDef* fieldDef = new BfFieldDef(); + *fieldDef = *fromField; + if (fieldDef->mDeclaringType == fromTypeDef) + fieldDef->mDeclaringType = typeDef; + fieldDef->mNextWithSameName = NULL; + typeDef->mFields.Add(fieldDef); + } + + typeDef->mSystem = fromTypeDef->mSystem; + typeDef->mProject = fromTypeDef->mProject; + typeDef->mPartialIdx = fromTypeDef->mPartialIdx; + typeDef->mTypeDeclaration = fromTypeDef->mTypeDeclaration; + typeDef->mHash = fromTypeDef->mHash; + typeDef->mSignatureHash = fromTypeDef->mSignatureHash; + typeDef->mFullHash = fromTypeDef->mFullHash; + typeDef->mInlineHash = fromTypeDef->mInlineHash; + typeDef->mNestDepth = fromTypeDef->mNestDepth; + + typeDef->mOuterType = fromTypeDef->mOuterType; + //typeDef->mOuterType = fromTypeDef->mOuterType; + typeDef->mNamespace = fromTypeDef->mNamespace; + + typeDef->mName = fromTypeDef->mName; + if (typeDef->mName != mEmptyAtom) + typeDef->mName->mRefCount++; + + //typeDef->mName = fromTypeDef->mName; + typeDef->mNameEx = fromTypeDef->mNameEx; + if (typeDef->mNameEx != NULL) + typeDef->mNameEx->mRefCount++; + //typeDef->mNameEx = fromTypeDef->mNameEx; + typeDef->mFullName = fromTypeDef->mFullName; + + typeDef->mFullNameEx = fromTypeDef->mFullNameEx; + //RefAtomComposite(typeDef->mFullNameEx); + + typeDef->mProtection = fromTypeDef->mProtection; + + typeDef->mTypeCode = fromTypeDef->mTypeCode; + + typeDef->mTypeCode = fromTypeDef->mTypeCode; + + typeDef->mIsAlwaysInclude = fromTypeDef->mIsAlwaysInclude; + typeDef->mIsNoDiscard = fromTypeDef->mIsNoDiscard; + typeDef->mIsPartial = fromTypeDef->mIsPartial; + typeDef->mIsExplicitPartial = fromTypeDef->mIsExplicitPartial; + //mPartialUsed + typeDef->mIsCombinedPartial = fromTypeDef->mIsCombinedPartial; + typeDef->mIsDelegate = fromTypeDef->mIsDelegate; + typeDef->mIsFunction = fromTypeDef->mIsFunction; + typeDef->mIsClosure = fromTypeDef->mIsClosure; + typeDef->mIsAbstract = fromTypeDef->mIsAbstract; + typeDef->mIsStatic = fromTypeDef->mIsStatic; + typeDef->mHasAppendCtor = fromTypeDef->mHasAppendCtor; + typeDef->mHasCEOnCompile = fromTypeDef->mHasCEOnCompile; + typeDef->mHasCtorNoBody = fromTypeDef->mHasCtorNoBody; + typeDef->mHasOverrideMethods = fromTypeDef->mHasOverrideMethods; + typeDef->mHasExtensionMethods = fromTypeDef->mHasExtensionMethods; + typeDef->mIsOpaque = fromTypeDef->mIsOpaque; + + typeDef->mDupDetectedRevision = fromTypeDef->mDupDetectedRevision; + + typeDef->mDirectAllocNodes = fromTypeDef->mDirectAllocNodes; + fromTypeDef->mDirectAllocNodes.Clear(); + + typeDef->mNamespaceSearch = fromTypeDef->mNamespaceSearch; + for (auto name : typeDef->mNamespaceSearch) + RefAtomComposite(name); + + typeDef->mStaticSearch = fromTypeDef->mStaticSearch; + typeDef->mInternalAccessSet = fromTypeDef->mInternalAccessSet; + + for (auto fromGenericParamDef : fromTypeDef->mGenericParamDefs) + { + BfGenericParamDef* genericParamDef = new BfGenericParamDef(); + *genericParamDef = *fromGenericParamDef; + typeDef->mGenericParamDefs.Add(genericParamDef); + } + + typeDef->mExternalConstraints = fromTypeDef->mExternalConstraints; + + typeDef->mBaseTypes = fromTypeDef->mBaseTypes; + typeDef->mNestedTypes = fromTypeDef->mNestedTypes; + + typeDef->mPartials = fromTypeDef->mPartials; + + VerifyTypeDef(typeDef); +} + +void BfSystem::UpdateEmittedTypeDef(BfTypeDef* typeDef) +{ + auto fromTypeDef = typeDef->mEmitParent; + + BF_ASSERT(fromTypeDef->mNextRevision == NULL); + + BfLogSys(this, "UpdateTypeDefCopy %p from %p (decl:%p)\n", typeDef, fromTypeDef, fromTypeDef->mTypeDeclaration); + + BF_ASSERT((typeDef->mDefState == BfTypeDef::DefState_Emitted) || (typeDef->mDefState == BfTypeDef::DefState_EmittedDirty)); + BF_ASSERT((fromTypeDef->mDefState != BfTypeDef::DefState_Emitted) && (fromTypeDef->mDefState != BfTypeDef::DefState_EmittedDirty)); + + typeDef->mTypeDeclaration = fromTypeDef->mTypeDeclaration; + typeDef->mOuterType = fromTypeDef->mOuterType; + + for (int methodIdx = 0; methodIdx < (int)typeDef->mMethods.size(); methodIdx++) + { + auto methodDef = typeDef->mMethods[methodIdx]; + if (methodIdx >= fromTypeDef->mMethods.mSize) + { + BF_ASSERT(methodDef->mDeclaringType == typeDef); + continue; + } + + BF_ASSERT(methodDef->mDeclaringType != typeDef); + + for (auto param : methodDef->mParams) + delete param; + for (auto genericParam : methodDef->mGenericParams) + delete genericParam; + + auto fromMethodDef = fromTypeDef->mMethods[methodIdx]; + + if ((fromMethodDef->mIsOperator) && (methodDef->mIsOperator)) + { + auto fromOperatorDef = (BfOperatorDef*)fromMethodDef; + auto operatorDef = (BfOperatorDef*)methodDef; + *operatorDef = *fromOperatorDef; + } + else + { + *methodDef = *fromMethodDef; + } + + for (int paramIdx = 0; paramIdx < fromMethodDef->mParams.mSize; paramIdx++) + { + auto fromParamDef = fromMethodDef->mParams[paramIdx]; + BfParameterDef* paramDef = new BfParameterDef(); + *paramDef = *fromParamDef; + methodDef->mParams[paramIdx] = paramDef; + } + + for (int genericParamIdx = 0; genericParamIdx < fromMethodDef->mGenericParams.mSize; genericParamIdx++) + { + auto fromGenericParam = fromMethodDef->mGenericParams[genericParamIdx]; + BfGenericParamDef* genericParam = new BfGenericParamDef(); + *genericParam = *fromGenericParam; + methodDef->mGenericParams[genericParamIdx] = genericParam; + } + } + + typeDef->mOperators.Clear(); + for (auto operatorDef : fromTypeDef->mOperators) + { + auto methodDef = typeDef->mMethods[operatorDef->mIdx]; + BF_ASSERT(methodDef->mIsOperator); + if (methodDef->mIsOperator) + typeDef->mOperators.Add((BfOperatorDef*)methodDef); + } + + for (int fieldIdx = 0; fieldIdx < typeDef->mFields.mSize; fieldIdx++) + { + auto fieldDef = typeDef->mMethods[fieldIdx]; + if (fieldIdx >= fromTypeDef->mFields.mSize) + { + BF_ASSERT(fieldDef->mDeclaringType == typeDef); + continue; + } + + BF_ASSERT(fieldDef->mDeclaringType != typeDef); + auto fromFieldDef = fromTypeDef->mMethods[fieldIdx]; + fieldDef->mDeclaringType = fromFieldDef->mDeclaringType; + } + + for (int propertyIdx = 0; propertyIdx < typeDef->mProperties.mSize; propertyIdx++) + { + auto propertyDef = typeDef->mProperties[propertyIdx]; + if (propertyIdx >= fromTypeDef->mProperties.mSize) + { + BF_ASSERT(propertyDef->mDeclaringType == typeDef); + continue; + } + + BF_ASSERT(propertyDef->mDeclaringType != typeDef); + auto fromPropertyDef = fromTypeDef->mProperties[propertyIdx]; + propertyDef->mDeclaringType = fromPropertyDef->mDeclaringType; + } + + typeDef->mGenericParamDefs.Clear(); + for (auto fromGenericParamDef : fromTypeDef->mGenericParamDefs) + { + BfGenericParamDef* genericParamDef = new BfGenericParamDef(); + *genericParamDef = *fromGenericParamDef; + typeDef->mGenericParamDefs.Add(genericParamDef); + } + + typeDef->mPartials = fromTypeDef->mPartials; + + if (typeDef->mDefState == BfTypeDef::DefState_EmittedDirty) + typeDef->mDefState = BfTypeDef::DefState_Emitted; + + BF_ASSERT(typeDef->mDefState == BfTypeDef::DefState_Emitted); +} + BfTypeDef* BfSystem::GetCombinedPartial(BfTypeDef* typeDef) { if ((!typeDef->mIsPartial) || (typeDef->mIsCombinedPartial)) @@ -4154,3 +4508,5 @@ BF_EXPORT void BF_CALLTYPE BfSystem_Log(BfSystem* bfSystem, char* str) BfLogSys(bfSystem, str); BfLogSys(bfSystem, "\n"); } + + diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index cec7a07d..42a35340 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -507,6 +507,7 @@ public: BfParameterDeclaration* mParamDeclaration; int mMethodGenericParamIdx; BfParamKind mParamKind; + uint8 mNamePrefixCount; // Number of @'s public: BfParameterDef() @@ -515,7 +516,9 @@ public: mMethodGenericParamIdx = -1; mParamKind = BfParamKind_Normal; mParamDeclaration = NULL; + mNamePrefixCount = 0; } + void SetName(BfAstNode* nameNode); }; class BfMemberDef @@ -528,6 +531,7 @@ public: #endif BfTypeDef* mDeclaringType; BfProtection mProtection; + uint8 mNamePrefixCount; // Number of @'s bool mIsStatic; bool mIsNoShow; bool mIsReadOnly; @@ -538,6 +542,7 @@ public: { mDeclaringType = NULL; mProtection = BfProtection_Public; + mNamePrefixCount = 0; mIsStatic = false; mIsNoShow = false; mIsReadOnly = false; @@ -547,6 +552,8 @@ public: virtual ~BfMemberDef() { } + + void SetName(BfAstNode* nameNode); }; class BfFieldDef : public BfMemberDef @@ -870,6 +877,7 @@ public: bool HasBody(); bool IsEmptyPartial(); bool IsDefaultCtor(); + bool IsCtorOrInit(); String ToString(); int GetExplicitParamCount(); }; @@ -941,7 +949,9 @@ public: DefState_InlinedInternals_Changed, // Code within methods, including inlined methods, changed DefState_Internals_Changed, // Only code within a non-inlined methods changed DefState_Refresh, - DefState_Deleted + DefState_Deleted, + DefState_Emitted, + DefState_EmittedDirty }; public: @@ -950,13 +960,13 @@ public: BfSystem* mSystem; BfProject* mProject; BfTypeDeclaration* mTypeDeclaration; - BfSource* mSource; - BfParser* mEmitParser; + BfSource* mSource; DefState mDefState; Val128 mSignatureHash; // Data, methods, etc Val128 mFullHash; Val128 mInlineHash; + BfTypeDef* mEmitParent; BfTypeDef* mOuterType; BfAtomComposite mNamespace; BfAtom* mName; @@ -1004,8 +1014,7 @@ public: bool mHasOverrideMethods; bool mIsOpaque; bool mIsNextRevision; - bool mInDeleteQueue; - bool mHasEmitMembers; + bool mInDeleteQueue; bool mForceUseNextRevision; public: @@ -1029,8 +1038,7 @@ public: mIsPartial = false; mIsCombinedPartial = false; mTypeDeclaration = NULL; - mSource = NULL; - mEmitParser = NULL; + mSource = NULL; mDefState = DefState_New; mHash = 0; mPartialIdx = -1; @@ -1047,11 +1055,11 @@ public: mIsOpaque = false; mPartialUsed = false; mIsNextRevision = false; - mInDeleteQueue = false; - mHasEmitMembers = false; + mInDeleteQueue = false; mForceUseNextRevision = false; mDupDetectedRevision = -1; mNestDepth = 0; + mEmitParent = NULL; mOuterType = NULL; mTypeDeclaration = NULL; mNextRevision = NULL; @@ -1062,9 +1070,9 @@ public: bool IsGlobalsContainer(); void Reset(); void FreeMembers(); - void ClearEmitted(); void PopulateMemberSets(); void ClearMemberSets(); + void ClearOldMemberSets(); void RemoveGenericParamDef(BfGenericParamDef* genericParamDef); int GetSelfGenericParamCount(); String ToString(); @@ -1074,8 +1082,19 @@ public: String GetAutoPropertyName(BfPropertyDeclaration* propertyDeclaration); BfAstNode* GetRefNode(); + bool IsEmitted() { return mEmitParent != NULL; } + + BfTypeDef* GetDefinition() + { + if (mEmitParent != NULL) + return mEmitParent; + return this; + } + BfTypeDef* GetLatest() { + if (mEmitParent != NULL) + return mEmitParent->GetLatest(); if (mNextRevision != NULL) return mNextRevision; return this; @@ -1123,14 +1142,15 @@ enum BfTargetType { BfTargetType_BeefConsoleApplication, BfTargetType_BeefWindowsApplication, - BfTargetType_BeefLib, - BfTargetType_BeefDynLib, + BfTargetType_BeefLib, BfTargetType_CustomBuild, BfTargetType_BeefTest, BfTargetType_C_ConsoleApplication, BfTargetType_C_WindowsApplication, BfTargetType_BeefApplication_StaticLib, - BfTargetType_BeefApplication_DynamicLib + BfTargetType_BeefApplication_DynamicLib, + BfTargetType_BeefLib_StaticLib, + BfTargetType_BeefLib_DynamicLib, }; enum BfProjectFlags @@ -1371,10 +1391,12 @@ public: void MessageAt(const StringImpl& msgPrefix, const StringImpl& error, BfSourceData* bfSource, int srcIdx, int srcLen = 1, BfFailFlags flags = BfFailFlag_None); void FixSrcStartAndEnd(BfSourceData* source, int& startIdx, int& endIdx); - BfError* WarnAt(int warningNumber, const StringImpl& warning, BfSourceData* bfSource, int srcIdx, int srcLen = 1); + BfError* WarnAt(int warningNumber, const StringImpl& warning, BfSourceData* bfSource, int srcIdx, int srcLen = 1, bool isDeferred = false); BfError* Warn(int warningNumber, const StringImpl& warning); - BfError* Warn(int warningNumber, const StringImpl& warning, BfAstNode* refNode); + BfError* Warn(int warningNumber, const StringImpl& warning, BfAstNode* refNode, bool isDeferred = false); + BfError* DeferWarn(int warningNumber, const StringImpl& warning, BfAstNode* refNode); BfError* WarnAfter(int warningNumber, const StringImpl& warning, BfAstNode* refNode); + BfError* WarnAfterAt(int warningNumber, const StringImpl& error, BfSourceData* bfSource, int srcIdx); BfMoreInfo* MoreInfoAt(const StringImpl& info, BfSourceData* bfSource, int srcIdx, int srcLen, BfFailFlags flags = BfFailFlag_None); BfMoreInfo* MoreInfo(const StringImpl& info, bool forceQueue = false); @@ -1610,6 +1632,9 @@ public: void InjectNewRevision(BfTypeDef* typeDef); void AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* compositeTypeDef, BfTypeDef* partialTypeDef); void FinishCompositePartial(BfTypeDef* compositeTypeDef); + void CopyTypeDef(BfTypeDef* typeDef, BfTypeDef* nextTypeDef); + void UpdateEmittedTypeDef(BfTypeDef* typeDef); + BfTypeDef* GetCombinedPartial(BfTypeDef* typeDef); BfTypeDef* GetOuterTypeNonPartial(BfTypeDef* typeDef); diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index 17375e6d..0b9384c3 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -1120,7 +1120,7 @@ CeOperand CeBuilder::GetOperand(BeValue* value, bool allowAlloca, bool allowImme if (checkBuilder->mInnerFunctionMap.TryGetValue(beFunction, &innerFunctionIdx)) { auto innerFunction = checkBuilder->mCeFunction->mInnerFunctions[innerFunctionIdx]; - if (!innerFunction->mInitialized) + if (innerFunction->mInitializeState < CeFunction::InitializeState_Initialized) mCeMachine->PrepareFunction(innerFunction, checkBuilder); CeOperand result = FrameAlloc(mCeMachine->GetBeContext()->GetPrimitiveType((sizeof(BfMethodInstance*) == 8) ? BeTypeCode_Int64 : BeTypeCode_Int32)); @@ -1288,7 +1288,7 @@ void CeBuilder::Build() auto methodInstance = mCeFunction->mMethodInstance; if (methodInstance != NULL) - { + { BfMethodInstance dupMethodInstance; dupMethodInstance.CopyFrom(methodInstance); auto methodDef = methodInstance->mMethodDef; @@ -1313,7 +1313,9 @@ void CeBuilder::Build() int startFunctionCount = (int)beModule->mFunctions.size(); ProcessMethod(methodInstance, &dupMethodInstance); - + if (mCeFunction->mInitializeState == CeFunction::InitializeState_Initialized) + return; + if (!dupMethodInstance.mIRFunction) { mCeFunction->mFailed = true; @@ -2855,6 +2857,7 @@ void CeBuilder::Build() CeContext::CeContext() { + mPrevContext = NULL; mCurEvalFlags = CeEvalFlags_None; mCeMachine = NULL; mReflectTypeIdOffset = -1; @@ -2876,6 +2879,8 @@ CeContext::~CeContext() BfError* CeContext::Fail(const StringImpl& error) { + if (mCurEmitContext != NULL) + mCurEmitContext->mFailed = true; auto bfError = mCurModule->Fail(StrFormat("Unable to comptime %s", mCurModule->MethodToString(mCurMethodInstance).c_str()), mCurTargetSrc, (mCurEvalFlags & CeEvalFlags_PersistantError) != 0); if (bfError == NULL) return NULL; @@ -2885,6 +2890,8 @@ BfError* CeContext::Fail(const StringImpl& error) BfError* CeContext::Fail(const CeFrame& curFrame, const StringImpl& str) { + if (mCurEmitContext != NULL) + mCurEmitContext->mFailed = true; auto bfError = mCurModule->Fail(StrFormat("Unable to comptime %s", mCurModule->MethodToString(mCurMethodInstance).c_str()), mCurTargetSrc, (mCurEvalFlags & CeEvalFlags_PersistantError) != 0, ((mCurEvalFlags & CeEvalFlags_DeferIfNotOnlyError) != 0) && !mCurModule->mHadBuildError); @@ -2951,19 +2958,25 @@ BfError* CeContext::Fail(const CeFrame& curFrame, const StringImpl& str) err += mCeMachine->mCeModule->MethodToString(ceFunction->mCeInnerFunctionInfo->mOwner->mMethodInstance, BfMethodNameFlag_OmitParams); } } - + if ((emitEntry != NULL) && (emitEntry->mFile != -1)) + { err += StrFormat(" at line% d:%d in %s", emitEntry->mLine + 1, emitEntry->mColumn + 1, ceFunction->mFiles[emitEntry->mFile].c_str()); - auto moreInfo = passInstance->MoreInfo(err, mCeMachine->mCeModule->mCompiler->GetAutoComplete() != NULL); - if ((moreInfo != NULL) && (emitEntry != NULL)) + auto moreInfo = passInstance->MoreInfo(err, mCeMachine->mCeModule->mCompiler->GetAutoComplete() != NULL); + if ((moreInfo != NULL)) + { + BfErrorLocation* location = new BfErrorLocation(); + location->mFile = ceFunction->mFiles[emitEntry->mFile]; + location->mLine = emitEntry->mLine; + location->mColumn = emitEntry->mColumn; + moreInfo->mLocation = location; + } + } + else { - BfErrorLocation* location = new BfErrorLocation(); - location->mFile = ceFunction->mFiles[emitEntry->mFile]; - location->mLine = emitEntry->mLine; - location->mColumn = emitEntry->mColumn; - moreInfo->mLocation = location; - } + auto moreInfo = passInstance->MoreInfo(err, mCeMachine->mCeModule->mCompiler->GetAutoComplete() != NULL); + } } return bfError; @@ -3237,7 +3250,7 @@ void CeContext::PrepareConstStructEntry(CeConstStructData& constEntry) { if (constEntry.mHash.IsZero()) { - constEntry.mHash = Hash128(&constEntry.mData[0], constEntry.mData.mSize); + constEntry.mHash = Hash128(constEntry.mData.mVals, constEntry.mData.mSize); if (!constEntry.mFixups.IsEmpty()) constEntry.mHash = Hash128(&constEntry.mFixups[0], constEntry.mFixups.mSize * sizeof(CeConstStructFixup), constEntry.mHash); } @@ -3293,6 +3306,9 @@ bool CeContext::GetStringFromStringView(addr_ce addr, StringImpl& str) bool CeContext::GetCustomAttribute(BfCustomAttributes* customAttributes, int attributeTypeId, addr_ce resultAddr) { + if (customAttributes == NULL) + return false; + BfType* attributeType = GetBfType(attributeTypeId); if (attributeType == NULL) return false; @@ -3357,7 +3373,17 @@ bool CeContext::WriteConstant(BfModule* module, addr_ce addr, BfConstant* consta auto aggConstant = (BfConstantAgg*)constant; if (type->IsSizedArray()) { - return false; + auto sizedArrayType = (BfSizedArrayType*)type; + for (int i = 0; i < sizedArrayType->mSize; i++) + { + auto fieldConstant = module->mBfIRBuilder->GetConstant(aggConstant->mValues[i]); + if (fieldConstant == NULL) + return false; + if (!WriteConstant(module, addr + i * sizedArrayType->mElementType->mSize, fieldConstant, sizedArrayType->mElementType)) + return false; + } + + return true; } else if (type->IsArray()) { @@ -3472,6 +3498,13 @@ bool CeContext::WriteConstant(BfModule* module, addr_ce addr, BfConstant* consta auto constTarget = module->mBfIRBuilder->GetConstantById(constBitCast->mTarget); return WriteConstant(module, addr, constTarget, type); } + + if (constant->mConstType == BfConstType_BitCastNull) + { + BF_ASSERT(type->IsPointer() || type->IsObjectOrInterface()); + memset(mMemory.mVals + addr, 0, type->mSize); + return true; + } if (constant->mConstType == BfConstType_GEP32_2) { @@ -3606,6 +3639,9 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType return BfIRValue(); } + + if (bfType->IsTypedPrimitive()) + return CreateConstant(module, ptr, bfType->GetUnderlyingType(), outType); if (bfType->IsTypeInstance()) { @@ -3619,38 +3655,47 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType // CE_CREATECONST_CHECKPTR(instData, typeInst->mInstSize); // } - if (typeInst->IsInstanceOf(mCeMachine->mCompiler->mStringTypeDef)) + if (typeInst->IsObjectOrInterface()) { - BfTypeInstance* stringTypeInst = (BfTypeInstance*)ceModule->ResolveTypeDef(mCeMachine->mCompiler->mStringTypeDef, BfPopulateType_Data); - module->PopulateType(stringTypeInst); - - auto lenByteCount = stringTypeInst->mFieldInstances[0].mResolvedType->mSize; - auto lenOffset = stringTypeInst->mFieldInstances[0].mDataOffset; - auto allocSizeOffset = stringTypeInst->mFieldInstances[1].mDataOffset; - auto ptrOffset = stringTypeInst->mFieldInstances[2].mDataOffset; - - int32 lenVal = *(int32*)(instData + lenOffset); - - char* charPtr = NULL; - - if (lenByteCount == 4) + addr_ce addr = *(addr_ce*)(ptr); + if (addr == 0) { - int32 allocSizeVal = *(int32*)(instData + allocSizeOffset); - if ((allocSizeVal & 0x40000000) != 0) - { - int32 ptrVal = *(int32*)(instData + ptrOffset); - charPtr = (char*)(ptrVal + memStart); - } - else - { - charPtr = (char*)(instData + ptrOffset); - } + return irBuilder->CreateConstNull(irBuilder->MapType(typeInst)); } + instData = memStart + addr; - CE_CREATECONST_CHECKPTR(charPtr, lenVal); - String str(charPtr, lenVal); - return module->GetStringObjectValue(str); - + if (typeInst->IsInstanceOf(mCeMachine->mCompiler->mStringTypeDef)) + { + BfTypeInstance* stringTypeInst = (BfTypeInstance*)ceModule->ResolveTypeDef(mCeMachine->mCompiler->mStringTypeDef, BfPopulateType_Data); + module->PopulateType(stringTypeInst); + + auto lenByteCount = stringTypeInst->mFieldInstances[0].mResolvedType->mSize; + auto lenOffset = stringTypeInst->mFieldInstances[0].mDataOffset; + auto allocSizeOffset = stringTypeInst->mFieldInstances[1].mDataOffset; + auto ptrOffset = stringTypeInst->mFieldInstances[2].mDataOffset; + + int32 lenVal = *(int32*)(instData + lenOffset); + + char* charPtr = NULL; + + if (lenByteCount == 4) + { + int32 allocSizeVal = *(int32*)(instData + allocSizeOffset); + if ((allocSizeVal & 0x40000000) != 0) + { + int32 ptrVal = *(int32*)(instData + ptrOffset); + charPtr = (char*)(ptrVal + memStart); + } + else + { + charPtr = (char*)(instData + ptrOffset); + } + } + + CE_CREATECONST_CHECKPTR(charPtr, lenVal); + String str(charPtr, lenVal); + return module->GetStringObjectValue(str); + } } if (typeInst->IsInstanceOf(mCeMachine->mCompiler->mStringViewTypeDef)) @@ -3740,8 +3785,8 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType if (!result) return BfIRValue(); fieldVals.Add(result); - } - + } + if (typeInst->mIsUnion) { auto unionInnerType = typeInst->GetUnionInnerType(); @@ -3818,6 +3863,8 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType BfIRValue CeContext::CreateAttribute(BfAstNode* targetSrc, BfModule* module, BfIRConstHolder* constHolder, BfCustomAttribute* customAttribute) { + SetAndRestoreValue prevIgnoreWrites(module->mBfIRBuilder->mIgnoreWrites, true); + module->mContext->mUnreifiedModule->PopulateType(customAttribute->mType); auto ceAttrAddr = CeMalloc(customAttribute->mType->mSize) - mMemory.mVals; BfIRValue ceAttrVal = module->mBfIRBuilder->CreateConstAggCE(module->mBfIRBuilder->MapType(customAttribute->mType, BfIRPopulateType_Identity), ceAttrAddr); @@ -3903,6 +3950,7 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns AutoTimer autoTimer(mCeMachine->mRevisionExecuteTime); + SetAndRestoreValue curPrevContext(mPrevContext, mCeMachine->mCurContext); SetAndRestoreValue prevContext(mCeMachine->mCurContext, this); SetAndRestoreValue prevEvalFlags(mCurEvalFlags, flags); SetAndRestoreValue prevTargetSrc(mCurTargetSrc, targetSrc); @@ -3975,7 +4023,14 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns { auto constant = module->mBfIRBuilder->GetConstant(arg); if (constant->mConstType == BfConstType_Undef) - isConst = false; + { + if (paramType->IsInstanceOf(module->mCompiler->mTypeTypeDef)) + { + args[argIdx] = module->CreateTypeDataRef(module->GetPrimitiveType(BfTypeCode_None)); + } + else + isConst = false; + } } if (!isConst) @@ -3999,13 +4054,24 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns bool added = false; CeFunction* ceFunction = mCeMachine->GetFunction(methodInstance, BfIRValue(), added); - if (ceFunction->mGenerating) + if (ceFunction->mInitializeState == CeFunction::InitializeState_Initializing_ReEntry) { - Fail("Recursive var-inference"); + String error = "Comptime method preparation recursion"; + auto curContext = this; + while (curContext != NULL) + { + if (curContext->mCurMethodInstance != NULL) + error += StrFormat("\n %s", module->MethodToString(curContext->mCurMethodInstance).c_str()); + + curContext = curContext->mPrevContext; + if ((curContext != NULL) && (curContext->mCurMethodInstance == mCurMethodInstance)) + break; + } + Fail(error); return BfTypedValue(); } - if (!ceFunction->mInitialized) + if (ceFunction->mInitializeState < CeFunction::InitializeState_Initialized) mCeMachine->PrepareFunction(ceFunction, NULL); auto stackPtr = &mMemory[0] + BF_CE_STACK_SIZE; @@ -4102,9 +4168,8 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns auto constant = module->mBfIRBuilder->GetConstant(arg); if (paramType->IsComposite()) - { - auto paramTypeInst = paramType->ToTypeInstance(); - useCompositeAddr -= paramTypeInst->mInstSize; + { + useCompositeAddr -= paramType->mSize; if (!WriteConstant(module, useCompositeAddr, constant, paramType, isParams)) { Fail(StrFormat("Failed to process argument for param '%s'", methodInstance->GetParamName(paramIdx).c_str())); @@ -4159,7 +4224,18 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns if (success) { BfTypedValue retValue; - if ((retInstAddr != 0) || (allocThisInstAddr != 0)) + if (returnType->IsObject()) + { + BfType* usedReturnType = returnType; + BfIRValue constVal = CreateConstant(module, (uint8*)&retInstAddr, returnType, &usedReturnType); + if (constVal) + returnValue = BfTypedValue(constVal, usedReturnType); + else + { + Fail("Failed to encode return argument"); + } + } + else if ((retInstAddr != 0) || (allocThisInstAddr != 0)) { auto* retPtr = memStart + retInstAddr; if (allocThisInstAddr != 0) @@ -4625,7 +4701,13 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _Fail("Invalid method instance"); return false; } - + + if (paramIdx < 0 || paramIdx >= methodInstance->mParams.mSize) + { + _Fail("paramIdx is out of range"); + return false; + } + addr_ce stringAddr = GetString(methodInstance->GetParamName(paramIdx)); _FixVariables(); *(int32*)(stackPtr + 0) = methodInstance->GetParamType(paramIdx)->mTypeId; @@ -4867,7 +4949,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* if (!checkFunction->mFailed) return true; - auto error = Fail(_GetCurFrame(), StrFormat("Method call '%s' failed", ceModule->MethodToString(checkFunction->mMethodInstance).c_str())); + auto error = Fail(_GetCurFrame(), StrFormat("Method call preparation '%s' failed", ceModule->MethodToString(checkFunction->mMethodInstance).c_str())); if ((error != NULL) && (!checkFunction->mGenError.IsEmpty())) mCeMachine->mCompiler->mPassInstance->MoreInfo("Comptime method generation error: " + checkFunction->mGenError); return false; @@ -5335,7 +5417,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* bool added = false; ctorCallFunction = mCeMachine->GetFunction(moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, added); - if (!ctorCallFunction->mInitialized) + if (ctorCallFunction->mInitializeState < CeFunction::InitializeState_Initialized) mCeMachine->PrepareFunction(ctorCallFunction, NULL); } @@ -5410,10 +5492,11 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* } callEntry.mFunction = callEntry.mFunctionInfo->mCeFunction; - if (!callEntry.mFunction->mInitialized) + if (callEntry.mFunction->mInitializeState < CeFunction::InitializeState_Initialized) { auto curFrame = _GetCurFrame(); SetAndRestoreValue prevFrame(mCurFrame, &curFrame); + BF_ASSERT(callEntry.mFunction->mInitializeState < CeFunction::InitializeState_Initialized); mCeMachine->PrepareFunction(callEntry.mFunction, NULL); } @@ -6852,7 +6935,7 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction) ceFunction->mFunctionKind = CeFunctionKind_Math_Tanh; } - ceFunction->mInitialized = true; + ceFunction->mInitializeState = CeFunction::InitializeState_Initialized; return; } } @@ -6863,13 +6946,27 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder AutoTimer autoTimer(mRevisionExecuteTime); SetAndRestoreValue prevCEFunction(mPreparingFunction, ceFunction); + BF_ASSERT(ceFunction->mInitializeState <= CeFunction::InitializeState_Initialized); + if (ceFunction->mFunctionKind == CeFunctionKind_NotSet) - CheckFunctionKind(ceFunction); + { + CheckFunctionKind(ceFunction); + if (ceFunction->mInitializeState == CeFunction::InitializeState_Initialized) + return; + } - BF_ASSERT(!ceFunction->mInitialized); - ceFunction->mInitialized = true; - ceFunction->mGenerating = true; + BF_ASSERT(ceFunction->mInitializeState <= CeFunction::InitializeState_Initialized); + if (ceFunction->mInitializeState == CeFunction::InitializeState_Initializing_ReEntry) + { + //Fail("Function generation re-entry"); + return; + } + if (ceFunction->mInitializeState == CeFunction::InitializeState_Initializing) + ceFunction->mInitializeState = CeFunction::InitializeState_Initializing_ReEntry; + else + ceFunction->mInitializeState = CeFunction::InitializeState_Initializing; + CeBuilder ceBuilder; SetAndRestoreValue prevBuilder(mCurBuilder, &ceBuilder); ceBuilder.mParentBuilder = parentBuilder; @@ -6878,7 +6975,7 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder ceBuilder.mCeFunction = ceFunction; ceBuilder.Build(); - ceFunction->mGenerating = false; + ceFunction->mInitializeState = CeFunction::InitializeState_Initialized; /*if (!ceFunction->mCode.IsEmpty()) { @@ -6914,8 +7011,8 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f CeFunction* ceFunction = NULL; if (!mFunctions.TryAdd(methodInstance, NULL, &functionInfoPtr)) { - ceFunctionInfo = *functionInfoPtr; - BF_ASSERT(ceFunctionInfo->mCeFunction != NULL); + ceFunctionInfo = *functionInfoPtr; + BF_ASSERT(ceFunctionInfo->mCeFunction != NULL); return ceFunctionInfo->mCeFunction; } @@ -6982,7 +7079,7 @@ CeFunction* CeMachine::GetPreparedFunction(BfMethodInstance* methodInstance) auto ceFunction = GetFunction(methodInstance, BfIRValue(), added); if (ceFunction == NULL) return NULL; - if (!ceFunction->mInitialized) + if (ceFunction->mInitializeState < CeFunction::InitializeState_Initialized) PrepareFunction(ceFunction, NULL); return ceFunction; } diff --git a/IDEHelper/Compiler/CeMachine.h b/IDEHelper/Compiler/CeMachine.h index 0400e3e8..071b1e4a 100644 --- a/IDEHelper/Compiler/CeMachine.h +++ b/IDEHelper/Compiler/CeMachine.h @@ -395,14 +395,22 @@ public: class CeFunction { +public: + enum InitializeState + { + InitializeState_None, + InitializeState_Initializing, + InitializeState_Initializing_ReEntry, + InitializeState_Initialized + }; + public: CeMachine* mCeMachine; CeFunctionInfo* mCeFunctionInfo; CeInnerFunctionInfo* mCeInnerFunctionInfo; BfMethodInstance* mMethodInstance; - CeFunctionKind mFunctionKind; - bool mGenerating; - bool mInitialized; + CeFunctionKind mFunctionKind; + InitializeState mInitializeState; bool mFailed; bool mIsVarReturn; Array mCode; @@ -425,9 +433,8 @@ public: mCeMachine = NULL; mCeFunctionInfo = NULL; mCeInnerFunctionInfo = NULL; - mFunctionKind = CeFunctionKind_NotSet; - mGenerating = false; - mInitialized = false; + mFunctionKind = CeFunctionKind_NotSet; + mInitializeState = InitializeState_None; mMethodInstance = NULL; mFailed = false; mIsVarReturn = false; @@ -682,11 +689,13 @@ public: BfMethodInstance* mMethodInstance; String mEmitData; String mExitEmitData; + bool mFailed; CeEmitContext() { mType = NULL; mMethodInstance = NULL; + mFailed = false; } }; @@ -694,6 +703,7 @@ class CeContext { public: CeMachine* mCeMachine; + CeContext* mPrevContext; int mReflectTypeIdOffset; int mExecuteId; CeEvalFlags mCurEvalFlags; diff --git a/IDEHelper/DbgExprEvaluator.cpp b/IDEHelper/DbgExprEvaluator.cpp index 5591e8f7..51c10fa0 100644 --- a/IDEHelper/DbgExprEvaluator.cpp +++ b/IDEHelper/DbgExprEvaluator.cpp @@ -5276,7 +5276,10 @@ void DbgExprEvaluator::LookupSplatMember(BfAstNode* targetNode, BfAstNode* looku if (!memberType->IsStruct()) Fail("Failed to lookup splat member", (lookupNode != NULL) ? lookupNode : targetNode); - BF_ASSERT((target.mVariable != NULL) || (target.mType->GetByteCount() == 0)); + if ((target.mVariable == NULL) && (target.mType->GetByteCount() != 0)) + Fail("Splat variable not found", (lookupNode != NULL) ? lookupNode : targetNode); + + //BF_ASSERT((target.mVariable != NULL) || (target.mType->GetByteCount() == 0)); mResult = target; mResult.mType = memberType; } @@ -5975,13 +5978,13 @@ void DbgExprEvaluator::PerformBinaryOperation(ASTREF(BfExpression*)& leftExpress // One pointer if ((!otherType->IsInteger()) || - ((binaryOp != BfBinaryOp_Add) && (binaryOp != BfBinaryOp_Subtract) && (!isCompare))) + ((binaryOp != BfBinaryOp_Add) && (binaryOp != BfBinaryOp_Subtract) && (binaryOp != BfBinaryOp_OverflowAdd) && (binaryOp != BfBinaryOp_OverflowSubtract) && (!isCompare))) { Fail("Can only add or subtract integer values from pointers", rightExpression); return; } - if ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract)) + if ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowAdd) || (binaryOp == BfBinaryOp_OverflowSubtract)) { auto underlyingType = otherType->GetUnderlyingType(); mResult.mType = resultType; @@ -6262,6 +6265,7 @@ void DbgExprEvaluator::PerformBinaryOperation(DbgType* resultType, DbgTypedValue switch (binaryOp) { case BfBinaryOp_Add: + case BfBinaryOp_OverflowAdd: if (resultType->mTypeCode == DbgType_Single) mResult.mSingle = convLeftValue.mSingle + convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) @@ -6270,6 +6274,7 @@ void DbgExprEvaluator::PerformBinaryOperation(DbgType* resultType, DbgTypedValue mResult.mInt64 = convLeftValue.GetInt64() + convRightValue.GetInt64(); break; case BfBinaryOp_Subtract: + case BfBinaryOp_OverflowSubtract: if (resultType->mTypeCode == DbgType_Single) mResult.mSingle = convLeftValue.mSingle - convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) @@ -6278,6 +6283,7 @@ void DbgExprEvaluator::PerformBinaryOperation(DbgType* resultType, DbgTypedValue mResult.mInt64 = convLeftValue.GetInt64() - convRightValue.GetInt64(); break; case BfBinaryOp_Multiply: + case BfBinaryOp_OverflowMultiply: if (resultType->mTypeCode == DbgType_Single) mResult.mSingle = convLeftValue.mSingle * convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) diff --git a/IDEHelper/DebugManager.cpp b/IDEHelper/DebugManager.cpp index c7ff3033..580959fb 100644 --- a/IDEHelper/DebugManager.cpp +++ b/IDEHelper/DebugManager.cpp @@ -745,7 +745,7 @@ BF_EXPORT int BF_CALLTYPE Debugger_GetAddrSize() return gDebugger->GetAddrSize(); } -BF_EXPORT bool BF_CALLTYPE Debugger_OpenFile(const char* launchPath, const char* targetPath, const char* args, const char* workingDir, void* envBlockPtr, int envBlockSize) +BF_EXPORT bool BF_CALLTYPE Debugger_OpenFile(const char* launchPath, const char* targetPath, const char* args, const char* workingDir, void* envBlockPtr, int envBlockSize, bool hotSwapEnabled) { BF_ASSERT(gDebugger == NULL); @@ -775,7 +775,7 @@ BF_EXPORT bool BF_CALLTYPE Debugger_OpenFile(const char* launchPath, const char* envBlock.Insert(0, (uint8*)envBlockPtr, envBlockSize); } - gDebugger->OpenFile(launchPath, targetPath, args, workingDir, envBlock); + gDebugger->OpenFile(launchPath, targetPath, args, workingDir, envBlock, hotSwapEnabled); return true; } diff --git a/IDEHelper/DebugTarget.cpp b/IDEHelper/DebugTarget.cpp index ddc24ea5..efe0055a 100644 --- a/IDEHelper/DebugTarget.cpp +++ b/IDEHelper/DebugTarget.cpp @@ -82,7 +82,7 @@ static bool PathEquals(const String& pathA, String& pathB) void DebugTarget::SetupTargetBinary() { - bool wantsHotHeap = mDebugger->mDbgProcessId == 0; + bool wantsHotHeap = (mDebugger->mHotSwapEnabled == true && mDebugger->mDbgProcessId == 0); #ifdef BF_DBG_32 if (wantsHotHeap) diff --git a/IDEHelper/Debugger.h b/IDEHelper/Debugger.h index a9416539..ce603352 100644 --- a/IDEHelper/Debugger.h +++ b/IDEHelper/Debugger.h @@ -257,7 +257,7 @@ public: virtual void OutputRawMessage(const StringImpl& msg) = 0; virtual int GetAddrSize() = 0; virtual bool CanOpen(const StringImpl& fileName, DebuggerResult* outResult) = 0; - virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array& envBlock) = 0; + virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array& envBlock, bool hotSwapEnabled) = 0; virtual bool Attach(int processId, BfDbgAttachFlags attachFlags) = 0; virtual void Run() = 0; virtual void HotLoad(const Array& objectFiles, int hotIdx) = 0; diff --git a/IDEHelper/Tests/BeefLinq/BeefSpace.toml b/IDEHelper/Tests/BeefLinq/BeefSpace.toml new file mode 100644 index 00000000..c8a1ef89 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/BeefSpace.toml @@ -0,0 +1,2 @@ +FileVersion = 1 +Projects = {"Beef.Linq" = {Path = "src"}, "Beef.Linq.Tests" = {Path = "src/test"}} diff --git a/IDEHelper/Tests/BeefLinq/src/BeefProj.toml b/IDEHelper/Tests/BeefLinq/src/BeefProj.toml new file mode 100644 index 00000000..858ac746 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/BeefProj.toml @@ -0,0 +1,7 @@ +FileVersion = 1 + +[Project] +Name = "Beef.Linq" +TargetType = "BeefLib" +StartupObject = "Beef.Linq.Program" +DefaultNamespace = "System.Linq" diff --git a/IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf b/IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf new file mode 100644 index 00000000..f855bcc7 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf @@ -0,0 +1,2868 @@ +using System.Collections; +using System; +using internal System.Linq; + +namespace System.Linq +{ + public static + { + public static class Enumerable + { + public struct EmptyEnumerable : IEnumerable + { + public Enumerator GetEnumerator() => .(); + + public struct Enumerator : IEnumerator + { + public Result GetNext() + { + return .Err; + } + } + } + + public static EmptyEnumerable Empty() => .(); + + public struct RangeEnumerable : IEnumerable where TSource : operator TSource + int + { + TSource mStart; + TSource mEnd; + + public this(TSource start, TSource end) + { + mStart = start; + mEnd = end; + } + + public Enumerator GetEnumerator() => .(mStart, mEnd); + + public struct Enumerator : IEnumerator + { + TSource mCurrent; + TSource mEnd; + + public this(TSource start, TSource end) + { + mCurrent = start; + mEnd = end; + } + + public Result GetNext() mut + { + if (mCurrent == mEnd) + return .Err; + + let next = mCurrent; + mCurrent = mCurrent + 1; + return .Ok(next); + } + } + } + + public static RangeEnumerable + Range(TSource count) + where TSource : operator TSource + int + { + return .(default, count); + } + + public static RangeEnumerable + Range(TSource start, TSource end) + where TSource : operator TSource + int + where TSource : operator TSource + TSource + { + return .(start, end); + } + + public struct RepeatEnumerable : IEnumerable + { + TSource mValue; + int mCount; + + public this(TSource value, int count) + { + mValue = value; + mCount = count; + } + + public Enumerator GetEnumerator() => .(mValue, mCount); + + public struct Enumerator : IEnumerator + { + TSource mValue; + int mCount; + + public this(TSource value, int count) + { + mValue = value; + mCount = count; + } + + public Result GetNext() mut + { + if (--mCount >= 0) + return .Ok(mValue); + + return .Err; + } + } + } + + public static RepeatEnumerable + Repeat(TSource value, int count) + { + return .(value, count); + } + } + + + #region Matching + public static bool All(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return InternalAll(items, predicate); + } + + public static bool All(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return InternalAll(items.GetEnumerator(), predicate); + } + + static bool InternalAll(TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): if (!predicate(val)) return false; + case .Err: return false; + } + + while (enumerator.GetNext() case .Ok(let val)) + if (!predicate(val)) + return false; + + return true; + } + } + + public static bool Any(this TCollection items) + where TCollection : concrete, IEnumerable + { + for (var it in items) + return true; + + return false; + } + + public static bool Any(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + for (var it in items) + if (predicate(it)) + return true; + + return false; + } + + public static bool Any(this TEnum items) + where TEnum : concrete, IEnumerator + { + for (var it in items) + return true; + + return false; + } + + public static bool Any(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + for (var it in items) + if (predicate(it)) + return true; + + return false; + } + + public static bool Contains(this TCollection items, TSource source) + where TCollection : concrete, IEnumerable + where bool : operator TSource == TSource + { + for (var it in items) + if (it == source) + return true; + + return false; + } + + public static bool Contains(this TEnum items, TSource source) + where TEnum : concrete, IEnumerator + where bool : operator TSource == TSource + { + for (var it in items) + if (it == source) + return true; + + return false; + } + + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerable + where TRight : concrete, IEnumerable + where bool : operator TSource == TSource + { + return InternalSequenceEquals< + decltype(default(TLeft).GetEnumerator()), + decltype(default(TRight).GetEnumerator()), + TSource>(left.GetEnumerator(), right.GetEnumerator()); + } + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerable + where TRight : concrete, IEnumerator + where bool : operator TSource == TSource + { + return InternalSequenceEquals< + decltype(default(TLeft).GetEnumerator()), + TRight, + TSource>(left.GetEnumerator(), right); + } + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerator + where TRight : concrete, IEnumerable + where bool : operator TSource == TSource + { + return InternalSequenceEquals< + TLeft, + decltype(default(TRight).GetEnumerator()), + TSource>(left, right.GetEnumerator()); + } + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerator + where TRight : concrete, IEnumerator + where bool : operator TSource == TSource + { + return InternalSequenceEquals(left, right); + } + + static bool InternalSequenceEquals(TLeft left, TRight right) + where TLeft : concrete, IEnumerator + where TRight : concrete, IEnumerator + where bool : operator TSource == TSource + { + using (var iterator0 = Iterator.Wrap(left)) + { + var e0 = iterator0.mEnum; + using (var iterator1 = Iterator.Wrap(right)) + { + var e1 = iterator1.mEnum; + while (true) + { + switch (e0.GetNext()) + { + case .Ok(let i0): + switch (e1.GetNext()) + { + case .Ok(let i1): + if (i0 != i1) + return false; + case .Err: + return false; + } + case .Err: + return e1.GetNext() case .Err; + } + } + } + } + } + + #endregion + + #region Aggregates + + public static TSource Average(this TCollection items) + where TCollection : concrete, IEnumerable + where TSource : operator TSource / int + where TSource : operator TSource + TSource + { + return InternalAverage(items.GetEnumerator()); + } + + public static TSource Average(this TEnum items) + where TEnum : concrete, IEnumerator + where TSource : operator TSource / int + where TSource : operator TSource + TSource + { + return InternalAverage(items); + } + + static TSource InternalAverage(TEnum items) + where TEnum : concrete, IEnumerator + where TSource : operator TSource / int + where TSource : operator TSource + TSource + { + var count = 0; + TSource sum = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + switch (enumerator.GetNext()) + { + case .Ok(let val): + sum = val; + count++; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + { + sum += val; + count++; + } + + return sum / count; + } + } + + public static TSource Max(this TCollection items) + where TCollection : concrete, IEnumerable + where bool : operator TSource < TSource + { + return InternalMax(items.GetEnumerator()); + } + + public static TSource Max(this TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + return InternalMax(items); + } + + static TSource InternalMax(TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + TSource max = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): max = val; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + { + let next = val; + if (max < next) + max = next; + } + } + return max; + } + + public static TSource Min(this TCollection items) + where TCollection : concrete, IEnumerable + where bool : operator TSource < TSource + { + return InternalMin(items.GetEnumerator()); + } + + public static TSource Min(this TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + return InternalMin(items); + } + + static TSource InternalMin(TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + TSource min = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): min = val; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + { + let next = val; + if (next < min) + min = next; + } + } + return min; + } + + public static TSource Sum(this TCollection items) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + where TSource : operator TSource + TSource + { + return InternalSum(items.GetEnumerator()); + } + + public static TSource Sum(this TEnum items) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + where TSource : operator TSource + TSource + { + return InternalSum(items); + } + + static TSource InternalSum(TEnum items) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + where TSource : operator TSource + TSource + { + TSource sum = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): sum = val; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + sum += val; + } + return sum; + } + + public static int Count(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (typeof(TCollection) == typeof(List)) + return (items as List).Count; + if (typeof(TCollection) == typeof(TSource[])) + return (items as TSource[]).Count; + + return InternalCount(items.GetEnumerator()); + } + + + public static int Count(this TEnum items) + where TEnum : concrete, IEnumerator + { + return InternalCount(items); + } + + public static int InternalCount(TEnum items) + where TEnum : concrete, IEnumerator + { + var count = 0; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + while (enumerator.GetNext() case .Ok) + count++; + } + return count; + } + + #endregion + + #region Find in enumerable + + internal static bool InternalElementAt(TEnum items, int index, out TSource val) + where TEnum : concrete, IEnumerator + { + var index; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + while (--index > 0) + { + if (enumerator.GetNext() case .Err) + break; + } + + if (index == 0 && enumerator.GetNext() case .Ok(out val)) + return true; + } + val = default; + return false; + } + + public static TSource ElementAt(this TCollection items, int index) + where TCollection : concrete, IEnumerable + { + if (InternalElementAt(items.GetEnumerator(), index, let val)) + return val; + + Runtime.FatalError("Not enough elements in the sequence."); + } + + public static TSource ElementAt(this TEnum items, int index) + where TEnum : concrete, IEnumerator + { + if (InternalElementAt(items, index, let val)) + return val; + + Runtime.FatalError("Not enough elements in the sequence."); + } + + public static TSource ElementAtOrDefault(this TCollection items, int index) + where TCollection : concrete, IEnumerable + { + if (InternalElementAt(items.GetEnumerator(), index, let val)) + return val; + + return default; + } + + public static TSource ElementAtOrDefault(this TEnum items, int index) + where TEnum : concrete, IEnumerator + { + if (InternalElementAt(items, index, let val)) + return val; + + return default; + } + + public static bool InternalFirst(TEnum items, out TSource val) + where TEnum : concrete, IEnumerator + { + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + if (enumerator.GetNext() case .Ok(out val)) + return true; + } + + return false; + } + + public static TSource First(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalFirst(items.GetEnumerator(), let val)) + return val; + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource First(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalFirst(items, let val)) + return val; + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource FirstOrDefault(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalFirst(items.GetEnumerator(), let val)) + return val; + + return default; + } + + public static TSource FirstOrDefault(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalFirst(items, let val)) + return val; + + return default; + } + + internal static bool InternalLast(TEnum items, out TSource val) + where TEnum : concrete, IEnumerator + { + var found = false; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + if (enumerator.GetNext() case .Ok(out val)) + found = true; + + while (enumerator.GetNext() case .Ok(let temp)) + val = temp; + } + + return found; + } + + public static TSource Last(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalLast(items.GetEnumerator(), let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource Last(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalLast(items, let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource LastOrDefault(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalLast(items.GetEnumerator(), let val)) + return val; + + return default; + } + + public static TSource LastOrDefault(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalLast(items, let val)) + return val; + + return default; + } + + internal static bool InternalSingle(TEnum items, out TSource val) + where TEnum : concrete, IEnumerator + { + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + if (enumerator.GetNext() case .Ok(out val)) + { + if (enumerator.GetNext() case .Err) + return true; + + Runtime.FatalError("Sequence matched more than one element."); + } + } + + return false; + } + + public static TSource Single(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalSingle(items.GetEnumerator(), let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource Single(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalSingle(items, let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource SingleOrDefault(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalSingle(items.GetEnumerator(), let val)) + return val; + + return default; + } + + public static TSource SingleOrDefault(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalSingle(items, let val)) + return val; + + return default; + } + + #endregion + +#region Enumerable Chains + struct Iterator + { + public static Iterator Wrap(TEnum items) + where TEnum : concrete, IEnumerator + { + return .(items); + } + + public static Iterator Wrap(TCollection items) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator()); + } + } + + struct Iterator : IDisposable + where TEnum : concrete, IEnumerator + { + internal TEnum mEnum; + + public this(TEnum items) + { + mEnum = items; + } + + [SkipCall] + public void Dispose() mut { } + + public static implicit operator Iterator(TEnum enumerator) => .(enumerator); + } + + extension Iterator : IDisposable where TEnum : IDisposable + { + public void Dispose() mut => mEnum.Dispose(); + } + + struct SelectEnumerable : Iterator, IEnumerable + where TSelect : delegate TResult(TSource) + where TEnum : concrete, IEnumerator + { + TSelect mDlg; + + public this(TEnum e, TSelect dlg) : base(e) + { + mDlg = dlg; + } + + Result GetNext() mut + { + if (mEnum.GetNext() case .Ok(let val)) + return mDlg(val); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static SelectEnumerable + Select(this TCollection items, TSelect select) + where TCollection : concrete, IEnumerable + where TSelect : delegate TResult(TSource) + { + return .(items.GetEnumerator(), select); + } + + public static SelectEnumerable + Select(this TEnum items, TSelect select) + where TEnum : concrete, IEnumerator + where TSelect : delegate TResult(TSource) + { + return .(items, select); + } + + struct WhereEnumerable : IEnumerable, IDisposable + where TPredicate : delegate bool(TSource) + where TEnum : concrete, IEnumerator + { + TPredicate mPredicate; + Iterator mIterator; + + public this(TEnum enumerator, TPredicate predicate) + { + mIterator = enumerator; + mPredicate = predicate; + } + + Result GetNext() mut + { + while (mIterator.mEnum.GetNext() case .Ok(let val)) + if (mPredicate(val)) + return .Ok(val); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + public this(SelfOuter self) + { + mSelf = self; + } + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut + { + mSelf.Dispose(); + } + } + + public void Dispose() mut + { + mIterator.Dispose(); + } + } + + public static WhereEnumerable + Where(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return .(items.GetEnumerator(), predicate); + } + + public static WhereEnumerable + Where(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return .(items, predicate); + } + + struct TakeEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + { + int mCount; + + public this(TEnum enumerator, int count) : base(enumerator) + { + mCount = count; + } + + public Result GetNext() mut + { + while (mCount-- > 0 && mEnum.GetNext() case .Ok(let val)) + return val; + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static TakeEnumerable + Take(this TCollection items, int count) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), count); + } + + public static TakeEnumerable + Take(this TEnum items, int count) + where TEnum : concrete, IEnumerator + { + return .(items, count); + } + + struct TakeWhileEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + TPredicate mPredicate; + + public this(TEnum enumerator, TPredicate predicate) : base(enumerator) + { + mPredicate = predicate; + } + + Result GetNext() mut + { + if (mEnum.GetNext() case .Ok(let val)) + if (mPredicate(val)) + return .Ok(val); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static TakeWhileEnumerable + TakeWhile(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return .(items.GetEnumerator(), predicate); + } + + public static TakeWhileEnumerable + TakeWhile(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return .(items, predicate); + } + + struct SkipEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + { + int mCount; + + public this(TEnum enumerator, int count) : base(enumerator) + { + mCount = count; + } + + public Result GetNext() mut + { + while (mCount-- > 0 && mEnum.GetNext() case .Ok(?)) { } + + while (mEnum.GetNext() case .Ok(let val)) + return val; + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static SkipEnumerable + Skip(this TCollection items, int count) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), count); + } + + public static SkipEnumerable + Skip(this TEnum items, int count) + where TEnum : concrete, IEnumerator + { + return .(items, count); + } + + struct SkipWhileEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + TPredicate mPredicate; + int mState = 0; + + public this(TEnum enumerator, TPredicate predicate) : base(enumerator) + { + mPredicate = predicate; + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + while (mEnum.GetNext() case .Ok(let val)) + { + if (!mPredicate(val)) + { + mState = 1; + return .Ok(val); + } + } + case 1: + return mEnum.GetNext(); + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static SkipWhileEnumerable + SkipWhile(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return .(items.GetEnumerator(), predicate); + } + + public static SkipWhileEnumerable + SkipWhile(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return .(items, predicate); + } + + struct DefaultIfEmptyEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + { + TSource mDefaultValue; + int mState = 0; + + public this(TEnum enumerator, TSource defaultValue) : base(enumerator) + { + mDefaultValue = defaultValue; + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + if (mEnum.GetNext() case .Ok(let val)) + { + mState = 1; + return .Ok(val); + } + + mState = 2; + return .Ok(mDefaultValue); + case 1: + return mEnum.GetNext(); + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TCollection items) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), default); + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TEnum items) + where TEnum : concrete, IEnumerator + { + return .(items, default); + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TCollection items, TSource defaultValue = default) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), defaultValue); + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TEnum items, TSource defaultValue = default) + where TEnum : concrete, IEnumerator + { + return .(items, defaultValue); + } + + struct DistinctEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + HashSet.Enumerator mEnum; + Iterator mIterator; + int mState = 0; + + public this(TEnum enumerator) + { + mIterator = .(enumerator); + mDistinctValues = new .(); + mEnum = default; + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + var enumerator = mIterator.mEnum; + while (enumerator.GetNext() case .Ok(let val)) + mDistinctValues.Add(val); + + mIterator.Dispose(); + mIterator = default; + mEnum = mDistinctValues.GetEnumerator(); + mState = 1; + fallthrough; + case 1: + return mEnum.GetNext(); + } + + return .Err; + } + + public void Dispose() mut + { + mEnum.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + + public void Dispose() mut => mEnum.Dispose(); + } + } + + public static DistinctEnumerable + Distinct(this TCollection items) + where TCollection : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator()); + } + + public static DistinctEnumerable + Distinct(this TEnum items) + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items); + } + + struct ReverseEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + { + List mCopyValues; + List.Enumerator mEnum; + Iterator mIterator; + int mIndex = -1; + + public this(TEnum enumerator) + { + mIterator = .(enumerator); + mCopyValues = new .(); + mEnum = default; + } + + public Result GetNext() mut + { + switch (mIndex) { + case -1: + var enumerator = mIterator.mEnum; + while (enumerator.GetNext() case .Ok(let val)) + mCopyValues.Add(val); + + mIterator.Dispose(); + mIterator = default; + mEnum = mCopyValues.GetEnumerator(); + mIndex = mCopyValues.Count; + fallthrough; + default: + if (--mIndex >= 0) + return .Ok(mCopyValues[mIndex]); + + return .Err; + } + } + + public void Dispose() mut + { + mEnum.Dispose(); + DeleteAndNullify!(mCopyValues); + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + public this(SelfOuter self) + { + mSelf = self; + } + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut + { + mSelf.Dispose(); + } + } + } + + public static ReverseEnumerable + Reverse(this TCollection items) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator()); + } + + struct MapEnumerable : Iterator, IEnumerator, IEnumerable + where bool : operator TSource < TSource + where TSource : operator TSource - TSource + where TResult : operator TResult + TResult + where TResult : operator TResult - TResult + where float : operator float / TSource + where float : operator TSource * float + where float : operator float / TResult + where TResult : operator explicit float + where TEnum : concrete, IEnumerator + { + int mState = 0; + float mScale = 0f, mMapScale; + TSource mMin = default; + TResult mMapMin; + + public this(TEnum enumerator, TResult mapMin, TResult mapMax) : base(enumerator) + { + mMapMin = mapMin; + mMapScale = 1f / (mapMax - mapMin); + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + var copyEnum = mEnum; + switch (copyEnum.GetNext()) { + case .Ok(let first): + var min = first; + var max = first; + + while (copyEnum.GetNext() case .Ok(let next)) + { + if (next < min) min = next; + if (max < next) max = next; + } + + mMin = min; + mScale = 1f / (max - min); + if (mScale == default) + { + mState = 2; + return .Ok(default); + } + + mState = 1; + case .Err: return .Err; + } + fallthrough; + case 1: + if (mEnum.GetNext() case .Ok(let val)) + return (TResult)(((val - mMin) * mScale) / mMapScale) + mMapMin; + case 2: + if (mEnum.GetNext() case .Ok(let val)) + return .Ok(default); + } + + return .Err; + } + + /*typealias SelfOuter = MapEnumerable; + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator: IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + }*/ + + public Self GetEnumerator() => this; + } + + public static MapEnumerable + Map(this TCollection items, TResult min, TResult max) + where TCollection : concrete, IEnumerable + where bool : operator TSource < TSource + where TSource : operator TSource - TSource + where TResult : operator TResult + TResult + where TResult : operator TResult - TResult + where float : operator float / TSource + where float : operator TSource * float + where float : operator float / TResult + where TResult : operator explicit float + { + return .(items.GetEnumerator(), min, max); + } + + public static MapEnumerable + Map(this TEnum items, TResult min, TResult max) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + where TSource : operator TSource - TSource + where TResult : operator TResult + TResult + where TResult : operator TResult - TResult + where float : operator float / TSource + where float : operator TSource * float + where float : operator float / TResult + where TResult : operator explicit float + { + return .(items, min, max); + } +#endregion + +#region ToXYZ methods + public static void ToDictionary(this TCollection items, TKeyDlg keyDlg, TValueDlg valueDlg, Dictionary output) + where TCollection : concrete, IEnumerable + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + where TValueDlg : delegate TValue(TSource) + { + for (var it in items) + output.Add(keyDlg(it), valueDlg(it)); + } + + public static void ToDictionary(this TEnum items, TKeyDlg keyDlg, TValueDlg valueDlg, Dictionary output) + where TEnum : concrete, IEnumerator + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + where TValueDlg : delegate TValue(TSource) + { + for (var it in items) + output.Add(keyDlg(it), valueDlg(it)); + } + + public static void ToDictionary(this TCollection items, TKeyDlg keyDlg, Dictionary output) + where TCollection : concrete, IEnumerable + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + { + for (var it in items) + output.Add(keyDlg(it), it); + } + + public static void ToDictionary(this TEnum items, TKeyDlg keyDlg, Dictionary output) + where TEnum : concrete, IEnumerator + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + { + for (var it in items) + output.Add(keyDlg(it), it); + } + + public static void ToHashSet(this TCollection items, HashSet output) + where TCollection : concrete, IEnumerable + where TSource : IHashable + { + for (var it in items) + output.Add(it); + } + + public static void ToHashSet(this TEnum items, HashSet output) + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + for (var it in items) + output.Add(it); + } + + public static void ToList(this TCollection items, List output) + where TCollection : concrete, IEnumerable + { + for (var it in items) + output.Add(it); + } + + public static void ToList(this TEnum items, List output) + where TEnum : concrete, IEnumerator + { + for (var it in items) + output.Add(it); + } +#endregion + +#region Aggregates + public static TSource + Aggregate(this TCollection items, TAccumulate accumulate) + where TCollection : concrete, IEnumerable + where TAccumulate : delegate TSource(TSource, TSource) + { + if (InternalAggregate(items.GetEnumerator(), accumulate, let result)) + return result; + + Runtime.FatalError("No elements in the sequence."); + } + + public static TSource + Aggregate(this TEnum items, TAccumulate accumulate) + where TEnum : concrete, IEnumerator + where TAccumulate : delegate TSource(TSource, TSource) + { + if (InternalAggregate(items, accumulate, let result)) + return result; + + Runtime.FatalError("No elements in the sequence."); + } + + public static TAccumulate + Aggregate(this TCollection items, TAccumulate seed, TAccDlg accumulate) + where TCollection : concrete, IEnumerable + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + { + if (InternalAggregate(items.GetEnumerator(), seed, accumulate, let result)) + return result; + + return seed; + } + + public static TAccumulate + Aggregate(this TEnum items, TAccumulate seed, TAccDlg accumulate) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + { + if (InternalAggregate(items, seed, accumulate, let result)) + return result; + + return seed; + } + + public static TResult + Aggregate(this TCollection items, TAccDlg accumulate, TResDlg resultSelector) + where TCollection : concrete, IEnumerable + where TAccDlg : delegate TSource(TSource, TSource) + where TResDlg : delegate TResult(TSource) + { + if (InternalAggregate(items.GetEnumerator(), accumulate, let result)) + return resultSelector(result); + + return resultSelector(default); + } + + public static TResult + Aggregate(this TEnum items, TAccDlg accumulate, TResDlg resultSelector) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TSource(TSource, TSource) + where TResDlg : delegate TResult(TSource) + { + if (InternalAggregate(items, accumulate, let result)) + return resultSelector(result); + + return resultSelector(default); + } + + public static TResult + Aggregate(this TCollection items, TAccumulate seed, TAccDlg accumulate, TResDlg resultSelector) + where TCollection : concrete, IEnumerable + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + where TResDlg : delegate TResult(TAccumulate) + { + if (InternalAggregate(items.GetEnumerator(), seed, accumulate, let result)) + return resultSelector(result); + + return resultSelector(seed); + } + + public static TResult + Aggregate(this TEnum items, TAccumulate seed, TAccDlg accumulate, TResDlg resultSelector) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + where TResDlg : delegate TResult(TAccumulate) + { + if (InternalAggregate(items, seed, accumulate, let result)) + return resultSelector(result); + + return resultSelector(seed); + } + + static bool InternalAggregate(TEnum items, TAccDlg accumulate, out TSource aggregate) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TSource(TSource, TSource) + { + aggregate = default; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + //I guess we need at least 2 elements to do an accumulation without a seed? + if (!(enumerator.GetNext() case .Ok(out aggregate))) + return false; + + while (enumerator.GetNext() case .Ok(let val)) + aggregate = accumulate(aggregate, val); + } + return true; + } + + + static bool InternalAggregate(TEnum items, TAccumulate seed, TAccDlg func, out TAccumulate result) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + { + TAccumulate sum = seed; + var accumulated = false; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + if (enumerator.GetNext() case .Ok(let val)) + { + sum = func(sum, val); + accumulated = true; + } + + if (accumulated) + while (enumerator.GetNext() case .Ok(let val)) + sum = func(sum, val); + } + + result = sum; + return accumulated; + } + #endregion + +#region GroupBy + + struct DynamicArray : IDisposable + { + TValue[] mPtr = default; + Span mSpan = default; + int mLength = 0; + int mSize = 4; + int mIndex = 0; + + public int Length => mLength; + + public this() + { + this.mPtr = new TValue[mSize]; + this.mLength = 0; + } + + public ref TValue this[int index] => ref mPtr[index]; + + public void Dispose() mut + { + DeleteAndNullify!(mPtr); + mPtr = null; + } + public void Add(TValue value) mut + { + if (mLength + 1 > mSize) + { + var newSize = mSize * 3 / 2; + var dst = new TValue[newSize]; + Array.Copy(mPtr, dst, mLength); + Swap!(mPtr, dst); + delete dst; + mSize = newSize; + } + mPtr[mLength++] = value; + } + + public Span.Enumerator GetEnumerator() mut + { + mSpan = .(mPtr, 0, mLength); + return mSpan.GetEnumerator(); + } + + public static implicit operator Span(Self it) => .(it.mPtr, 0, it.mLength); + } + + public extension DynamicArray : IDisposable + where TValue : IDisposable + { + public void Dispose() mut + { + for (var it in mPtr) + it.Dispose(); + + base.Dispose(); + } + } + + public struct Grouping : IEnumerable, IDisposable, IResettable + { + List mValues; + int mIndex = 0; + public readonly TKey Key; + + public this(TKey key) + { + Key = key; + mValues = new .(); + } + + public void Reset() mut + { + mIndex = 0; + } + + public List.Enumerator GetEnumerator() + { + return mValues.GetEnumerator(); + } + + public void Add(TValue value) mut + { + mValues.Add(value); + } + + public void Dispose() mut + { + DeleteAndNullify!(mValues); + } + } + + public class GroupByResult : IEnumerable> + where bool : operator TKey == TKey//where TKey : IHashable + { + DynamicArray> mResults = .() ~ mResults.Dispose(); + + public int Count => mResults.Length; + + public void Add(Grouping group) + { + mResults.Add(group); + } + + public ref Grouping this[int index] => ref mResults[index]; + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IRefEnumerator*>, IEnumerator>, IResettable + { + SelfOuter mSelf; + Span> mSpan; + int mIndex = 0; + + public this(SelfOuter self) + { + mSelf = self; + mSpan = self.mResults; + } + + public Result> GetNext() mut + { + if (mIndex < mSpan.Length) + return .Ok(mSpan[mIndex++]); + + return .Err; + } + + public Result*> GetNextRef() mut + { + if (mIndex < mSpan.Length) + return .Ok(&mSpan[mIndex++]); + + return .Err; + } + + public void Reset() mut + { + mIndex = 0; + } + } + } + + public struct GroupByEnumerable : IEnumerable>, IDisposable + where TEnum : concrete, IEnumerator + where bool : operator TKey == TKey//where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + where TValueDlg : delegate TValue(TSource) + { + GroupByResult mResults; + TKeyDlg mKeyDlg; + TValueDlg mValueDlg; + Iterator mIterator; + int mIndex = -1; + bool mDeleteResult; + + public this(GroupByResult results, TEnum enumerator, TKeyDlg keyDlg, TValueDlg valueDlg, bool deleteResult) + { + mResults = results; + mIterator = .(enumerator); + mKeyDlg = keyDlg; + mValueDlg = valueDlg; + mDeleteResult = deleteResult; + } + + Result> GetNext() mut + { + if (mIndex == -1) + { + while (mIterator.mEnum.GetNext() case .Ok(let val)) + { + let k = mKeyDlg(val); + let v = mValueDlg(val); + var added = false; + for (var it in ref mResults) + { + if (it.Key == k) + { + it.Add(v); + added = true; + } + } + + if (!added) + { + var group = mResults.Add(.. .(k)); + group.Add(v); + } + } + mIndex = 0; + } + + if (mIndex < mResults.Count) + return mResults[mIndex++]; + + return .Err; + } + + public void Dispose() mut + { + mIterator.Dispose(); + if (mDeleteResult) + DeleteAndNullify!(mResults); + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator>, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result> GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + } + + extension GroupByEnumerable + where TValueDlg : Object + { + public void Dispose() mut + { + base.Dispose(); + DeleteAndNullify!(mValueDlg); + } + } + + public static GroupByEnumerable + GroupBy(this TCollection items, TKeyDlg key) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(new .(), items.GetEnumerator(), key, new (val) => val, true); + } + + public static GroupByEnumerable + GroupBy(this TEnum items, TKeyDlg key) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(new .(), items, key, new (val) => val, true); + } + + public static GroupByEnumerable + GroupBy(this TCollection items, TKeyDlg key, GroupByResult results) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(results, items.GetEnumerator(), key, new (val) => val, false); + } + + public static GroupByEnumerable + GroupBy(this TEnum items, TKeyDlg key, GroupByResult results) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(results, items, key, new (val) => val, false); + } + + /*public static GroupByEnumerable GroupBy(this TCollection items, TKeyDlg + key, GroupByResult results) where TCollection : concrete, IEnumerable where TKeyDlg : + delegate TKey(TSource) where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(results, items.GetEnumerator(), key, scope (val) => val); + }*/ +#endregion + struct UnionEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + Iterator mSource; + Iterator mOther; + int mState = 0; + + public this(TEnum sourceEnumerator, TEnum2 otherEnumerator) + { + mSource = sourceEnumerator; + mOther = otherEnumerator; + + mDistinctValues = new .(); + } + + Result GetNext() mut + { + switch (mState) { + case 0: + var e = mSource.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Add(val)) + return .Ok(val); + + mState++; + fallthrough; + case 1: + var e = mOther.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Add(val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mSource.Dispose(); + mOther.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + } + + public static UnionEnumerable + Union(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static UnionEnumerable + Union(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items, other.GetEnumerator()); + } + + public static UnionEnumerable + Union(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items.GetEnumerator(), other); + } + + public static UnionEnumerable + Union(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + return .(items, other); + } + + struct ExceptEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + Iterator mSource; + Iterator mOther; + int mState = 0; + + public this(TEnum sourceEnumerator, TEnum2 otherEnumerator) + { + mSource = sourceEnumerator; + mOther = otherEnumerator; + + mDistinctValues = new .(); + } + + Result GetNext() mut + { + switch (mState) { + case 0: + var e = mOther.mEnum; + while (e.GetNext() case .Ok(let val)) + mDistinctValues.Add(val); + + mState++; + fallthrough; + case 1: + var e = mSource.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Add(val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mSource.Dispose(); + mOther.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + } + + public static ExceptEnumerable + Except(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static ExceptEnumerable + Except(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items, other.GetEnumerator()); + } + + public static ExceptEnumerable + Except(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items.GetEnumerator(), other); + } + + public static ExceptEnumerable + Except(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + return .(items, other); + } + + struct IntersectEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + Iterator mSource; + Iterator mIntersect; + int mState = 0; + + public this(TEnum sourceEnumerator, TEnum2 intersectEnumerator) + { + mSource = .(sourceEnumerator); + mIntersect = .(intersectEnumerator); + mDistinctValues = new .(); + } + + Result GetNext() mut + { + switch (mState) + { + case 0: + var e = mSource.mEnum; + while (e.GetNext() case .Ok(let val)) + mDistinctValues.Add(val); + + mState++; + fallthrough; + case 1: + var e = mIntersect.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Remove(val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mSource.Dispose(); + mIntersect.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + } + + public static IntersectEnumerable + Intersect(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static IntersectEnumerable + Intersect(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items, other.GetEnumerator()); + } + + public static IntersectEnumerable + Intersect(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items.GetEnumerator(), other); + } + + public static IntersectEnumerable + Intersect(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + return .(items, other); + } + + struct ZipEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TResult(TSource first, TSource second) + { + Iterator mSource; + Iterator mOther; + TSelect mSelect; + + public this(TEnum sourceEnumerator, TEnum2 otherEnumerator, TSelect select) + { + mSource = sourceEnumerator; + mOther = otherEnumerator; + mSelect = select; + } + + Result GetNext() mut + { + if (mSource.mEnum.GetNext() case .Ok(let first)) + if (mOther.mEnum.GetNext() case .Ok(let second)) + return mSelect(first, second); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + + public void Dispose() mut + { + mSource.Dispose(); + mOther.Dispose(); + } + } + + public static ZipEnumerable + Zip(this TCollection items, TCollection2 other, TSelect select) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items.GetEnumerator(), other.GetEnumerator(), select); + } + + public static ZipEnumerable + Zip(this TEnum items, TCollection2 other, TSelect select) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items, other.GetEnumerator(), select); + } + + public static ZipEnumerable + Zip(this TCollection items, TEnum other, TSelect select) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items.GetEnumerator(), other, select); + } + + public static ZipEnumerable + Zip(this TEnum items, TEnum2 other, TSelect select) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items, other, select); + } + + struct ConcatEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + Iterator mFirst; + Iterator mSecond; + int mState = 0; + + public this(TEnum firstEnumerator, TEnum2 secondEnumerator) + { + mFirst = firstEnumerator; + mSecond = secondEnumerator; + } + + Result GetNext() mut + { + switch (mState) { + case 0: + if (mFirst.mEnum.GetNext() case .Ok(let val)) + return .Ok(val); + + mState++; + fallthrough; + case 1: + if (mSecond.mEnum.GetNext() case .Ok(let val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mFirst.Dispose(); + mSecond.Dispose(); + } + } + + + + public static ConcatEnumerable + Concat(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static ConcatEnumerable + Concat(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + { + return .(items, other.GetEnumerator()); + } + + public static ConcatEnumerable + Concat(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + { + return .(items.GetEnumerator(), other); + } + + public static ConcatEnumerable + Concat(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + return .(items, other); + } + + public static ConcatEnumerable + Append(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static ConcatEnumerable + Append(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + { + return .(items, other.GetEnumerator()); + } + + public static ConcatEnumerable + Append(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + { + return .(items.GetEnumerator(), other); + } + + public static ConcatEnumerable + Append(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + return .(items, other); + } + + public static ConcatEnumerable + Prepend(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + { + return .(other.GetEnumerator(), items.GetEnumerator()); + } + + public static ConcatEnumerable + Prepend(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + { + return .(other.GetEnumerator(), items); + } + + public static ConcatEnumerable + Prepend(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + { + return .(other, items.GetEnumerator()); + } + + public static ConcatEnumerable + Prepend(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + return .(other, items); + } + + + + static class OrderByComparison + where int : operator T <=> T + { + typealias TCompare = delegate int(T lhs, T rhs); + public readonly static TCompare Comparison = (new (lhs, rhs) => lhs <=> rhs) ~ delete _; + } + + struct SortedEnumerable : IEnumerator<(TKey key, TSource value)>, IDisposable + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + List<(TKey key, TSource value)> mOrderedList; + Iterator mIterator; + TKeyDlg mKey; + TCompare mCompare; + int mIndex; + int mCount = 0; + bool mDescending; + + public this(TEnum firstEnumerator, TKeyDlg key, TCompare compare, bool descending) + { + mOrderedList = new .(); + mKey = key; + mIterator = firstEnumerator; + mCompare = compare; + mIndex = -1; + mDescending = descending; + } + + public Result<(TKey key, TSource value)> GetNext() mut + { + if (mIndex == -1) + { + while (mIterator.mEnum.GetNext() case .Ok(let val)) + mOrderedList.Add((mKey(val), val)); + + mOrderedList.Sort(scope (l, r) => mCompare(l.key, r.key)); + mCount = mOrderedList.Count;//keeping vars local + mIndex = mDescending ? mCount : 0; + } + + if (mDescending) + { + if (mIndex > 0) + return .Ok(mOrderedList[--mIndex]); + } + else if (mIndex < mCount) + return .Ok(mOrderedList[mIndex++]); + + return .Err; + } + + + public Enumerator GetEnumerator() => .(this); + public struct Enumerator : IEnumerator<(TKey key, TSource value)>, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result<(TKey key, TSource value)> GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + + public void Dispose() mut + { + mIterator.Dispose(); + DeleteAndNullify!(mOrderedList); + } + } + + //if the data is a large struct, this could get pretty slow with all the copies + //we may need to investigate the ability to use ptrs instead if the type is a struct? + struct OrderByEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + { + typealias TCompare = delegate int(TSource lhs, TSource rhs); + + struct Comparer + { + public TCompare comparer; + public bool descending; + } + + DynamicArray mValues; + int[] mMap; + //List mOrderedList; + List mCompares = default; + Iterator mIterator; + int mIndex; + + public this(TEnum enumerator, TCompare compare, bool descending) + { + mCompares = new .(); + var add = mCompares.GrowUnitialized(1); + add.comparer = compare; + add.descending = descending; + + mValues = ?; + mMap = null; + mIterator = enumerator; + mIndex = -1; + } + + void QuickSort(int left, int right) { + var left; + var right; +#unwarn + repeat { + int i = left; + int j = right; + int x = mMap[i + ((j - i) >> 1)]; + repeat { + while (i < mMap.Count && CompareKeys(x, mMap[i]) > 0) i++; + while (j >= 0 && CompareKeys(x, mMap[j]) < 0) j--; + if (i > j) break; + if (i < j) { + int temp = mMap[i]; + mMap[i] = mMap[j]; + mMap[j] = temp; + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) { + if (left < j) QuickSort(left, j); + left = i; + } + else { + if (i < right) QuickSort(i, right); + right = j; + } + } while (left < right); + } + + [Inline] + int CompareKeys(int index1, int index2) + { + for(var cmp in ref mCompares) + { + int c = cmp.comparer(mValues[index1], mValues[index2]); + if (c != 0) + return cmp.descending ? -c : c; + } + + return index1 - index2; + } + + Result GetNext() mut + { + if (mIndex == -1) + { + mIndex = 0; + mValues = .(); + while (mIterator.mEnum.GetNext() case .Ok(let val)) + mValues.Add(val); + + let count = mValues.Length; + mMap = new int[count]; + for (int i = 0; i < count; i++) mMap[i] = i; + QuickSort( 0, count - 1); + } + + if (mIndex < mValues.Length) + return .Ok(mValues[mMap[mIndex++]]); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mIterator.Dispose(); + + for(var it in mCompares) + delete it.comparer; + DeleteAndNullify!(mCompares); + DeleteAndNullify!(mMap); + mValues.Dispose(); + } + + public Self ThenBy(TKeyDlg2 keySelect, TCompare2 comparison) mut + where TKeyDlg2 : delegate TKey2(TSource) + where TCompare2 : delegate int(TKey2 lhs, TKey2 rhs) + { + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = false; + return this; + } + + public Self ThenBy(TKeyDlg2 keySelect) + where TKeyDlg2 : delegate TKey2(TSource) + where int : operator TKey2 <=> TKey2 + { + let comparison = OrderByComparison.Comparison; + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = false; + return this; + } + + + public Self ThenByDescending(TKeyDlg2 keySelect, TCompare2 comparison) mut + where TKeyDlg2 : delegate TKey2(TSource) + where TCompare2 : delegate int(TKey2 lhs, TKey2 rhs) + { + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = true; + return this; + } + + public Self ThenByDescending(TKeyDlg2 keySelect) + where TKeyDlg2 : delegate TKey2(TSource) + where int : operator TKey2 <=> TKey2 + { + let comparison = OrderByComparison.Comparison; + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = true; + return this; + } + } + + public static OrderByEnumerable + OrderBy(this TCollection items, TKeyDlg keySelect) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items.GetEnumerator(), new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderBy(this TEnum items, TKeyDlg keySelect) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderBy(this TCollection items, TKeyDlg keySelect, TCompare comparison) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items.GetEnumerator(), new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderBy(this TEnum items, TKeyDlg keySelect, TCompare comparison) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderByDescending(this TCollection items, TKeyDlg keySelect) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items.GetEnumerator(), new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + public static OrderByEnumerable + OrderByDescending(this TEnum items, TKeyDlg keySelect) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + public static OrderByEnumerable + OrderByDescending(this TCollection items, TKeyDlg keySelect, TCompare comparison) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items.GetEnumerator(),new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + public static OrderByEnumerable + OrderByDescending(this TEnum items, TKeyDlg keySelect, TCompare comparison) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + struct SelectManyEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TEnum2(TSource) + { + public readonly static String SelectManyEnum = new String() ~ delete _; + + Iterator mItems; + Iterator mCurrent = default; + TSelect mSelect; + int mState = -1; + + public this(TEnum firstEnumerator, TSelect select) + { + mItems = firstEnumerator; + mSelect = select; + } + + Result GetNext(out bool moveNext) mut + { + if (mState < 1) + { + if (mState == 0) + mCurrent.Dispose(); + + if (mItems.mEnum.GetNext() case .Ok(var val)) + { + mCurrent = mSelect(val); + mState = 1; + } + else + { + //no more elements + moveNext = false; + return .Err; + } + } + + if (mCurrent.mEnum.GetNext() case .Ok(let val)) + { + moveNext = false; + return .Ok(val); + } + + //done with current enumerator + mState = 0; + moveNext = true; + return .Err; + } + + Result GetNext() mut + { + var moveNext = true; + while (moveNext) + { + let result = GetNext(out moveNext); + if (!moveNext) + return result; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mItems.Dispose(); + } + } + extension SelectManyEnumerable + where TSelect : Object + { + public void Dispose() mut + { + base.Dispose(); + DeleteAndNullify!(mSelect); + } + } + + public static SelectManyEnumerable + SelectMany(this TCollection items, TSelect select) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSelect : delegate TCollection2(TSource) + { + return .(items.GetEnumerator(), new (x) => select(x).GetEnumerator()); + } + + public static SelectManyEnumerable + SelectMany(this TCollection items, TSelect select) + where TCollection : concrete, IEnumerable + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TEnum2(TSource) + { + return .(items.GetEnumerator(), select); + } + + /*struct OfTypeEnumerable : Iterator, IEnumerator, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : class + { + public this(TEnum enumerator) : base(enumerator) + { + } + + public Result GetNext() mut + { + while (mEnum.GetNext() case .Ok(let val)) + { + if (val is TOf) + return .Ok(*(TOf*)Internal.UnsafeCastToPtr(val)); + } + return .Err; + } + + public Self GetEnumerator() + { + return this; + } + } + + public static OfTypeEnumerable + OfType(this TCollection items) + where TCollection : concrete, IEnumerable + where TSource : class + { + return .(items.GetEnumerator()); + }*/ + + + } +} diff --git a/IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml b/IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml new file mode 100644 index 00000000..70d49547 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml @@ -0,0 +1,8 @@ +FileVersion = 1 +Dependencies = {corlib = "*", "Beef.Linq" = "*"} + +[Project] +Name = "Beef.Linq.Tests" +TargetType = "BeefTest" +StartupObject = "Befe.Linq.Tests.Program" +DefaultNamespace = "System.Linq" diff --git a/IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf b/IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf new file mode 100644 index 00000000..bc7e2f32 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf @@ -0,0 +1,773 @@ +//#define INCLUDE_FAILURES + +using System.Collections; +using System; +namespace System.Linq +{ + public class EnumerableTests + { + [Test] + public static void Any() + { + let data = scope List(); + + var actual = data.Any(); + Test.Assert(!actual); + + actual = data.Any((it) => it == 2); + Test.Assert(!actual); + + data.Add(1); + data.Add(2); + data.Add(3); + data.Add(4); + + actual = data.Any((it) => it == 2); + Test.Assert(actual); + + actual = data.Any(); + Test.Assert(actual); + + data.RemoveAt(1); + actual = data.Any((it) => it == 2); + Test.Assert(!actual); + } + + [Test] + public static void All() + { + let data = scope List(); + + var actual = data.All((it) => it == 2); + Test.Assert(!actual); + + data.Add(2); + data.Add(2); + data.Add(2); + data.Add(2); + + actual = data.All((it) => it == 2); + Test.Assert(actual); + + data.Add(3); + actual = data.All((it) => it == 2); + Test.Assert(!actual); + } + + + struct ContainsTest : IEnumerator + { + int mState = 0; + + public Result GetNext() mut + { + if (mState > 3) + return .Err; + + return .Ok(mState++); + } + } + + [Test] + public static void Contains() + { + let data = ContainsTest(); + + var actual = data.Contains(2); + Test.Assert(actual); + + actual = data.Contains(4); + Test.Assert(!actual); + } + + + [Test] + public static void Average() + { + let data = scope List() { 1, 1, 2, 2, 4 }; + { + let actual = data.Average(); + Test.Assert(actual == 2); + } + { + let actual = data.GetEnumerator().Average(); + Test.Assert(actual == 2); + } + } + + + [Test] + public static void Max() + { + { + let data = scope List(); + + var actual = data.Max(); + Test.Assert(actual == default); + + data.Add(3); + actual = data.Max(); + Test.Assert(actual == 3); + + data.Add(1); + actual = data.Max(); + Test.Assert(actual == 3); + } + { + let data = scope List(); + + var actual = data.GetEnumerator().Max(); + Test.Assert(actual == default); + + data.Add(3); + actual = data.GetEnumerator().Max(); + Test.Assert(actual == 3); + + data.Add(1); + actual = data.GetEnumerator().Max(); + Test.Assert(actual == 3); + } + } + + [Test] + public static void Min() + { + let data = scope List(); + + var actual = data.Min(); + Test.Assert(actual == default); + + data.Add(3); + actual = data.Min(); + Test.Assert(actual == 3); + + data.Add(1); + actual = data.Min(); + Test.Assert(actual == 1); + } + + [Test] + public static void Sum() + { + { + let data = scope List() { 1, 2, 3, 4 }; + let actual = data.Sum(); + Test.Assert(actual == 10); + } + { + let data = scope List() { 1, 2, 3, 4 }; + let actual = data.Sum(); + Test.Assert(actual == 10); + } + { + let data = scope List() { 1, null, 3, 4 }; + let actual = data.Sum(); + Test.Assert(actual == null); + } + { + let data = scope List(); + let actual = data.Sum(); + Test.Assert(actual == null); + } + } + + [Test] + public static void ElementAt() + { + let data = scope List() { 1, 2, 3 }; + let actual = data.ElementAt(1); + Test.Assert(actual == 1); + } + + [Test] + public static void First() + { + let data = scope List() { 1, 2, 3 }; + let actual = data.First(); + Test.Assert(actual == 1); + } + + [Test] + public static void FirstOrDefault() + { + let data = scope List(); + let actual = data.FirstOrDefault(); + Test.Assert(actual == default); + } + + [Test] + public static void Last() + { + let data = scope List() { 1, 2, 3 }; + let actual = data.Last(); + Test.Assert(actual == 3); + } + + [Test] + public static void LastOrDefault() + { + let data = scope List(); + let actual = data.LastOrDefault(); + Test.Assert(actual == default); + } + + [Test] + public static void Take() + { + let data = scope List(); + for (var i < 20) data.Add(i); + + let actual = data.Take(10).ToList(.. scope .()); + + let expected = scope List(); + for (var i < 10) expected.Add(i); + + Test.Assert(actual.Count == 10); + Test.Assert(actual.SequenceEquals(expected) == true); + } + + [Test] + public static void Skip() + { + let data = scope List(); + for (var i < 20) data.Add(i); + + let actual = data.Skip(10).ToList(.. scope .()); + + let expected = scope List(); + for (var i < 10) expected.Add(i + 10); + + Test.Assert(actual.Count == 10); + Test.Assert(actual.SequenceEquals(expected) == true); + } + + [Test] + public static void Empty() + { + let actual = Enumerable.Empty().ToList(.. scope .()); + Test.Assert(actual.Count == 0); + } + + [Test] + public static void Range() + { + { + let actual = Enumerable.Range(10).ToList(.. scope .()); + let expected = scope List(); + for (var i < 10) expected.Add(i); + Test.Assert(actual.SequenceEquals(expected) == true); + } + { + let actual = Enumerable.Range(10, 20).ToList(.. scope .()); + let expected = scope List(); + for (var i < 10) expected.Add(i + 10); + Test.Assert(actual.SequenceEquals(expected) == true); + } + } + + [Test] + public static void Map() + { + { + let data = scope List() { 0, 5, 10 }; + let actual = data.Map(0f, 1f).ToList(.. scope .()); + let expected = scope List() { 0f, 0.5f, 1f }; + + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List() { 0, 5, 10 }; + let actual = data.Map(0, 100).ToList(.. scope .()); + let expected = scope List(); + expected.Add(0); + expected.Add(50); + expected.Add(100); + + Test.Assert(actual.SequenceEquals(expected)); + } + } + + + [Test] + public static void Select() + { + let data = scope List<(int x, int y, float z, float w)>() { (1, 2, 3, 4), (4, 3, 2, 1) }; + let actual = data.Select((it) => (x: it.x, y: it.y)).ToList(.. scope .()); + let expected = scope List<(int x, int y)>(); + expected.Add((1, 2)); + expected.Add((4, 3)); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void Where() + { + let data = scope List<(int x, int y, float z, float w)>() { (1, 2, 3, 4), (4, 3, 2, 1) }; + let actual = data.Where((it) => it.x == 1).ToList(.. scope .()); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == (1, 2, 3, 4)); + } + + [Test] + public static void TakeWhile() + { + let data = scope List() { 1, 1, 2, 4 }; + let actual = data.TakeWhile((it) => it == 1).ToList(.. scope .()); + let expected = scope List(); + expected.Add(1); + expected.Add(1); + Test.Assert(actual.Count == 2); + } + + + [Test] + public static void SkipWhile() + { + let data = scope List() { 1, 1, 2, 3 }; + let actual = data.SkipWhile((it) => it == 1).ToList(.. scope .()); + let expected = scope List(); + expected.Add(2); + expected.Add(3); + Test.Assert(actual.Count == expected.Count); + } + + [Test] + public static void Repeat() + { + let actual = Enumerable.Repeat(10, 10).ToList(.. scope .()); + let expected = scope List(); + for (var i < 10) + expected.Add(10); + + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void Distinct() + { + let data = scope List() { 1, 1, 2, 3 }; + let actual = data.Distinct().ToList(.. scope .()); + let expected = scope List(); + expected.Add(1); + expected.Add(2); + expected.Add(3); + + Test.Assert(actual.Count == expected.Count); + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void Reverse() + { + let data = scope List() { 1, 1, 2, 3 }; + let actual = data.Reverse().ToList(.. scope .()); + let expected = scope List(); + expected.Add(3); + expected.Add(2); + expected.Add(1); + expected.Add(1); + + Test.Assert(actual.Count == expected.Count); + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void DefaultIfEmpty() + { + { + let data = scope List(); + let actual = data.DefaultIfEmpty().ToList(.. scope .()); + let expected = scope List(); + expected.Add(null); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == null); + } + { + let data = scope List(); + let actual = data.DefaultIfEmpty().ToList(.. scope .()); + let expected = scope List(); + expected.Add(10); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == 0); + } + { + let data = scope List(); + let actual = data.DefaultIfEmpty(10).ToList(.. scope .()); + let expected = scope List(); + expected.Add(10); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == 10); + } + } + + + [Test] + public static void Aggregate() + { + { + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate((sum, next) => sum + next); + Test.Assert(actual == 15); + } + { + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate(5, (sum, next) => sum + next); + Test.Assert(actual == 20); + } + + /*{ + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate( (sum, next) => sum + next, (result) => result * 1000f); + Test.Assert(actual == 15000f); + }*/ + { + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate(5, (sum, next) => sum + next, (result) => result * 1000f); + Test.Assert(actual == 20000f); + } + } + +#region ToXYZ methods + [Test] + public static void ToDictionary() + { + { + let data = scope List<(int x, float y)>() { (1, 2f), (4, 3f) }; + let actual = data.ToDictionary((it) => it.x, .. scope .()); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.Contains((1, (1, 2f)))); + Test.Assert(actual.Contains((4, (4, 3f)))); + } + { + let data = scope List<(int x, float y)>() { (1, 2f), (4, 3f) }; + let actual = data.ToDictionary((it) => it.x, (it) => it.y, .. scope .()); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.Contains((1, 2f))); + Test.Assert(actual.Contains((4, 3f))); + } + } + + [Test] + public static void ToHashSet() + { + let data = scope List() { 1, 2, 2 }; + let actual = data.ToHashSet(.. scope .()); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.Contains(1)); + Test.Assert(actual.Contains(2)); + } +#endregion + +#region GroupBy + + /*public static mixin GroupBy(this TCollection items, TKey key) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + { + let groupByMemory = scope:mixin GroupByMemory(); + return InternalGroupBy(groupByMemory, items, key); + }*/ + + [Test] + public static void GroupBy() + { + { + let data = scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8), (2, 4, 5), (1, 1, 1), (2, 2, 2) }; + let actual = data.GroupBy((key) => key.x, scope .()).ToList(.. scope .()); + + Test.Assert(actual.Count == 3); + + var i = 0; + for (var it in actual) + { + switch (it.Key) + { + case 0: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8) })); i |= 1; + case 1: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (1, 1, 1) })); i |= 2; + case 2: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (2, 4, 5), (2, 2, 2) })); i |= 4; + } + } + + Test.Assert(i == 7); + } + + { + let data = scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8), (2, 4, 5), (1, 1, 1), (2, 2, 2) }; + + var i = 0; + for (var it in data.GroupBy((key) => key.x)) + { + switch (it.Key) + { + case 0: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8) })); i |= 1; + case 1: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (1, 1, 1) })); i |= 2; + case 2: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (2, 4, 5), (2, 2, 2) })); i |= 4; + } + } + + Test.Assert(i == 7); + } + } + +#endregion + + [Test] + public static void Union() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 3, 4, 5 }; + let actual = data.Union(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[6](0, 1, 2, 3, 4, 5))); + } + + [Test] + public static void Intersect() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 1, 5, 2 }; + let actual = data.Intersect(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[2](1, 2))); + } + + [Test] + public static void Except() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 0, 5, 2 }; + let actual = data.Except(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[1](1))); + } + + [Test] + public static void Zip() + { + let data = scope List() { 0, 1, 2, 3 }; + let other = scope List() { 1, 2, 3 }; + let actual = data.Zip(other, (first, second) => first + second).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[3](1, 3, 5))); + } + + [Test] + public static void Concat() + { + let data = scope List() { 0, 1, 2, 3 }; + let other = scope List() { 1, 2, 3 }; + let actual = data.Concat(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[?](0, 1, 2, 3, 1, 2, 3))); + } + + [Test] + public static void Append() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 1, 2, 3, 3 }; + let actual = data.Append(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[?](0, 1, 2, 1, 2, 3, 3))); + } + + [Test] + public static void Prepend() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 1, 2, 3, 3 }; + let actual = data.Prepend(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[?](1, 2, 3, 3, 0, 1, 2))); + } + + [Test] + public static void OrderBy() + { + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + //orderby has some temp allocations, this test is just to make sure those temp allocations don't fail + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).OrderBy((it) => it.x, (l, r) => r - l).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (3, 2), (2, 0), (1, 2), (1, 3), (0, 4) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + [Test] + public static void OrderByDescending() + { + //this method shouldn't be using reverse, but I'm being lazy + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderByDescending((it) => it.x).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected.Reverse())); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderByDescending((it) => it.x, (l, r) => l - r).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected.Reverse())); + } + { + //orderby has some temp allocations, this test is just to make sure those temp allocations don't fail + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderByDescending((it) => it.x, (l, r) => l - r).OrderBy((it) => it.x, (l, r) => r - l).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (3, 2), (2, 0), (1, 2), (1, 3), (0, 4) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + [Test] + public static void ThenBy() + { + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x).ThenBy((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).ThenBy((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + //orderby has some temp allocations, this test is just to make sure those temp allocations don't fail + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).OrderBy((it) => it.x, (l, r) => r - l).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (3, 2), (2, 0), (1, 2), (1, 3), (0, 4) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + + [Test] + public static void ThenByDescending() + { + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x).ThenByDescending((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).ThenByDescending((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + [Test] + public static void SelectMany() + { + { + let data = scope List>() + { + scope List(), + scope List(), + scope List(), + scope List(), + scope List() + }; + + for (var i < 5) + for (var k < 2) + data[i].Add(i * 2 + k); + + let actual = data.SelectMany((x) => x).ToList(.. scope .()); + let actual2 = data.SelectMany((x) => x.GetEnumerator()).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(scope int[](0, 1, 2, 3, 4, 5, 6, 7, 8, 9))); + Test.Assert(actual2.SequenceEquals(scope int[](0, 1, 2, 3, 4, 5, 6, 7, 8, 9))); + } + } + +#region Failures +#if INCLUDE_FAILURES + [Test(ShouldFail = true)] + public static void ElementAtSequenceError() + { + let data = scope List(){ 1, 2, 3}; + data.ElementAt(4); + } + + [Test(ShouldFail = true)] + public static void FirstFatalOnEmpty() + { + let data = scope List(); + data.First(); + } + + [Test(ShouldFail = true)] + public static void LastFatalOnEmpty() + { + let data = scope List(); + + data.Last(); + } +#endif +#endregion + + [AttributeUsage(.Method)] + public struct MyTestAttribute : Attribute + { + } + + [Reflect(.Methods), AlwaysInclude(IncludeAllMethods = true)] + public struct ReflectionTest + { + [MyTest, AlwaysInclude, Reflect] + public int HelloWorld() { return 1; } + } + +#region Reported bugs + + /*[Test] + public static void HitGetMethodsReflectionIssue() + { + let actual = typeof(ReflectionTest).GetMethods().Where((m) => m.GetCustomAttribute() case .Ok).ToList(.. scope .()); + Test.Assert(actual.Count == 1); + }*/ + + [Test] + public static void HigCallingMutatingIssue() + { + int[] test1 = scope .(10, 11, 10, 12, 13, 14, -1); + let actual = test1.Reverse().Where((x) => x > 0 && x % 2 == 0).Sum(); + + Test.Assert(actual == 46); + } +#endregion + } +} diff --git a/IDEHelper/Tests/src/Arrays.bf b/IDEHelper/Tests/src/Arrays.bf index 3cac81fc..e0e3b3ef 100644 --- a/IDEHelper/Tests/src/Arrays.bf +++ b/IDEHelper/Tests/src/Arrays.bf @@ -1,3 +1,5 @@ +#pragma warning disable 168 + using System; namespace Tests { @@ -22,6 +24,10 @@ namespace Tests Test.Assert(sa.mB == 22); Test.Assert(sa.mC == 33); + var val = arr[(.)1]; + int[3] arr2 = .(1, 2, 3); + var val2 = arr2[(.)1]; + #if BF_64_BIT /*int a = (int)(void*)&sa - (int)Internal.UnsafeCastToPtr(arr); int b = typeof(System.Array).InstanceSize; diff --git a/IDEHelper/Tests/src/Comptime.bf b/IDEHelper/Tests/src/Comptime.bf index 2fe333cd..385b6fa7 100644 --- a/IDEHelper/Tests/src/Comptime.bf +++ b/IDEHelper/Tests/src/Comptime.bf @@ -84,6 +84,8 @@ namespace Tests struct StructA { public int mA = 123; + public static StructA sSA; + public const StructA cSA = .(); [OnCompile(.TypeInit), Comptime] public static void Generate() diff --git a/IDEHelper/Tests/src/Delegates.bf b/IDEHelper/Tests/src/Delegates.bf index d191e847..639fadb0 100644 --- a/IDEHelper/Tests/src/Delegates.bf +++ b/IDEHelper/Tests/src/Delegates.bf @@ -114,6 +114,18 @@ namespace Tests public delegate int DelegateB(T val); } + [CRepr] + struct Vector3f + { + public float mX; + public float mY; + } + + static Vector3f GetVector3f() + { + return .() { mX = 101, mY = 102 }; + } + [Test] public static void TestBasics() { @@ -167,6 +179,9 @@ namespace Tests e += new (sender, e) => {}; e += new => LocalEventHandler; e.Dispose(); + + delegate Vector3f() vecDlg = scope => GetVector3f; + Test.Assert(vecDlg().mX == 101); } public static void Modify(ref int a, ref Splattable b) diff --git a/IDEHelper/Tests/src/Enums.bf b/IDEHelper/Tests/src/Enums.bf index 8e6d01b3..43f139cf 100644 --- a/IDEHelper/Tests/src/Enums.bf +++ b/IDEHelper/Tests/src/Enums.bf @@ -114,6 +114,13 @@ namespace Tests } Test.Assert(val == 0); + const EnumA cea = .A; + switch (cea) + { + case .A: + val = 123; + } + ee = .B(10); } diff --git a/IDEHelper/Tests/src/Expressions.bf b/IDEHelper/Tests/src/Expressions.bf new file mode 100644 index 00000000..dd7eea67 --- /dev/null +++ b/IDEHelper/Tests/src/Expressions.bf @@ -0,0 +1,22 @@ +#pragma warning disable 168 + +using System; + +namespace Tests +{ + class Expressions + { + [Test] + public static void TestBasics() + { + int num = -1; + if ( { + var obj = scope Object(); + num > 999 + }) + { + Test.FatalError(); + } + } + } +} diff --git a/IDEHelper/Tests/src/Floats.bf b/IDEHelper/Tests/src/Floats.bf new file mode 100644 index 00000000..9bbb2e2a --- /dev/null +++ b/IDEHelper/Tests/src/Floats.bf @@ -0,0 +1,26 @@ +using System; + +namespace Tests +{ + class Floats + { + public static void FloatParseTest(StringView string, float expectedResult) + { + float result = float.Parse(string); + Test.Assert(expectedResult == result); + } + + [Test] + public static void TestBasics() + { + FloatParseTest("1.2", 1.2f); + FloatParseTest("-0.2", -0.2f); + FloatParseTest("2.5E2", 2.5E2f); + FloatParseTest("2.7E-10", 2.7E-10f); + FloatParseTest("-0.17E-7", -0.17E-7f); + FloatParseTest("8.7e6", 8.7e6f); + FloatParseTest("3.3e-11", 3.3e-11f); + FloatParseTest("0.002e5", 0.002e5f); + } + } +} diff --git a/IDEHelper/Tests/src/Functions.bf b/IDEHelper/Tests/src/Functions.bf index 7228b7c3..24f22151 100644 --- a/IDEHelper/Tests/src/Functions.bf +++ b/IDEHelper/Tests/src/Functions.bf @@ -8,19 +8,32 @@ namespace Tests { class ClassA { - int mA = 123; + public int mA = 123; public int GetA(float f) { return mA + (int)f; } + public virtual int GetB(float f) + { + return mA + (int)f + 1000; + } + public int GetT(T val) where T : var { return mA + (int)val; } } + class ClassB : ClassA + { + public override int GetB(float f) + { + return mA + (int)f + 2000; + } + } + struct StructA { int mA = 123; @@ -130,6 +143,22 @@ namespace Tests } } + [CRepr] + struct StructCRepr + { + public int32 something = 1; + + bool Run() => something == 1; + + public static void Test() + { + StructCRepr sc = .(); + function bool(StructCRepr this) func = => Run; + + Test.Assert(func(sc)); + } + } + public static int UseFunc0(function int (T this, float f) func, T a, float b) { return func(a, b); @@ -143,7 +172,9 @@ namespace Tests [Test] public static void TestBasics() { - ClassA ca = scope .(); + ClassA ca = scope ClassA(); + ClassA ca2 = scope ClassB(); + StructA sa = .(); StructB sb = .(); @@ -153,6 +184,17 @@ namespace Tests func0 = => ca.GetT; Test.Assert(func0(ca, 100.0f) == 223); + func0 = => ca.GetB; + Test.Assert(func0(ca, 100.0f) == 1223); + func0 = => ca2.GetB; + Test.Assert(func0(ca, 100.0f) == 2223); + func0 = => ClassA.GetB; + Test.Assert(func0(ca, 100.0f) == 1223); + func0 = => ClassB.GetB; + Test.Assert(func0(ca, 100.0f) == 2223); + + func0 = => ca.GetA; + function int (StructA this, float f) func1 = => sa.GetA; var func1b = func1; Test.Assert(func1(sa, 100.0f) == 23623); @@ -182,6 +224,8 @@ namespace Tests UseFunc0(func2, sa, 100.0f); true }); + + StructCRepr.Test(); } } } diff --git a/IDEHelper/Tests/src/Generics.bf b/IDEHelper/Tests/src/Generics.bf index e49acc62..01d420b6 100644 --- a/IDEHelper/Tests/src/Generics.bf +++ b/IDEHelper/Tests/src/Generics.bf @@ -296,6 +296,18 @@ namespace Tests } + public static void TestGen(T val) + where T : IEnumerable + where TItem : var + { + } + + public static void TestPreGen() + { + List a = default; + TestGen(a); + } + [Test] public static void TestBasics() { diff --git a/IDEHelper/Tests/src/Indexers.bf b/IDEHelper/Tests/src/Indexers.bf index ae4fd683..978867d3 100644 --- a/IDEHelper/Tests/src/Indexers.bf +++ b/IDEHelper/Tests/src/Indexers.bf @@ -24,6 +24,14 @@ namespace Tests return 234; } } + + public int BaseIndex + { + get + { + return base[2]; + } + } } class ClassC : ClassB @@ -33,7 +41,7 @@ namespace Tests struct StructA { - int mA; + public int mA; public int this[int index] { @@ -47,6 +55,25 @@ namespace Tests return 2; } } + + public int this[params int[] indices] + { + get mut + { + int total = 0; + for (var i in indices) + total += i; + mA += total; + return total; + } + + set mut + { + for (var i in indices) + mA += i; + mA += value * 1000; + } + } } [Test] @@ -61,12 +88,20 @@ namespace Tests Test.Assert(value == 234); value = ca[0]; Test.Assert(value == 234); + value = cb.BaseIndex; + Test.Assert(value == 123); StructA sa = default; let sa2 = sa; Test.Assert(sa[0] == 2); Test.Assert(sa2[0] == 1); + + sa[3, 4, 5] = 9; + Test.Assert(sa.mA == 9012); + int a = sa[3, 4, 5]; + Test.Assert(a == 12); + Test.Assert(sa.mA == 9024); } } } diff --git a/IDEHelper/Tests/src/Ints.bf b/IDEHelper/Tests/src/Ints.bf index f9d35fcd..07836e3a 100644 --- a/IDEHelper/Tests/src/Ints.bf +++ b/IDEHelper/Tests/src/Ints.bf @@ -38,6 +38,10 @@ namespace Tests { Test.Assert(0b0111010110111100110100010101 == 123456789); Test.Assert(0o726746425 == 123456789); + + int i0 = 5; + int i1 = i0 % 1; + Test.Assert(i1 == 0); } } } diff --git a/IDEHelper/Tests/src/Loops.bf b/IDEHelper/Tests/src/Loops.bf index b08a07c2..9909ec56 100644 --- a/IDEHelper/Tests/src/Loops.bf +++ b/IDEHelper/Tests/src/Loops.bf @@ -1,6 +1,8 @@ #pragma warning disable 168 using System; +using System.Collections; +using System.Diagnostics; namespace Tests { @@ -12,6 +14,28 @@ namespace Tests public int32 mB; } + class EnumeratorTest : IEnumerator, IDisposable + { + public int mDispCount; + + public void Dispose() + { + mDispCount++; + } + + public Result GetNext() + { + return .Err; + } + } + + static int sGetValCount = 0; + + static int GetVal() + { + return 10 + sGetValCount++ / 2; + } + [Test] public static void TestBasics() { @@ -26,6 +50,146 @@ namespace Tests StructA sa = val; int idx = @val; } + + var e = scope EnumeratorTest(); + for (var val in e) + { + } + Test.Assert(e.mDispCount == 1); + for (var val in e) + { + break; + } + Test.Assert(e.mDispCount == 2); + TestEnumerator1(e); + Test.Assert(e.mDispCount == 3); + TestEnumerator2(e); + Test.Assert(e.mDispCount == 4); + + int iterations = 0; + for (int i < GetVal()) + { + iterations++; + } + Test.Assert(iterations == 19); + Test.Assert(sGetValCount == 20); + + int total = 0; + for (int i in 1..<10) + { + if (i == 5) + Test.Assert(total == 1+2+3+4); + total += i; + } + Test.Assert(total == 1+2+3+4+5+6+7+8+9); + + total = 0; + for (int i in 1...10) + { + if (i == 5) + Test.Assert(total == 1+2+3+4); + total += i; + } + Test.Assert(total == 1+2+3+4+5+6+7+8+9+10); + + total = 0; + for (int i in (1..<10).Reversed) + { + if (i == 5) + Test.Assert(total == 9+8+7+6); + total += i; + + } + Test.Assert(total == 9+8+7+6+5+4+3+2+1); + + total = 0; + for (int i in (1...10).Reversed) + { + if (i == 5) + Test.Assert(total == 10+9+8+7+6); + total += i; + + } + Test.Assert(total == 10+9+8+7+6+5+4+3+2+1); + + Test.Assert(!(1...3).Contains(0)); + Test.Assert((1...3).Contains(1)); + Test.Assert((1...3).Contains(2)); + Test.Assert((1...3).Contains(3)); + Test.Assert(!(1...3).Contains(4)); + + Test.Assert(!(1..<3).Contains(0)); + Test.Assert((1..<3).Contains(1)); + Test.Assert((1..<3).Contains(2)); + Test.Assert(!(1..<3).Contains(3)); + + Test.Assert((1...3).Contains(1...3)); + Test.Assert((1...3).Contains(1...2)); + Test.Assert(!(1...3).Contains(1...4)); + Test.Assert((1...3).Contains(2..<3)); + Test.Assert((1...3).Contains(2..<4)); + Test.Assert(!(1...3).Contains(2..<5)); + Test.Assert(!(1..<3).Contains(1...3)); + Test.Assert((1..<3).Contains(1..<3)); + Test.Assert(!(1..<3).Contains(1..<4)); + + List iList = scope .() { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; + total = 0; + for (int i in iList[...]) + total += i; + Test.Assert(total == 10+20+30+40+50+60+70+80+90+100); + + total = 0; + for (int i in iList[1...]) + total += i; + Test.Assert(total == 20+30+40+50+60+70+80+90+100); + + total = 0; + for (int i in iList[...^2]) + total += i; + Test.Assert(total == 10+20+30+40+50+60+70+80+90); + + total = 0; + for (int i in iList[..<^2]) + total += i; + Test.Assert(total == 10+20+30+40+50+60+70+80); + + int itr = 0; + total = 0; + for (int i in iList[...^2][1...^2].Reversed) + { + if (itr == 1) + Test.Assert(i == 70); + total += i; + ++itr; + } + Test.Assert(total == 80+70+60+50+40+30+20); + + var str = scope String(); + (2...^3).ToString(str); + Test.Assert(str == "2...^3"); + + int[] iEmptyArr = scope .(); + var emptySpan = iEmptyArr[...]; + + for (var i in emptySpan.Reversed) + Test.FatalError(); + } + + public static void TestEnumerator1(EnumeratorTest e) + { + for (var val in e) + { + return; + } + } + + public static void TestEnumerator2(EnumeratorTest e) + { + for (var val in e) + { + return; + } } } } diff --git a/IDEHelper/Tests/src/Mixins.bf b/IDEHelper/Tests/src/Mixins.bf index 288d74e9..b7078cf3 100644 --- a/IDEHelper/Tests/src/Mixins.bf +++ b/IDEHelper/Tests/src/Mixins.bf @@ -1,4 +1,5 @@ using System; +using System.Collections; namespace Tests { @@ -47,6 +48,31 @@ namespace Tests a = 234; } + public static mixin CircularMixin(T value) + where T : var + { + 10 + } + + public static mixin CircularMixin(Dictionary value) + where K : var, IHashable + where V : var + { + + int total = 0; + if (value == null) + total += 1; + else + { + for (let (k, v) in ref value) + { + total += CircularMixin!(k); + total += CircularMixin!(*v); + } + } + total + 100 + } + [Test] public static void TestBasics() { @@ -73,6 +99,10 @@ namespace Tests CheckStr(sv.ToScopeCStr!()); }; func("Test"); + + Dictionary> test = scope .() {(1,null)}; + int val = CircularMixin!(test); + Test.Assert(val == 211); } [Test] diff --git a/IDEHelper/Tests/src/Operators.bf b/IDEHelper/Tests/src/Operators.bf index 3358b3a5..cdd1d04a 100644 --- a/IDEHelper/Tests/src/Operators.bf +++ b/IDEHelper/Tests/src/Operators.bf @@ -198,6 +198,41 @@ namespace Tests } } + struct StructH + { + public static int operator+(StructH lhs, int rhs) + { + return 1; + } + + public static int operator+(int lhs, StructH rhs) + { + return 2; + } + + [Commutable] + public static int operator+(StructH lhs, float rhs) + { + return 3; + } + + public static int operator&+(StructH lhs, int rhs) + { + return 4; + } + + public static int operator&+(int lhs, StructH rhs) + { + return 5; + } + + [Commutable] + public static int operator&+(StructH lhs, float rhs) + { + return 6; + } + } + struct StructOp where T : operator T + T2 { public T DoIt(T val, T2 val2) @@ -428,6 +463,20 @@ namespace Tests Test.Assert(10 + sf == 2); Test.Assert(sf + 1.0f == 3); Test.Assert(2.0f + sf == 3); + Test.Assert(sf &+ 10 == 1); + Test.Assert(10 &+ sf == 2); + Test.Assert(sf &+ 1.0f == 3); + Test.Assert(2.0f &+ sf == 3); + + StructH sh = default; + Test.Assert(sh + 10 == 1); + Test.Assert(10 + sh == 2); + Test.Assert(sh + 1.0f == 3); + Test.Assert(2.0f + sh == 3); + Test.Assert(sh &+ 10 == 4); + Test.Assert(10 &+ sh == 5); + Test.Assert(sh &+ 1.0f == 6); + Test.Assert(2.0f &+ sh == 6); StructG sg = .(100); StructG sg2 = .(200); diff --git a/IDEHelper/Tests/src/Program.bf b/IDEHelper/Tests/src/Program.bf index fa66b2d0..5694db1e 100644 --- a/IDEHelper/Tests/src/Program.bf +++ b/IDEHelper/Tests/src/Program.bf @@ -4,7 +4,7 @@ namespace Tests { public static void Main() { - + } } } diff --git a/IDEHelper/Tests/src/Reflection.bf b/IDEHelper/Tests/src/Reflection.bf index 89dcc0f1..f7bb4ad6 100644 --- a/IDEHelper/Tests/src/Reflection.bf +++ b/IDEHelper/Tests/src/Reflection.bf @@ -46,11 +46,13 @@ namespace Tests { public int32 mA; public float mB; + public Type mC; - public this(int32 a, float b) + public this(int32 a, float b, Type c) { mA = a; mB = b; + mC = c; } } @@ -69,7 +71,7 @@ namespace Tests public static int32 sA = 234; public static String sStr = "AA"; - [AlwaysInclude, AttrC(71, 72)] + [AlwaysInclude, AttrC(71, 72, typeof(float))] static float StaticMethodA(int32 a, int32 b, float c, ref int32 d, ref StructA sa) { d += a + b; @@ -169,7 +171,7 @@ namespace Tests } } - [Reflect(.All), AttrC(1, 2)] + [Reflect(.All), AttrC(1, 2, typeof(StringView))] class ClassB { [AttrA(11, 22, "StrA")] @@ -317,6 +319,7 @@ namespace Tests let attrC = methodInfo.GetCustomAttribute().Get(); Test.Assert(attrC.mA == 71); Test.Assert(attrC.mB == 72); + Test.Assert(attrC.mC == typeof(float)); case 5: Test.Assert(methodInfo.Name == "StaticMethodB"); @@ -418,6 +421,7 @@ namespace Tests let attrC = typeof(ClassB).GetCustomAttribute().Get(); Test.Assert(attrC.mA == 1); Test.Assert(attrC.mB == 2); + Test.Assert(attrC.mC == typeof(StringView)); ClassC c = scope .(); Test.Assert(typeof(ClassC).GetField("mA").Value.Name == "mA"); diff --git a/IDEHelper/Tests/src/Reflection2.bf b/IDEHelper/Tests/src/Reflection2.bf new file mode 100644 index 00000000..c9ab725d --- /dev/null +++ b/IDEHelper/Tests/src/Reflection2.bf @@ -0,0 +1,30 @@ +#pragma warning disable 168 + +using System; + +namespace Tests +{ + class Reflection2 + { + public typealias RemovePtr = comptype(RemovePtr(typeof(T))); + + [Comptime] + public static Type RemovePtr(Type type) + { + if (type.IsPointer) + return type.UnderlyingType; + + return type; + } + + [Test] + public static void TestBasics() + { + const Type t = typeof(StringView); + int fieldCount = t.FieldCount; + + Test.Assert(typeof(RemovePtr) == typeof(int32)); + Test.Assert(typeof(RemovePtr) == typeof(uint32)); + } + } +} diff --git a/IDEHelper/Tests/src/Structs.bf b/IDEHelper/Tests/src/Structs.bf index 9fe14660..ff30c0e2 100644 --- a/IDEHelper/Tests/src/Structs.bf +++ b/IDEHelper/Tests/src/Structs.bf @@ -135,6 +135,24 @@ namespace Tests } } + public struct StructN + { + public int mA = 123; + } + + public struct StructO + { + public StructN mA; + } + + public static StructN GetStructN() + { + var sn = StructN(); + var so = scope StructO(); + so.mA = sn; + return sn; + } + [Test] static void TestBasics() { @@ -168,6 +186,9 @@ namespace Tests Test.Assert(sm.a == 12); Test.Assert(sm.b == 23); Test.Assert(sm.c == 200); + + StructN sn = GetStructN(); + Test.Assert(sn.mA == 123); } [Test] diff --git a/IDEHelper/Tests/src/Switches.bf b/IDEHelper/Tests/src/Switches.bf new file mode 100644 index 00000000..6648fefb --- /dev/null +++ b/IDEHelper/Tests/src/Switches.bf @@ -0,0 +1,18 @@ +using System; + +namespace Tests +{ + class Switches + { + int Switch0(Result res) + { + switch (res) + { + case .Ok(let a): + return 0; + case .Err(let b): + return 1; + } + } + } +} diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index b443c538..c2d98fa6 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -1003,7 +1003,7 @@ bool WinDebugger::CanOpen(const StringImpl& fileName, DebuggerResult* outResult) return canRead; } -void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array& envBlock) +void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array& envBlock, bool hotSwapEnabled) { BF_ASSERT(!mIsRunning); mLaunchPath = launchPath; @@ -1011,6 +1011,7 @@ void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targe mArgs = args; mWorkingDir = workingDir; mEnvBlock = envBlock; + mHotSwapEnabled = hotSwapEnabled; mDebugTarget = new DebugTarget(this); } @@ -7477,14 +7478,14 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c if ((valueCount == 0) || (bitsLeft != 0)) { if (valueCount > 0) - retVal += " | "; - retVal += StrFormat("%d", bitsLeft); + retVal += " | "; + retVal += StrFormat("%lld", bitsLeft); if (language == DbgLanguage_C) { if (valueCount > 0) editVal += " | "; - editVal += StrFormat("%d", bitsLeft); + editVal += StrFormat("%lld", bitsLeft); } } diff --git a/IDEHelper/WinDebugger.h b/IDEHelper/WinDebugger.h index 9ee575f7..8cb9f511 100644 --- a/IDEHelper/WinDebugger.h +++ b/IDEHelper/WinDebugger.h @@ -427,6 +427,7 @@ public: String mTargetPath; String mArgs; String mWorkingDir; + bool mHotSwapEnabled; Array mEnvBlock; DebugTarget* mEmptyDebugTarget; DebugTarget* mDebugTarget; @@ -617,7 +618,7 @@ public: virtual void OutputRawMessage(const StringImpl& msg) override; virtual int GetAddrSize() override; virtual bool CanOpen(const StringImpl& fileName, DebuggerResult* outResult) override; - virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array& envBlock) override; + virtual void OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array& envBlock, bool hotSwapEnabled) override; virtual bool Attach(int processId, BfDbgAttachFlags attachFlags) override; virtual void Run() override; virtual void HotLoad(const Array& objectFiles, int hotIdx) override; diff --git a/bin/build.sh b/bin/build.sh index 1ba7f886..5c99230f 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -59,6 +59,14 @@ ln -s -f $ROOTPATH/jbuild/Release/bin/libBeefRT.a libBeefRT.a ln -s -f $ROOTPATH/jbuild/Release/bin/libBeefySysLib.$LIBEXT libBeefySysLib.$LIBEXT ln -s -f $ROOTPATH/jbuild/Release/bin/libIDEHelper.$LIBEXT libIDEHelper.$LIBEXT +ln -s -f $ROOTPATH/jbuild_d/Debug/bin/libBeefRT_d.a ../../BeefLibs/Beefy2D/dist/libBeefRT_d.a +ln -s -f $ROOTPATH/jbuild_d/Debug/bin/libBeefySysLib_d.$LIBEXT ../../BeefLibs/Beefy2D/dist/libBeefySysLib_d.$LIBEXT +ln -s -f $ROOTPATH/jbuild_d/Debug/bin/libIDEHelper_d.$LIBEXT ../../BeefLibs/Beefy2D/dist/libIDEHelper_d.$LIBEXT + +ln -s -f $ROOTPATH/jbuild/Release/bin/libBeefRT.a ../../BeefLibs/Beefy2D/dist/libBeefRT.a +ln -s -f $ROOTPATH/jbuild/Release/bin/libBeefySysLib.$LIBEXT ../../BeefLibs/Beefy2D/dist/libBeefySysLib.$LIBEXT +ln -s -f $ROOTPATH/jbuild/Release/bin/libIDEHelper.$LIBEXT ../../BeefLibs/Beefy2D/dist/libIDEHelper.$LIBEXT + ### DEBUG ### echo Building BeefBuild_bootd diff --git a/bin/test_build.bat b/bin/test_build.bat index 921387e8..4e3b35f2 100644 --- a/bin/test_build.bat +++ b/bin/test_build.bat @@ -15,6 +15,8 @@ CALL bin/msbuild.bat IDEHelper\Tests\CLib\CLib.vcxproj /p:Configuration=Debug /p @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR IDE\dist\BeefBuild_d -proddir=IDEHelper\Tests -test @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR +IDE\dist\BeefBuild_d -proddir=IDEHelper\Tests\BeefLinq -test +@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR @ECHO Testing IDEHelper\Tests (Win32) CALL bin/msbuild.bat IDEHelper\Tests\CLib\CLib.vcxproj /p:Configuration=Debug /p:Platform=x86 diff --git a/bin/test_ide.bat b/bin/test_ide.bat index 176e966b..afb06e49 100644 --- a/bin/test_ide.bat +++ b/bin/test_ide.bat @@ -63,6 +63,10 @@ PUSHD %~dp0..\ @CALL :TEST @IF !ERRORLEVEL! NEQ 0 GOTO HADERROR +@SET TESTPATH=IDE\Tests\BugW007 +@CALL :TEST +@IF !ERRORLEVEL! NEQ 0 GOTO HADERROR + @SET TESTPATH=IDE\Tests\IndentTest @CALL :TEST @IF !ERRORLEVEL! NEQ 0 GOTO HADERROR