Skip to content

Commit 65a40a4

Browse files
authored
Merge pull request #55 from WattleScript/feat-private-fields
Feature: Private Fields
2 parents f70c24d + 44dd543 commit 65a40a4

35 files changed

+450
-123
lines changed

src/WattleScript.Interpreter/DataTypes/MemberModifierFlags.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace WattleScript.Interpreter
66
public enum MemberModifierFlags
77
{
88
None = 0,
9-
Static = 1
9+
Static = (1 << 0),
10+
Private = (1 << 1),
11+
Public = (1 << 2)
1012
}
1113
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Collections.Generic;
2+
3+
namespace WattleScript.Interpreter
4+
{
5+
public sealed class PrivateKeyInfo
6+
{
7+
internal HashSet<string> Fields = new HashSet<string>();
8+
9+
public bool IsKeyPrivate(DynValue key)
10+
{
11+
if (key.Type != DataType.String) return false;
12+
return Fields.Contains(key.String);
13+
}
14+
15+
internal void Merge(PrivateKeyInfo parent)
16+
{
17+
Fields.UnionWith(parent.Fields);
18+
}
19+
20+
internal PrivateKeyInfo() { }
21+
}
22+
}

src/WattleScript.Interpreter/DataTypes/SymbolRef.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public class SymbolRef
4141
/// </summary>
4242
public bool IsBaseClass { get; set; }
4343

44+
/// <summary>
45+
/// Sets whether or not this symbol is the `this` argument of a function
46+
/// </summary>
47+
public bool IsThisArgument { get; set; }
48+
4449
/// <summary>
4550
/// Set to true if this symbol is a placeholder (no allocation)
4651
/// </summary>

src/WattleScript.Interpreter/DataTypes/Table.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public Table(Script owner, params DynValue[] arrayValues)
6969
/// This is only for metadata purposes, and does not affect execution.
7070
/// </summary>
7171
public MemberModifierFlags ModifierFlags { get; set; }
72+
73+
/// <summary>
74+
/// Holds information relating to private keys (if present)
75+
/// </summary>
76+
public PrivateKeyInfo PrivateKeys { get; set; }
7277

7378
/// <summary>
7479
/// Removes all items from the Table.

src/WattleScript.Interpreter/Execution/InstructionFieldUsage.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
3838
case OpCode.PushNil:
3939
case OpCode.PushTrue:
4040
case OpCode.PushFalse:
41+
case OpCode.CopyPriv:
4142
return InstructionFieldUsage.None;
4243
case OpCode.Pop:
4344
case OpCode.Copy:
@@ -52,6 +53,7 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
5253
case OpCode.Less:
5354
case OpCode.Eq:
5455
case OpCode.CNot:
56+
case OpCode.SetPriv:
5557
return InstructionFieldUsage.NumVal;
5658
case OpCode.Jump:
5759
case OpCode.Jf:
@@ -68,6 +70,7 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
6870
case OpCode.JLclInit:
6971
case OpCode.Args:
7072
case OpCode.TblInitN:
73+
case OpCode.MergePriv:
7174
return InstructionFieldUsage.NumVal | InstructionFieldUsage.NumVal2;
7275
case OpCode.Local:
7376
case OpCode.Upvalue:
@@ -80,11 +83,10 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
8083
case OpCode.NewRange:
8184
case OpCode.TblInitI:
8285
case OpCode.TabProps:
83-
return InstructionFieldUsage.NumVal | InstructionFieldUsage.NumVal2 | InstructionFieldUsage.NumVal3;
8486
case OpCode.Index:
8587
case OpCode.IndexL:
8688
case OpCode.IndexN:
87-
return InstructionFieldUsage.NumVal; //string argument
89+
return InstructionFieldUsage.NumVal | InstructionFieldUsage.NumVal2 | InstructionFieldUsage.NumVal3;
8890
case OpCode.Closure:
8991
return InstructionFieldUsage.NumVal;
9092
case OpCode.Nop:

