tlang-runtime-compiler/TLang.VM/TVMFile.cs

158 lines
6.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using TLang.Common;
namespace TLang.VM
{
public class TVMFile
{
public List<Class> Classes {get;set;}=new List<Class>();
public List<Function> Functions {get;set;}=new List<Function>();
public List<TLangDependency> Dependencies {get;set;}=new List<TLangDependency>();
public List<Chunk> Chunks {get;set;}=new List<Chunk>();
public TLangVersion Version {get;set;}
public RootEnvironment Environment {get;private set;}
public ChunkExecuter DefaultChunkExecuter => new ChunkExecuter(this,Chunks[0],Environment);
public TVMFile(Stream file,RootEnvironment env)
{
Environment = env;
byte[] data=new byte[11];
int read=file.Read(data,0,data.Length);
CheckData(data,read);
int deps=ReadBigEndianIntenger(file);
for(int i = 0;i<deps;i++)
{
int nameLen = ReadBigEndianIntenger(file);
data = new byte[nameLen];
EnsureRead(file,data);
string name = System.Text.Encoding.UTF8.GetString(data);
data = new byte[4];
EnsureRead(file,data);
var version = TLangVersion.FromBytes(data,0);
Dependencies.Add(new TLangDependency(name,version));
}
int funcsLen = ReadBigEndianIntenger(file);
for(int i = 0;i<funcsLen;i++)
{
int nameLen = ReadBigEndianIntenger(file);
data = new byte[nameLen];
EnsureRead(file,data);
string name = System.Text.Encoding.UTF8.GetString(data);
int chunkId = ReadBigEndianIntenger(file);
Functions.Add(new Function(){Name = name,ChunkId = chunkId});
}
int classLen = ReadBigEndianIntenger(file);
for(int i = 0;i<classLen;i++)
{
int nameLen = ReadBigEndianIntenger(file);
data = new byte[nameLen];
EnsureRead(file,data);
string name = System.Text.Encoding.UTF8.GetString(data);
nameLen = ReadBigEndianIntenger(file);
data = new byte[nameLen];
EnsureRead(file,data);
string inheritsFrom = System.Text.Encoding.UTF8.GetString(data);
Class classes = new Class();
classes.Name = name;
classes.InheritsFrom = inheritsFrom;
int classEntryCount = ReadBigEndianIntenger(file);
for(int j = 0;j<classEntryCount;j++)
{
int modifier = file.ReadByte();
if(modifier == -1) throw new IOException("End of file");
nameLen = ReadBigEndianIntenger(file);
data = new byte[nameLen];
EnsureRead(file,data);
string name2 = System.Text.Encoding.UTF8.GetString(data);
int chunkId = ReadBigEndianIntenger(file);
classes.ClassEntries.Add(new ClassEntry(){Modifier = (byte)modifier, Name = name2, ChunkId = chunkId});
}
Classes.Add(classes);
}
int chunks = ReadBigEndianIntenger(file);
for(int i = 0;i<chunks;i++)
{
Chunk chunk = new Chunk();
int argCount = ReadBigEndianIntenger(file);
for(int j = 0;j<argCount;j++)
{
int nameLen = ReadBigEndianIntenger(file);
data = new byte[nameLen];
EnsureRead(file,data);
string name2 = System.Text.Encoding.UTF8.GetString(data);
chunk.Arguments.Add(name2);
}
int length = ReadBigEndianIntenger(file);
var code = new byte[length];
int offset = 0;
int read0 = 0;
do{
read0 = Math.Min(length-offset,1024);
if(read0 > 0)
read0=file.Read(code,offset,read0);
offset+=read0;
} while(read0 > 0);
chunk.Code = code;
Chunks.Add(chunk);
}
}
private void EnsureRead(Stream file,byte[] data)
{
int i = file.Read(data,0,data.Length);
if(i != data.Length) throw new IndexOutOfRangeException("Array must be filled");
}
private int ReadBigEndianIntenger(Stream file)
{
byte[] bytes=new byte[4];
EnsureRead(file,bytes);
if(BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToInt32(bytes,0);
}
private void CheckData(byte[] data, int read)
{
if(read < 11 || data[0] != 'T' || data[1] != 'V' || data[2] != 'M') throw new BadImageFormatException("TVM file invalid");
if(TLangVersion.FromBytes(data,3).IntegerVersion > TLangVersion.Version.IntegerVersion) throw new BadImageFormatException("TVM Version is too old");
Version = TLangVersion.FromBytes(data,7);
}
public bool LoadDependencies(IDependencyPool dependencyPool)
{
foreach(var dep in Dependencies)
{
if(!dependencyPool.DependencyExists(dep))
{
if(!dependencyPool.AddDependency(dep,Environment)) return false;
}
}
foreach(var func in Functions)
{
Environment.SetObject(func.Name,new TClosure(this,Chunks[func.ChunkId],Environment));
}
foreach(var class0 in Classes)
{
Environment.AvailableClasses.Add(class0.Name,new LoadedClassData(class0.InheritsFrom,this,class0.ClassEntries));
Environment.SetObject(class0.Name,new TExternalMethod(args=>{
TClassInstance instance = new TClassInstance(this,class0,Environment);
instance.Init(args);
return instance;
}));
}
return true;
}
}
}