Skip to content

Commit

Permalink
ScriptEditor: Fixed underestimation of struct size for LE.
Browse files Browse the repository at this point in the history
LE pointers are 8 bytes, not 4! Getting this wrong could lead to the stack getting corrupted when calling a function that returns a large struct.
  • Loading branch information
SirCxyrtyx committed Jul 27, 2021
1 parent 5fe0ea6 commit 1bd3358
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace LegendaryExplorerCore.UnrealScript.Analysis.Symbols

public class SymbolTable
{
public class OperatorDefinitions
private class OperatorDefinitions
{
public readonly CaseInsensitiveDictionary<List<PreOpDeclaration>> PrefixOperators = new();
public readonly CaseInsensitiveDictionary<List<InOpDeclaration>> InfixOperators = new();
Expand Down Expand Up @@ -49,28 +49,32 @@ public static bool IsPrimitive(VariableType vt) =>
private readonly OperatorDefinitions Operators;
public List<string> InFixOperatorSymbols => Operators.InFixOperatorSymbols;

private SymbolTable()
public readonly MEGame Game;

private SymbolTable(MEGame game)
{
Operators = new OperatorDefinitions();
ScopeNames = new LinkedList<string>();
Scopes = new LinkedList<ASTNodeDict>();
Cache = new CaseInsensitiveDictionary<ASTNodeDict>();
Types = new CaseInsensitiveDictionary<VariableType>();
Game = game;
}

private SymbolTable(LinkedList<string> scopeNames, LinkedList<ASTNodeDict> scopes, CaseInsensitiveDictionary<ASTNodeDict> cache, CaseInsensitiveDictionary<VariableType> types, OperatorDefinitions ops)
private SymbolTable(LinkedList<string> scopeNames, LinkedList<ASTNodeDict> scopes, CaseInsensitiveDictionary<ASTNodeDict> cache, CaseInsensitiveDictionary<VariableType> types, OperatorDefinitions ops, MEGame game)
{
Operators = ops;
ScopeNames = scopeNames;
Scopes = scopes;
Cache = cache;
Types = types;
Game = game;
}

public static SymbolTable CreateIntrinsicTable(Class objectClass, MEGame game)
{
const EClassFlags intrinsicClassFlags = EClassFlags.Intrinsic;
var table = new SymbolTable();
var table = new SymbolTable(game);

#region CORE

Expand Down Expand Up @@ -883,7 +887,8 @@ public SymbolTable Clone()
new LinkedList<ASTNodeDict>(Scopes.Select(dict => new ASTNodeDict(dict))),
new CaseInsensitiveDictionary<ASTNodeDict>(Cache.ToDictionary(kvp => kvp.Key, kvp => new ASTNodeDict(kvp.Value))),
new CaseInsensitiveDictionary<VariableType>(Types),
Operators);
Operators,
Game);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ public bool VisitNode(Function node)
if (node.ReturnValueDeclaration is not null)
{
//if the return type is > 64 bytes, it can't be allocated on the stack.
node.RetValNeedsDestruction = node.ReturnValueDeclaration.Flags.Has(EPropertyFlags.NeedCtorLink) || node.ReturnType.Size > 64;
node.RetValNeedsDestruction = node.ReturnValueDeclaration.Flags.Has(EPropertyFlags.NeedCtorLink) || node.ReturnType.Size(Symbols.Game) > 64;
}
}
return Success;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ public bool VisitNode(Goto node)
public bool VisitNode(ExpressionOnlyStatement node)
{

if (GetAffector(node.Value) is Function func && func.RetValNeedsDestruction)
if (GetAffector(node.Value) is {RetValNeedsDestruction: true} func)
{
WriteOpCode(OpCodes.EatReturnValue);
WriteObjectRef(ResolveReturnValue(func));
Expand Down Expand Up @@ -1389,7 +1389,7 @@ private void EmitVariableSize(Expression expr)
{
null => 0,
{ PropertyType: EPropertyType.StringRef } => 0,
_ => (byte)exprType.Size
_ => (byte)exprType.Size(Game)
});
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using LegendaryExplorerCore.Packages;
using LegendaryExplorerCore.UnrealScript.Analysis.Visitors;
using LegendaryExplorerCore.UnrealScript.Utilities;

Expand Down Expand Up @@ -30,7 +31,7 @@ public override IEnumerable<ASTNode> ChildNodes
}
}

public override int Size => (ElementType?.Size ?? 0) * Length;
public override int Size(MEGame game) => (ElementType?.Size(game) ?? 0) * Length;