src/WattleScript.Interpreter/Execution/Scopes/BuildTimeScope.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ private SymbolRef CreateUpValue(BuildTimeScope buildTimeScope, SymbolRef symb, i
9090
if (closuredFrame == currentFrame) {
9191
var uv = m_ClosureBuilders[currentFrame + 1].CreateUpvalue(this, symb);
9292
uv.IsBaseClass = symb.IsBaseClass;
93+
uv.IsThisArgument = symb.IsThisArgument;
9394
uv.Placeholder = symb.Placeholder;
9495
return uv;
9596
}
@@ -99,6 +100,7 @@ private SymbolRef CreateUpValue(BuildTimeScope buildTimeScope, SymbolRef symb, i
99100
SymbolRef upvalue = CreateUpValue(buildTimeScope, symb, closuredFrame, currentFrame - 1);
100101
var uv = m_ClosureBuilders[currentFrame + 1].CreateUpvalue(this, upvalue);
101102
uv.IsBaseClass = symb.IsBaseClass;
103+
uv.IsThisArgument = symb.IsThisArgument;
102104
uv.Placeholder = symb.Placeholder;
103105
return uv;
104106
}
@@ -115,6 +117,13 @@ public SymbolRef DefineBaseRef()
115117
retVal.IsBaseClass = true;
116118
return retVal;
117119
}
120+
121+
public SymbolRef DefineThisArg(string name)
122+
{
123+
var retVal = DefineLocal(name);
124+
retVal.IsThisArgument = true;
125+
return retVal;
126+
}
118127

119128
//Defines a placeholder symbol for base that will error if used
120129
public SymbolRef DefineBaseEmpty()

src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,23 @@ public int Emit_Invalid(string type)
199199

200200
public int Emit_TabProps(TableKind kind, MemberModifierFlags flags, bool isReadOnly)
201201
{
202-
return AppendInstruction(new Instruction(OpCode.TabProps, (int) kind, (int)flags, isReadOnly ? (uint)1 : 0));
202+
return AppendInstruction(new Instruction(OpCode.TabProps, (int) flags, (int)kind, isReadOnly ? (uint)1 : 0));
203203
}
204204

205+
public int Emit_SetPriv(int num)
206+
{
207+
return AppendInstruction(new Instruction(OpCode.SetPriv, num));
208+
}
209+
210+
public int Emit_MergePriv(int src, int dst)
211+
{
212+
return AppendInstruction(new Instruction(OpCode.MergePriv, src, dst));
213+
}
214+
215+
public int Emit_CopyPriv()
216+
{
217+
return AppendInstruction(new Instruction(OpCode.CopyPriv));
218+
}
205219

206220
public int Emit_Pop(int num = 1)
207221
{
@@ -582,18 +596,20 @@ public int Emit_TblInitI(bool lastpos, int count, int create)
582596
return AppendInstruction(new Instruction(OpCode.TblInitI, count, lastpos ? 1 : 0, (uint)create));
583597
}
584598

585-
public int Emit_Index(string index = null, bool isNameIndex = false, bool isExpList = false, bool isMethodCall = false)
599+
public int Emit_Index(string index = null, bool isNameIndex = false, bool isExpList = false, bool isMethodCall = false, bool accessPrivate = false)
586600
{
587601
OpCode o;
588602
if (isNameIndex) o = OpCode.IndexN;
589603
else if (isExpList) o = OpCode.IndexL;
590604
else o = OpCode.Index;
591-
return AppendInstruction(new Instruction(o, StringArg(index), isMethodCall ? 1 : 0));
605+
return AppendInstruction(new Instruction(o, StringArg(index), isMethodCall ? 1 : 0, accessPrivate ? 1U : 0));
592606
}
593607

