This repository has been archived by the owner on Nov 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 146
/
Script.cs
160 lines (152 loc) · 7.02 KB
/
Script.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright (C) 2016-2023 The Neo Project.
//
// The neo-vm is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.
using Neo.VM.Types;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Neo.VM
{
/// <summary>
/// Represents the script executed in the VM.
/// </summary>
[DebuggerDisplay("Length={Length}")]
public class Script
{
private readonly ReadOnlyMemory<byte> _value;
private readonly bool strictMode;
private readonly Dictionary<int, Instruction> _instructions = new();
/// <summary>
/// The length of the script.
/// </summary>
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _value.Length;
}
}
/// <summary>
/// Gets the <see cref="OpCode"/> at the specified index.
/// </summary>
/// <param name="index">The index to locate.</param>
/// <returns>The <see cref="OpCode"/> at the specified index.</returns>
public OpCode this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return (OpCode)_value.Span[index];
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Script"/> class.
/// </summary>
/// <param name="script">The bytecodes of the script.</param>
public Script(ReadOnlyMemory<byte> script) : this(script, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Script"/> class.
/// </summary>
/// <param name="script">The bytecodes of the script.</param>
/// <param name="strictMode">
/// Indicates whether strict mode is enabled.
/// In strict mode, the script will be checked, but the loading speed will be slower.
/// </param>
/// <exception cref="BadScriptException">In strict mode, the script was found to contain bad instructions.</exception>
public Script(ReadOnlyMemory<byte> script, bool strictMode)
{
this._value = script;
if (strictMode)
{
for (int ip = 0; ip < script.Length; ip += GetInstruction(ip).Size) { }
foreach (var (ip, instruction) in _instructions)
{
switch (instruction.OpCode)
{
case OpCode.JMP:
case OpCode.JMPIF:
case OpCode.JMPIFNOT:
case OpCode.JMPEQ:
case OpCode.JMPNE:
case OpCode.JMPGT:
case OpCode.JMPGE:
case OpCode.JMPLT:
case OpCode.JMPLE:
case OpCode.CALL:
case OpCode.ENDTRY:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.PUSHA:
case OpCode.JMP_L:
case OpCode.JMPIF_L:
case OpCode.JMPIFNOT_L:
case OpCode.JMPEQ_L:
case OpCode.JMPNE_L:
case OpCode.JMPGT_L:
case OpCode.JMPGE_L:
case OpCode.JMPLT_L:
case OpCode.JMPLE_L:
case OpCode.CALL_L:
case OpCode.ENDTRY_L:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.TRY:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8_1)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.TRY_L:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32_1)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.NEWARRAY_T:
case OpCode.ISTYPE:
case OpCode.CONVERT:
StackItemType type = (StackItemType)instruction.TokenU8;
if (!Enum.IsDefined(typeof(StackItemType), type))
throw new BadScriptException();
if (instruction.OpCode != OpCode.NEWARRAY_T && type == StackItemType.Any)
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
}
}
}
this.strictMode = strictMode;
}
/// <summary>
/// Get the <see cref="Instruction"/> at the specified position.
/// </summary>
/// <param name="ip">The position to get the <see cref="Instruction"/>.</param>
/// <returns>The <see cref="Instruction"/> at the specified position.</returns>
/// <exception cref="ArgumentException">In strict mode, the <see cref="Instruction"/> was not found at the specified position.</exception>
public Instruction GetInstruction(int ip)
{
if (ip >= Length) throw new ArgumentOutOfRangeException(nameof(ip));
if (!_instructions.TryGetValue(ip, out Instruction? instruction))
{
if (strictMode) throw new ArgumentException($"ip not found with strict mode", nameof(ip));
instruction = new Instruction(_value, ip);
_instructions.Add(ip, instruction);
}
return instruction;
}
public static implicit operator ReadOnlyMemory<byte>(Script script) => script._value;
public static implicit operator Script(ReadOnlyMemory<byte> script) => new(script);
public static implicit operator Script(byte[] script) => new(script);
}
}