public bool Equals(StaticArrayType other)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using LegendaryExplorerCore.Gammtek.Extensions;
using LegendaryExplorerCore.Packages;
using LegendaryExplorerCore.Unreal.BinaryConverters;
using LegendaryExplorerCore.UnrealScript.Analysis.Visitors;
using LegendaryExplorerCore.UnrealScript.Utilities;
Expand Down Expand Up @@ -74,16 +75,13 @@ public string GetInheritanceString()
return str;
}

public override int Size
public override int Size(MEGame game)
{
get
{
(int structSize, _) = GetSizeAndAlign();
return structSize;
}
(int structSize, _) = GetSizeAndAlign(game);
return structSize;
}

private (int structSize, int structAlign) GetSizeAndAlign()
private (int structSize, int structAlign) GetSizeAndAlign(MEGame game)
{
int structSize = 0;
int structAlign = 4;
Expand All @@ -92,7 +90,7 @@ public override int Size
foreach (VariableDeclaration varDecl in VariableDeclarations)
{
VariableType cur = varDecl.VarType;
int varSize = cur.Size;
int varSize = cur.Size(game);
int varAlign = 4;
if (cur is StaticArrayType staticArrayType)
{
Expand Down Expand Up @@ -121,17 +119,37 @@ public override int Size
}
else if (cur.PropertyType == EPropertyType.String)
{
varSize = 12 * varDecl.ArrayLength; //TODO: verify this
if (game.IsLEGame())
{
varSize = 16 * varDecl.ArrayLength;
varAlign = 8;
}
else
{
varSize = 12 * varDecl.ArrayLength; //TODO: verify this
}
}
else if (cur is DynamicArrayType)
{
varSize = 12; //TODO: verify this
if (game.IsLEGame())
{
varSize = 16;
varAlign = 8;
}
else
{
varSize = 12;
}
}
else if (cur is Struct curStruct)
{
(varSize, varAlign) = curStruct.GetSizeAndAlign();
(varSize, varAlign) = curStruct.GetSizeAndAlign(game);
varSize *= varDecl.ArrayLength;
}
else if (cur.PropertyType is EPropertyType.Object or EPropertyType.Delegate && game.IsLEGame())
{
varAlign = 8;
}

structSize = structSize.Align(varAlign) + varSize;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using LegendaryExplorerCore.Packages;
using LegendaryExplorerCore.Unreal;
using LegendaryExplorerCore.UnrealScript.Analysis.Visitors;
using LegendaryExplorerCore.UnrealScript.Utilities;
Expand Down Expand Up @@ -41,7 +42,7 @@ public override IEnumerable<ASTNode> ChildNodes
}
}

public int GetSize() => VarType?.Size ?? 0;
public int GetSize(MEGame game) => VarType?.Size(game) ?? 0;

public string FilePath { get; init; }
public int UIndex { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using LegendaryExplorerCore.Packages;
using LegendaryExplorerCore.UnrealScript.Analysis.Visitors;
using LegendaryExplorerCore.UnrealScript.Utilities;

Expand All @@ -13,17 +14,17 @@ public class VariableType : ASTNode, IHasFileReference

public EPropertyType PropertyType;

public virtual int Size => PropertyType switch
public virtual int Size(MEGame game) => PropertyType switch
{
EPropertyType.None => 0,
EPropertyType.Byte => 1,
EPropertyType.Int => 4,
EPropertyType.Bool => 4,
EPropertyType.Float => 4,
EPropertyType.Object => 4,
EPropertyType.Object => game.IsLEGame() ? 8 : 4,
EPropertyType.Name => 8,
EPropertyType.Delegate => 12,
EPropertyType.Interface => 8,
EPropertyType.Delegate => game.IsLEGame() ? 16 : 12,
EPropertyType.Interface => game.IsLEGame() ? 16 : 8,
EPropertyType.Struct => 0,
EPropertyType.Vector => 12,
EPropertyType.Rotator => 12,
Expand Down
3 changes: 1 addition & 2 deletions SharedProjects/Piccolo/Piccolo.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<!-- Since this project uses winforms, it cannot target .net standard-->
<TargetFrameworks>net48;net5.0-windows</TargetFrameworks>
<TargetFrameworks>net5.0-windows</TargetFrameworks>
<OutputType>Library</OutputType>
<LangVersion>9.0</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
Expand Down

0 comments on commit 1bd3358

Please sign in to comment.