594-
public int Emit_IndexSet(int stackofs, int tupleidx, string index = null, bool isNameIndex = false, bool isExpList = false)
608+
public int Emit_IndexSet(int stackofs, int tupleidx, string index = null, bool isNameIndex = false, bool isExpList = false, bool accessPrivate = false)
595609
{
596610
OpCode o;
611+
if (stackofs >= 0x40000000) throw new InternalErrorException("stackofs >= 0x40000000");
612+
if (accessPrivate) stackofs |= 0x40000000;
597613
if (isNameIndex) o = OpCode.IndexSetN;
598614
else if (isExpList) o = OpCode.IndexSetL;
599615
else o = OpCode.IndexSet;

src/WattleScript.Interpreter/Execution/VM/Instruction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public uint NumValB
3232
get => (uint) (_data >> 39); //25 bits unsigned
3333
set
3434
{
35-
if (value > 0x2000000) throw new ArgumentOutOfRangeException("NumValB");
35+
if (value > 0x1FFFFFF) throw new ArgumentOutOfRangeException("NumValB");
3636
_data = (_data & ~0xFFFFFF8000000000) |
3737
((ulong) value) << 39;
3838
}

src/WattleScript.Interpreter/Execution/VM/OpCode.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal enum OpCode
2222
TblInitN, // Initializes NumVal named entries, NumVal2 0 = don't create, 1 = create normal, 2 = create shared
2323
TblInitI, // Initializes NumVal table positional entries, NumVal3 0 = don't create, 1 = create normal, 2 = create shared
2424
NewRange, // Creates a range from the v-stack
25-
TabProps, // Sets v-stack top table kind and readonly flag. Does not pop
25+
TabProps, // Sets v-stack top table modifier flags, kind and readonly flag. Does not pop
2626
SetMetaTab, // Sets v-stack - 1 table's metatable to vstack top & pops once.
2727

2828
StoreLcl, Local,
@@ -127,6 +127,9 @@ internal enum OpCode
127127
//Throws error using class name in NumValB if type check fails
128128
MixInit, //Checks type of mixin on v-stack top, stores init to v-stack + 1, adds functions to v-stack + 2, pops top
129129
//Error check uses NumVal for mixin name
130+
SetPriv, //Sets NumVal keys to private (pops NumVal items)
131+
MergePriv, //Merges the PrivateKeyInfo of v-stack(NumVal) into v-stack(NumVal2)
132+
CopyPriv, //Copies the PrivateKeyInfo of v-stack top into v-stack +1, pops 1 value
130133
// Meta
131134
Invalid, // Crashes the executor with an unrecoverable NotImplementedException. This MUST always be the last opcode in enum
132135
}

src/WattleScript.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,9 @@ private DynValue Processing_Loop(int instructionPtr, bool canAwait = false)
275275
{
276276
ref var top = ref m_ValueStack.Peek();
277277
if (top.Type != DataType.Table) throw new InternalErrorException("v-stack top NOT table");
278-
top.Table.Kind = (TableKind)i.NumVal;
278+
top.Table.Kind = (TableKind)i.NumVal2;
279279
top.Table.ReadOnly = i.NumVal3 != 0;
280-
top.Table.ModifierFlags = (MemberModifierFlags)i.NumVal2;
280+
top.Table.ModifierFlags = (MemberModifierFlags)i.NumVal;
281281
break;
282282
}
283283
case OpCode.SetMetaTab:
@@ -387,6 +387,15 @@ private DynValue Processing_Loop(int instructionPtr, bool canAwait = false)
387387
case OpCode.NewRange:
388388
ExecNewRange(i);
389389
break;
390+
case OpCode.SetPriv:
391+
ExecSetPriv(i);
392+
break;
393+
case OpCode.CopyPriv:
394+
ExecCopyPriv(i);
395+
break;
396+
case OpCode.MergePriv:
397+
ExecMergePriv(i);
398+
break;
390399
case OpCode.ToNum:
391400
{
392401
ref var top = ref m_ValueStack.Peek();
@@ -618,6 +627,40 @@ private void ExecExpTuple(Instruction i)
618627

619628
}
620629

