From 07d75bce05ce132783c4b8ddcad80b6383e07bea Mon Sep 17 00:00:00 2001 From: Steve Halliwell Date: Tue, 12 Dec 2023 07:01:55 +1000 Subject: [PATCH 1/2] Change. Instances are now frozen pre init No longer can classes make new fields in init --- README.md | 2 +- ulox/ulox.core.bench/BenchmarkScripts.cs | 19 ++- ulox/ulox.core.tests/ClassTests.cs | 47 +++--- ulox/ulox.core.tests/FreezeTests.cs | 15 -- .../uloxs/Samples/06-Class Features.ulox | 5 +- ulox/ulox.core.tests/uloxs/Tests/Classes.ulox | 2 + .../Package/Runtime/Compiler/Compiler.cs | 4 +- .../Package/Runtime/Compiler/CompilerExt.cs | 12 +- .../Package/Runtime/Engine/Engine.cs | 2 +- .../Package/Runtime/Engine/Program.cs | 1 - ulox/ulox.core/Package/Runtime/Engine/VM.cs | 145 ++++++------------ .../Runtime/Library/Classes/DynamicClass.cs | 13 +- .../Runtime/Library/Classes/VMClass.cs | 7 +- .../Package/Runtime/Types/UserTypeInternal.cs | 14 +- 14 files changed, 107 insertions(+), 181 deletions(-) diff --git a/README.md b/README.md index 9ddbb1b1..78a77955 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ var var anAddress = Address(); anAddress.number = 7; -//classes are the most feature rich. +//classes are the feature rich. // The order of elements declared within the class is enforced class MyFoo { diff --git a/ulox/ulox.core.bench/BenchmarkScripts.cs b/ulox/ulox.core.bench/BenchmarkScripts.cs index 3143cb4b..97f95770 100644 --- a/ulox/ulox.core.bench/BenchmarkScripts.cs +++ b/ulox/ulox.core.bench/BenchmarkScripts.cs @@ -75,15 +75,13 @@ fun RandVec2() (x,y) class Ball { - init() - { - var (x,y) = RandVec2(); - this.posx = x; - this.posy = y; + var posx = 0; + var posy = 0; + var velx = 0; + var vely = 0; - var (vx,vy) = RandVec2(); - this.velx = vx; - this.vely = vy; + init(posx, posy, velx, vely) + { } } @@ -93,7 +91,9 @@ fun SetupGame() for(var i = 0; i < numBallsToSpawn; i += 1) { - balls.Add(Ball()); + var (x,y) = RandVec2(); + var (vx,vy) = RandVec2(); + balls.Add(Ball(x,y,vx,vy)); } print(balls.Count()); } @@ -106,7 +106,6 @@ fun Update() loop balls { - //print(GenerateStackDump()); item.posx = item.posx + item.velx * dt; item.posy = item.posy + item.vely * dt; diff --git a/ulox/ulox.core.tests/ClassTests.cs b/ulox/ulox.core.tests/ClassTests.cs index 7ce1cc3a..1f3365fe 100644 --- a/ulox/ulox.core.tests/ClassTests.cs +++ b/ulox/ulox.core.tests/ClassTests.cs @@ -276,9 +276,8 @@ public void Engine_Class_BoundMethod() { testEngine.Run(@" class CoffeeMaker { - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} brew() { print (""Enjoy your cup of "" + this.coffee); @@ -300,9 +299,8 @@ public void Engine_Class_BoundMethod_InternalAndReturn() { testEngine.Run(@" class CoffeeMaker { - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} brew() { print (""Enjoy your cup of "" + this.coffee); @@ -331,9 +329,8 @@ public void Engine_Class_BoundMethodWithParams_InternalAndReturn() { testEngine.Run(@" class CoffeeMaker { - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} brew(method) { print (""Enjoy your cup of "" + this.coffee + "" ("" + method + "")""); @@ -357,9 +354,8 @@ public void Engine_Class_BoundMethod_ViaReturn() { testEngine.Run(@" class CoffeeMaker { - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} brew() { print (""Enjoy your cup of "" + this.coffee); @@ -387,9 +383,8 @@ public void Engine_Class_BoundMethod_ViaReturn_InOtherObject() { testEngine.Run(@" class CoffeeMaker { - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} brew() { print (""Enjoy your cup of "" + this.coffee); @@ -427,9 +422,8 @@ public void FunctionInField_WhenAssignedAndCalled_ShouldReturnExpected() testEngine.Run(@" class CoffeeMaker { var brew; - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} } var maker = CoffeeMaker(""coffee and chicory""); @@ -449,9 +443,8 @@ public void Engine_Class_Init_Simple1() { testEngine.Run(@" class CoffeeMaker { - init(_coffee) { - this.coffee = _coffee; - } + var coffee; + init(coffee) {} brew() { print (""Enjoy your cup of "" + this.coffee); @@ -718,7 +711,7 @@ class V } [Test] - public void Init_WhenCreatingField_ShouldSucceed() + public void Init_AttempCreateField_ShouldFail() { testEngine.Run(@" class T @@ -729,7 +722,7 @@ class T var t = T(); print(t.a);"); - Assert.AreEqual("1", testEngine.InterpreterResult); + StringAssert.StartsWith("Attempted to create a new entry", testEngine.InterpreterResult); } [Test] @@ -738,7 +731,7 @@ public void Method_WhenAccessingSelfField_ShouldSucceed() testEngine.Run(@" class T { - init(){this.name = ""name"";} + var name = ""name""; Say(){print (this.name);} } var t = T(); @@ -753,7 +746,7 @@ public void Method_WhenAssigningExistingFieldFromArg_ShouldSucceed() testEngine.Run(@" class T { - init(){this.a = 1;} + var a = 1; Set(v) { this.a = v; @@ -773,7 +766,7 @@ public void Method_WhenAssigningExistingFieldFromConst_ShouldSucceed() testEngine.Run(@" class T { - init(){this.a = 1;} + var a = 1; Set() { this.a = 7; @@ -793,7 +786,7 @@ public void Method_WhenAssigningExistingFieldFromConstAndInternalPrint_ShouldSuc testEngine.Run(@" class T { - init(){this.a = 1;} + var a = 1; Set() { this.a = 7; diff --git a/ulox/ulox.core.tests/FreezeTests.cs b/ulox/ulox.core.tests/FreezeTests.cs index 059bdd9c..89476b36 100644 --- a/ulox/ulox.core.tests/FreezeTests.cs +++ b/ulox/ulox.core.tests/FreezeTests.cs @@ -34,21 +34,6 @@ class Foo StringAssert.StartsWith("Attempted to create a new ", testEngine.InterpreterResult); } - [Test] - public void InstanceFromClass_WhenHasInitAndNoVars_ShouldSucceed() - { - testEngine.Run(@" -class CoffeeMaker -{ - init(_a) { this.a = _a; } -} - -var maker = CoffeeMaker(""black""); -print(maker.a);"); - - Assert.AreEqual("black", testEngine.InterpreterResult); - } - [Test] public void Class_WhenFrozenAndNonExistingFieldWritten_ShouldPreventChangeAndLog() { diff --git a/ulox/ulox.core.tests/uloxs/Samples/06-Class Features.ulox b/ulox/ulox.core.tests/uloxs/Samples/06-Class Features.ulox index 3f76f7ee..b8fde368 100644 --- a/ulox/ulox.core.tests/uloxs/Samples/06-Class Features.ulox +++ b/ulox/ulox.core.tests/uloxs/Samples/06-Class Features.ulox @@ -1,15 +1,16 @@ // init is called when an instance is created class WithInitVal { + var someValue; init(val) { // this keyword gets access to the instance currently running the method - this.val = val; + this.someValue = val; } } var foo = WithInitVal(7); -print(foo.val); +print(foo.someValue); // class vars are initialised prior to init class WithVars diff --git a/ulox/ulox.core.tests/uloxs/Tests/Classes.ulox b/ulox/ulox.core.tests/uloxs/Tests/Classes.ulox index bbbaed8c..132c6b3b 100644 --- a/ulox/ulox.core.tests/uloxs/Tests/Classes.ulox +++ b/ulox/ulox.core.tests/uloxs/Tests/Classes.ulox @@ -8,6 +8,8 @@ class Method class WithInit { + var a,b; + init(a,b) { this.a = a; diff --git a/ulox/ulox.core/Package/Runtime/Compiler/Compiler.cs b/ulox/ulox.core/Package/Runtime/Compiler/Compiler.cs index 35445509..db6bd704 100644 --- a/ulox/ulox.core/Package/Runtime/Compiler/Compiler.cs +++ b/ulox/ulox.core/Package/Runtime/Compiler/Compiler.cs @@ -49,8 +49,8 @@ private void Setup() new EnumTypeCompliette() ); - this.AddDeclarationCompilette( - (TokenType.FUNCTION, FunctionDeclaration)); + + AddDeclarationCompilette(new CompiletteAction(TokenType.FUNCTION, FunctionDeclaration)); this.AddStatementCompilette( new ReturnStatementCompilette(), diff --git a/ulox/ulox.core/Package/Runtime/Compiler/CompilerExt.cs b/ulox/ulox.core/Package/Runtime/Compiler/CompilerExt.cs index 8fa44668..54ef6f49 100644 --- a/ulox/ulox.core/Package/Runtime/Compiler/CompilerExt.cs +++ b/ulox/ulox.core/Package/Runtime/Compiler/CompilerExt.cs @@ -2,11 +2,6 @@ { public static class CompilerExt { - public static void AddDeclarationCompilette(this Compiler comp, (TokenType match, System.Action action) processAction) - { - comp.AddDeclarationCompilette(new CompiletteAction(processAction.match, processAction.action)); - } - public static void AddDeclarationCompilette(this Compiler comp, params ICompilette[] compilettes) { foreach (var item in compilettes) @@ -15,16 +10,11 @@ public static void AddDeclarationCompilette(this Compiler comp, params ICompilet } } - public static void AddStatementCompilette(this Compiler comp, (TokenType match, System.Action action) processAction) - { - comp.AddStatementCompilette(new CompiletteAction(processAction.match, processAction.action)); - } - public static void AddStatementCompilette(this Compiler comp, params (TokenType match, System.Action action)[] processActions) { foreach (var item in processActions) { - comp.AddStatementCompilette(item); + comp.AddStatementCompilette(new CompiletteAction(item.match, item.action)); } } diff --git a/ulox/ulox.core/Package/Runtime/Engine/Engine.cs b/ulox/ulox.core/Package/Runtime/Engine/Engine.cs index 816fb139..50735065 100644 --- a/ulox/ulox.core/Package/Runtime/Engine/Engine.cs +++ b/ulox/ulox.core/Package/Runtime/Engine/Engine.cs @@ -10,7 +10,7 @@ public sealed class Engine public Engine(Context executionContext) { Context = executionContext; - Context.Vm.SetEngine(this); + Context.Vm.Engine = this; Context.AddLibrary(new StdLibrary()); } diff --git a/ulox/ulox.core/Package/Runtime/Engine/Program.cs b/ulox/ulox.core/Package/Runtime/Engine/Program.cs index b06d7170..17affe4c 100644 --- a/ulox/ulox.core/Package/Runtime/Engine/Program.cs +++ b/ulox/ulox.core/Package/Runtime/Engine/Program.cs @@ -12,7 +12,6 @@ public sealed class Program public List CompiledScripts { get; } = new List(); - public string Disassembly { get diff --git a/ulox/ulox.core/Package/Runtime/Engine/VM.cs b/ulox/ulox.core/Package/Runtime/Engine/VM.cs index a4875f7b..fe467340 100644 --- a/ulox/ulox.core/Package/Runtime/Engine/VM.cs +++ b/ulox/ulox.core/Package/Runtime/Engine/VM.cs @@ -15,7 +15,7 @@ public sealed class Vm internal FastStack CallFrames => _callFrames; private CallFrame _currentCallFrame; private Chunk _currentChunk; - public Engine Engine { get; private set; } + public Engine Engine { get; internal set; } private readonly LinkedList openUpvalues = new LinkedList(); public Table Globals { get; private set; } = new Table(); public TestRunner TestRunner { get; private set; } = new TestRunner(() => new Vm()); @@ -56,12 +56,6 @@ public Value GetArg(int index) public Value GetNextArg(ref int index) => _valueStack[_currentCallFrame.StackStart + (++index)]; - public int CurrentFrameStackValues => _valueStack.Count - _currentCallFrame.StackStart; - public Value StackTop => _valueStack.Peek(); - public int StackCount => _valueStack.Count; - - public void SetEngine(Engine engine) => Engine = engine; - public InterpreterResult PushCallFrameAndRun(Value func, byte args) { PushCallFrameFromValue(func, args); @@ -113,7 +107,7 @@ public void CopyStackFrom(Vm vm) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ByteCodePacket ReadPacket(Chunk chunk) + private ByteCodePacket ReadPacket(Chunk chunk) => chunk.Instructions[_currentCallFrame.InstructionPointer++]; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -134,27 +128,6 @@ private void PushNewCallframe(CallFrame callFrame) } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte PopCallFrame() - { - var poppedStackStart = _currentCallFrame.StackStart; - //remove top - _callFrames.Pop(); - - //update cache - if (_callFrames.Count > 0) - { - _currentCallFrame = _callFrames.Peek(); - _currentChunk = _currentCallFrame.Closure.chunk; - } - else - { - _currentCallFrame = default; - _currentChunk = default; - } - return poppedStackStart; - } - public InterpreterResult Interpret(Chunk chunk) { //push this empty string to match the expectation of the function compiler @@ -911,7 +884,7 @@ private void DoNativeCall(OpCode opCode) { if (_currentCallFrame.nativeFunc == null) ThrowRuntimeException($"{opCode} without nativeFunc encountered. This is not allowed"); - var argCount = CurrentFrameStackValues; + var argCount = _valueStack.Count - _currentCallFrame.StackStart; var res = _currentCallFrame.nativeFunc.Invoke(this); if (res == NativeCallResult.SuccessfulExpression) @@ -940,62 +913,59 @@ private void ProcessReturns() { _returnStack.Push(ValueStack[_currentCallFrame.StackStart]); } - FinishReturnOp(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DoMultiVarOp(Chunk chunk, bool start) - { - if (start) - _currentCallFrame.MultiAssignStart = (byte)StackCount; - else - CacheStackForMultiAssignValidation(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FinishReturnOp() - { CloseUpvalues(_currentCallFrame.StackStart); PopFrameAndDiscard(); - TransferReturnToStack(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void PopFrameAndDiscard() - { - var prevStackStart = PopCallFrame(); - DiscardPopToCount(prevStackStart); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DiscardPopToCount(byte prevStackStart) - { - var toRemove = System.Math.Max(0, _valueStack.Count - prevStackStart); - - DiscardPop(toRemove); + //transform from return stack to value stack + for (int i = 0; i < _returnStack.Count; i++) + { + Push(_returnStack[i]); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void TransferReturnToStack() + private void DoMultiVarOp(Chunk chunk, bool start) { - for (int i = 0; i < _returnStack.Count; i++) + if (start) + _currentCallFrame.MultiAssignStart = (byte)_valueStack.Count; + else { - Push(_returnStack[i]); + //this is only so the multi return validate mechanism can continue to function, + //it's not actually contributing to how multi return works. + _returnStack.Reset(); + var returnCount = _valueStack.Count - _currentCallFrame.MultiAssignStart; + for (int i = 0; i < returnCount; i++) + { + _returnStack.Push(Value.Null()); + } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CacheStackForMultiAssignValidation() + private void PopFrameAndDiscard() { - //this is only so the multi return validate mechanism can continue to function, - //it's not actually contributing to how multi return works. - _returnStack.Reset(); - var returnCount = _valueStack.Count - _currentCallFrame.MultiAssignStart; - for (int i = 0; i < returnCount; i++) + var poppedStackStart = _currentCallFrame.StackStart; + //remove top + _callFrames.Pop(); + + //update cache + if (_callFrames.Count > 0) + { + _currentCallFrame = _callFrames.Peek(); + _currentChunk = _currentCallFrame.Closure.chunk; + } + else { - _returnStack.Push(Value.Null()); + _currentCallFrame = default; + _currentChunk = default; } + + var prevStackStart = poppedStackStart; + var toRemove = System.Math.Max(0, _valueStack.Count - prevStackStart); + + DiscardPop(toRemove); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1296,28 +1266,19 @@ private void CreateInstance(UserTypeInternal asClass, byte argCount) { var instInternal = asClass.MakeInstance(); var inst = Value.New(instInternal); - _valueStack[_valueStack.Count - 1 - argCount] = inst; - - InitNewInstance(asClass, argCount, inst); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void InitNewInstance(UserTypeInternal klass, byte argCount, Value inst) - { var stackCount = _valueStack.Count; var instLocOnStack = (byte)(stackCount - argCount - 1); + _valueStack[instLocOnStack] = inst; - DuplicateStackValuesNew(instLocOnStack, argCount); - PushFrameCallNativeWithFixedStackStart(ClassFinishCreation, instLocOnStack, argCount, 1); - - if (!klass.Initialiser.IsNull()) + //InitNewInstance + if (!asClass.Initialiser.IsNull()) { //with an init list we don't return this - PushCallFrameFromValue(klass.Initialiser, argCount); + PushCallFrameFromValue(asClass.Initialiser, argCount); //push a native call here so we can bind the fields to init param names - if (klass.Initialiser.type == ValueType.Closure && - klass.Initialiser.val.asClosure.chunk.Arity > 0) + if (asClass.Initialiser.type == ValueType.Closure && + asClass.Initialiser.val.asClosure.chunk.Arity > 0) { DuplicateStackValuesNew(instLocOnStack, argCount); PushFrameCallNative(CopyMatchingParamsToFields, argCount, 1); @@ -1325,12 +1286,12 @@ private void InitNewInstance(UserTypeInternal klass, byte argCount, Value inst) } else if (argCount != 0) { - ThrowRuntimeException($"Expected zero args for class '{klass}', as it does not have an 'init' method but got {argCount} args"); + ThrowRuntimeException($"Expected zero args for class '{asClass}', as it does not have an 'init' method but got {argCount} args"); } - foreach (var (chunk, labelID) in klass.InitChains) + foreach (var (chunk, labelID) in asClass.InitChains) { - if (!klass.Initialiser.IsNull()) + if (!asClass.Initialiser.IsNull()) Push(inst); PushNewCallframe(new CallFrame() @@ -1371,16 +1332,6 @@ private NativeCallResult CopyMatchingParamsToFields(Vm vm) return NativeCallResult.SuccessfulStatement; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private NativeCallResult ClassFinishCreation(Vm vm) - { - var instVal = vm.GetArg(0); - var inst = instVal.val.asInstance; - inst.FromUserType.FinishCreation(inst); - vm.SetNativeReturn(0, instVal); - return NativeCallResult.SuccessfulExpression; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool DoCustomOverloadOp(OpCode opCode, Value self, Value arg1, Value arg2) { diff --git a/ulox/ulox.core/Package/Runtime/Library/Classes/DynamicClass.cs b/ulox/ulox.core/Package/Runtime/Library/Classes/DynamicClass.cs index 231f0f34..f6bd117f 100644 --- a/ulox/ulox.core/Package/Runtime/Library/Classes/DynamicClass.cs +++ b/ulox/ulox.core/Package/Runtime/Library/Classes/DynamicClass.cs @@ -13,6 +13,13 @@ public DynamicClass() : base(DynamicClassName, UserType.Native) ); } + public override InstanceInternal MakeInstance() + { + var res = base.MakeInstance(); + res.Unfreeze(); + return res; + } + private NativeCallResult HasField(Vm vm) { var obj = vm.GetArg(1); @@ -44,11 +51,5 @@ private NativeCallResult RemoveField(Vm vm) return NativeCallResult.SuccessfulExpression; } - - public override void FinishCreation(InstanceInternal inst) - { - base.FinishCreation(inst); - inst.Unfreeze(); - } } } diff --git a/ulox/ulox.core/Package/Runtime/Library/Classes/VMClass.cs b/ulox/ulox.core/Package/Runtime/Library/Classes/VMClass.cs index f0dd4e65..68c33275 100644 --- a/ulox/ulox.core/Package/Runtime/Library/Classes/VMClass.cs +++ b/ulox/ulox.core/Package/Runtime/Library/Classes/VMClass.cs @@ -11,7 +11,7 @@ public sealed class VMClass : UserTypeInternal public VMClass(Func createVM) : base(new HashedString("VM"), UserType.Native) { CreateVM = createVM; - this.AddMethod(ClassTypeCompilette.InitMethodName, Value.New(InitInstance, 1, 0), null); + AddMethod(ClassTypeCompilette.InitMethodName, Value.New(InitInstance, 1, 0), null); this.AddMethodsToClass( (nameof(AddGlobal), Value.New(AddGlobal, 1, 2)), (nameof(GetGlobal), Value.New(GetGlobal, 1, 1)), @@ -20,6 +20,7 @@ public VMClass(Func createVM) : base(new HashedString("VM"), UserType.Native (nameof(CopyBackToEnclosing), Value.New(CopyBackToEnclosing, 1, 0)), (nameof(Resume), Value.New(Resume, 1, 0)) ); + AddFieldName(VMFieldName); } private NativeCallResult InitInstance(Vm vm) @@ -67,7 +68,7 @@ private NativeCallResult Start(Vm vm) Vm ourVM = GetArg0Vm(vm); var chunk = vm.GetArg(1).val.asClosure.chunk; ourVM.Interpret(chunk); - vm.SetNativeReturn(0, ourVM.StackTop); + vm.SetNativeReturn(0, ourVM.ValueStack.Peek()); return NativeCallResult.SuccessfulExpression; } @@ -75,7 +76,7 @@ private NativeCallResult Resume(Vm vm) { Vm ourVM = GetArg0Vm(vm); ourVM.Run(); - vm.SetNativeReturn(0, ourVM.StackTop); + vm.SetNativeReturn(0, ourVM.ValueStack.Peek()); return NativeCallResult.SuccessfulExpression; } diff --git a/ulox/ulox.core/Package/Runtime/Types/UserTypeInternal.cs b/ulox/ulox.core/Package/Runtime/Types/UserTypeInternal.cs index de61ddd6..e3fe4d6c 100644 --- a/ulox/ulox.core/Package/Runtime/Types/UserTypeInternal.cs +++ b/ulox/ulox.core/Package/Runtime/Types/UserTypeInternal.cs @@ -93,7 +93,15 @@ public void PrepareFromType(Vm vm) [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual InstanceInternal MakeInstance() { - return new InstanceInternal(this); + var ret = new InstanceInternal(this); + + foreach (var fieldName in _fieldsNames) + { + ret.SetField(fieldName, Value.Null()); + } + + ret.Freeze(); + return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -156,10 +164,6 @@ public Value GetOverloadClosure(OpCode opCode) return overloadableOperators[OpCodeToOverloadIndex[opCode]]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public virtual void FinishCreation(InstanceInternal inst) - => inst.Freeze(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddFieldName(HashedString fieldName) => _fieldsNames.Add(fieldName); From 68b38b702798d7d90223a4ab0101fe54c2c4f606 Mon Sep 17 00:00:00 2001 From: Steve Halliwell Date: Tue, 12 Dec 2023 20:00:06 +1000 Subject: [PATCH 2/2] Init arg matching now performed during init compile not as special VM logic --- ulox/ulox.core.tests/NativeCallTests.cs | 6 +- .../Compilettes/ClassTypeCompilette.cs | 16 ++++- ulox/ulox.core/Package/Runtime/Engine/VM.cs | 64 +++---------------- 3 files changed, 27 insertions(+), 59 deletions(-) diff --git a/ulox/ulox.core.tests/NativeCallTests.cs b/ulox/ulox.core.tests/NativeCallTests.cs index 1facac50..3db8eb64 100644 --- a/ulox/ulox.core.tests/NativeCallTests.cs +++ b/ulox/ulox.core.tests/NativeCallTests.cs @@ -172,9 +172,9 @@ public void Run_WhenNativeFuncWithMultipleArgs_ShouldReceiveAllInOrder() NativeCallResult Func(Vm vm) { var index = 0; - var a = vm.GetNextArg(ref index).val.asString; - var b = vm.GetNextArg(ref index).val.asString; - var c = vm.GetNextArg(ref index).val.asString; + var a = vm.GetArg(++index).val.asString; + var b = vm.GetArg(++index).val.asString; + var c = vm.GetArg(++index).val.asString; vm.SetNativeReturn(0, Value.New($"Hello, {a}, {b}, and {c}, I'm native.")); return NativeCallResult.SuccessfulExpression; } diff --git a/ulox/ulox.core/Package/Runtime/Compiler/Compilettes/ClassTypeCompilette.cs b/ulox/ulox.core/Package/Runtime/Compiler/Compilettes/ClassTypeCompilette.cs index b269b876..029009e5 100644 --- a/ulox/ulox.core/Package/Runtime/Compiler/Compilettes/ClassTypeCompilette.cs +++ b/ulox/ulox.core/Package/Runtime/Compiler/Compilettes/ClassTypeCompilette.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace ULox { @@ -24,8 +25,8 @@ public ClassTypeCompilette() _innerDeclarationCompilettes = new Dictionary)>() { { TokenType.STATIC, (TypeCompiletteStage.Static, StaticElement) }, - { TokenType.INIT, (TypeCompiletteStage.Init, c => CompileMethod(c, FunctionType.Init)) }, { TokenType.VAR, (TypeCompiletteStage.Var, Property) }, + { TokenType.INIT, (TypeCompiletteStage.Init, c => CompileMethod(c, FunctionType.Init)) }, { TokenType.MIXIN, (TypeCompiletteStage.Mixin, Mixin) }, { TokenType.SIGNS, (TypeCompiletteStage.Signs, Signs) }, }; @@ -87,6 +88,19 @@ private void CompileMethod(Compiler compiler, FunctionType functionType) if (functionType == FunctionType.Init) { + foreach (var argId in compiler.CurrentCompilerState.chunk.ArgumentConstantIds) + { + var argName = compiler.CurrentCompilerState.chunk.ReadConstant(argId).val.asString.String; + if (CurrentTypeInfoEntry.Fields.FirstOrDefault(x => x == argName) != null) + { + var (_1,_2, id) = compiler.ResolveNameLookupOpCode(argName); + compiler.EmitPacket(new ByteCodePacket(OpCode.GET_LOCAL, (byte)0)); + compiler.EmitPacket(new ByteCodePacket(OpCode.GET_LOCAL, (byte)id)); + compiler.EmitPacket(new ByteCodePacket(OpCode.SET_PROPERTY, (byte)argId)); + compiler.EmitPop(); + } + } + if (returnCount != 0) compiler.ThrowCompilerException("Init functions cannot specify named return vars."); } diff --git a/ulox/ulox.core/Package/Runtime/Engine/VM.cs b/ulox/ulox.core/Package/Runtime/Engine/VM.cs index fe467340..624ba762 100644 --- a/ulox/ulox.core/Package/Runtime/Engine/VM.cs +++ b/ulox/ulox.core/Package/Runtime/Engine/VM.cs @@ -36,7 +36,7 @@ public Vm() [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Value, Value) Pop2() { -#if FASTSTACK_FORCE_CLEAR +#if !FASTSTACK_FORCE_CLEAR _valueStack.DiscardPop(2); return (_valueStack.Peek(-2), _valueStack.Peek(-1)); #else @@ -53,9 +53,6 @@ public Vm() public Value GetArg(int index) => _valueStack[_currentCallFrame.StackStart + index]; - public Value GetNextArg(ref int index) - => _valueStack[_currentCallFrame.StackStart + (++index)]; - public InterpreterResult PushCallFrameAndRun(Value func, byte args) { PushCallFrameFromValue(func, args); @@ -167,7 +164,7 @@ public InterpreterResult Run() break; case OpCode.RETURN: - if (DoReturnOp(chunk)) + if (DoReturnOp()) return InterpreterResult.OK; break; @@ -425,7 +422,7 @@ public InterpreterResult Run() VmUtil.GenerateCallStackDump(this)); } case OpCode.BUILD: - DoBuildOp(chunk); + DoBuildOp(); break; case OpCode.NATIVE_CALL: @@ -433,7 +430,7 @@ public InterpreterResult Run() break; case OpCode.VALIDATE: - DoValidateOp(chunk, packet.ValidateOp); + DoValidateOp(packet.ValidateOp); break; case OpCode.GET_PROPERTY: @@ -777,7 +774,7 @@ private void DoGetUpvalueOp(Chunk chunk, byte slot) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DoValidateOp(Chunk chunk, ValidateOp validateOp) + private void DoValidateOp(ValidateOp validateOp) { switch (validateOp) { @@ -810,7 +807,7 @@ private void DoValidateOp(Chunk chunk, ValidateOp validateOp) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DoBuildOp(Chunk chunk) + private void DoBuildOp() { var givenVar = Pop(); var str = givenVar.str(); @@ -835,7 +832,7 @@ private void DoClosureOp(Chunk chunk, ByteCodePacket.ClosureDetails closureDetai var type = closureDetails.ClosureType; var b1 = closureDetails.b1; var b2 = closureDetails.b2; - ClosureInternal closure = default; + var closure = default(ClosureInternal); if (type != ClosureType.Closure) ThrowRuntimeException($"Closure type '{type}' unexpected."); @@ -868,7 +865,7 @@ private void DoClosureOp(Chunk chunk, ByteCodePacket.ClosureDetails closureDetai } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool DoReturnOp(Chunk chunk) + private bool DoReturnOp() { var origCallFrameCount = _callFrames.Count; var wantsToYieldOnReturn = _currentCallFrame.YieldOnReturn; @@ -1076,16 +1073,10 @@ private void Call(ClosureInternal closureInternal, byte argCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] private void PushFrameCallNative(CallFrame.NativeCallDelegate nativeCallDel, byte argCount, byte returnCount) - { - PushFrameCallNativeWithFixedStackStart(nativeCallDel, (byte)(_valueStack.Count - argCount - 1), argCount, returnCount); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void PushFrameCallNativeWithFixedStackStart(CallFrame.NativeCallDelegate nativeCallDel, byte stackStart, byte argCount, byte returnCount) { PushNewCallframe(new CallFrame() { - StackStart = stackStart, + StackStart = (byte)(_valueStack.Count - argCount - 1), Closure = NativeCallClosure, nativeFunc = nativeCallDel, InstructionPointer = 0, @@ -1275,14 +1266,6 @@ private void CreateInstance(UserTypeInternal asClass, byte argCount) { //with an init list we don't return this PushCallFrameFromValue(asClass.Initialiser, argCount); - - //push a native call here so we can bind the fields to init param names - if (asClass.Initialiser.type == ValueType.Closure && - asClass.Initialiser.val.asClosure.chunk.Arity > 0) - { - DuplicateStackValuesNew(instLocOnStack, argCount); - PushFrameCallNative(CopyMatchingParamsToFields, argCount, 1); - } } else if (argCount != 0) { @@ -1303,35 +1286,6 @@ private void CreateInstance(UserTypeInternal asClass, byte argCount) } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private NativeCallResult CopyMatchingParamsToFields(Vm vm) - { - var instVal = vm.GetArg(0); - - var inst = instVal.val.asInstance; - - var initChunk = inst.FromUserType.Initialiser.val.asClosure.chunk; - var argConstantIds = initChunk.ArgumentConstantIds; - - const int argOffset = 1; - - for (int i = 0; i < argConstantIds.Count; i++) - { - var arg = initChunk.Constants[i]; - if (arg.type == ValueType.String) - { - var paramName = arg.val.asString; - if (inst.Fields.Contains(paramName)) - { - var value = vm.GetArg(i + argOffset); - inst.SetField(paramName, value); - } - } - } - - return NativeCallResult.SuccessfulStatement; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool DoCustomOverloadOp(OpCode opCode, Value self, Value arg1, Value arg2) {