630+
private void ExecSetPriv(Instruction i)
631+
{
632+
ref DynValue t = ref m_ValueStack.Peek(i.NumVal);
633+
if (t.Type != DataType.Table)
634+
throw new InternalErrorException("SETPRIV called on non-table object");
635+
t.Table.PrivateKeys = new PrivateKeyInfo();
636+
while (i.NumVal-- > 0) {
637+
t.Table.PrivateKeys.Fields.Add(m_ValueStack.Pop().CastToString());
638+
}
639+
}
640+
641+
private void ExecCopyPriv(Instruction i)
642+
{
643+
ref DynValue dest = ref m_ValueStack.Peek(1);
644+
ref DynValue src = ref m_ValueStack.Peek(0);
645+
if (dest.Type != DataType.Table || src.Type != DataType.Table)
646+
throw new InternalErrorException("COPYPRIV called on non-table object");
647+
dest.Table.PrivateKeys = src.Table.PrivateKeys;
648+
m_ValueStack.Pop();
649+
}
650+
651+
private void ExecMergePriv(Instruction i)
652+
{
653+
ref DynValue dest = ref m_ValueStack.Peek(i.NumVal2);
654+
ref DynValue src = ref m_ValueStack.Peek(i.NumVal);
655+
if (dest.Type != DataType.Table || src.Type != DataType.Table)
656+
throw new InternalErrorException("MERGEPRIV called on non-table object");
657+
if (src.Table.PrivateKeys != null)
658+
{
659+
dest.Table.PrivateKeys ??= new PrivateKeyInfo();
660+
dest.Table.PrivateKeys.Merge(src.Table.PrivateKeys);
661+
}
662+
}
663+
621664
private void ExecIterPrep()
622665
{
623666
DynValue v = m_ValueStack.Pop();
@@ -1690,7 +1733,9 @@ private int ExecIndexSet(Instruction i, int instructionPtr)
16901733
// stack: vals.. - base - index
16911734
bool isNameIndex = i.OpCode == OpCode.IndexSetN;
16921735
bool isMultiIndex = (i.OpCode == OpCode.IndexSetL);
1693-
1736+
// extract privateAccess bool from spare bit in numVal
1737+
bool accessPrivate = (i.NumVal & 0x40000000) != 0;
1738+
i.NumVal &= 0x3FFFFFFF;
16941739
string i_str = GetString((int)i.NumVal3);
16951740
DynValue originalIdx = i_str != null ? DynValue.NewString(i_str) : m_ValueStack.Pop();
16961741
DynValue idx = originalIdx.ToScalar();
@@ -1710,6 +1755,12 @@ private int ExecIndexSet(Instruction i, int instructionPtr)
17101755

17111756
if (!isMultiIndex)
17121757
{
1758+
//Private fields - error on write
1759+
if (!accessPrivate && obj.Table.PrivateKeys != null &&
1760+
obj.Table.PrivateKeys.IsKeyPrivate(idx))
1761+
{
1762+
throw new ScriptRuntimeException($"cannot write to private key '{idx.ToPrintString()}'");
1763+
}
17131764
//Don't do check for __newindex if there is no metatable to begin with
17141765
if (obj.Table.MetaTable == null || !obj.Table.Get(idx).IsNil())
17151766
{
@@ -1796,6 +1847,7 @@ private int ExecIndex(Instruction i, int instructionPtr)
17961847
// stack: base - index
17971848
bool isNameIndex = i.OpCode == OpCode.IndexN;
17981849
bool isMultiIndex = i.OpCode == OpCode.IndexL;
1850+
bool accessPrivate = i.NumVal3 > 0;
17991851

18001852
string i_str = GetString(i.NumVal);
18011853
DynValue originalIdx = i_str != null ? DynValue.NewString(i_str) : m_ValueStack.Pop();
@@ -1815,6 +1867,14 @@ private int ExecIndex(Instruction i, int instructionPtr)
18151867
{
18161868
if (!isMultiIndex)
18171869
{
1870+
//Don't return private fields
1871+
if (!accessPrivate && obj.Table.PrivateKeys != null &&
1872+
obj.Table.PrivateKeys.IsKeyPrivate(idx))
1873+
{
1874+
m_ValueStack.Push(DynValue.Nil);
1875+
return instructionPtr;
1876+
}
1877+
18181878
var v = obj.Table.Get(idx);
18191879

18201880
if (!v.IsNil())

0 commit comments

Comments
 (0)