浏览代码

Support for comptime file IO and process creation

Brian Fiete 3 年之前
父节点
当前提交
ce4b6e04de

+ 17 - 0
BeefLibs/corlib/src/Compiler.bf

@@ -2,6 +2,7 @@ using System.Reflection;
 using System.Diagnostics;
 using System.Collections;
 using System.Security.Cryptography;
+using System.IO;
 
 namespace System
 {
@@ -306,5 +307,21 @@ namespace System
 			if (Compiler.IsComptime)
 				Comptime_EmitMixin(text);
 		}
+
+		[Comptime]
+		public static Span<uint8> ReadBinary(StringView path)
+		{
+			List<uint8> data = scope .();
+			File.ReadAll(path, data);
+			return data;
+		}
+
+		[Comptime]
+		public static String ReadText(StringView path)
+		{
+			String data = scope .();
+			File.ReadAllText(path, data);
+			return data;
+		}
 	}
 }

+ 19 - 0
BeefLibs/corlib/src/Diagnostics/Debug.bf

@@ -106,5 +106,24 @@ namespace System.Diagnostics
 			if (gIsDebuggerPresent)
 				Break();
 		}
+
+		public static void WriteMemory(Span<uint8> mem)
+		{
+			String str = scope .();
+			for (int i < mem.Length)
+			{
+				if ((i != 0) && (i % 16 == 0))
+					str.Append('\n');
+				str.AppendF($" {mem.[Friend]mPtr[i]:X2}");
+			}
+			str.Append('\n');
+			Write(str);
+		}
+
+		public static void WriteMemory<T>(T result)
+		{
+#unwarn
+			WriteMemory(.((.)&result, sizeof(T)));
+		}
 	}
 }

+ 3 - 0
BeefLibs/corlib/src/OperatingSystem.bf

@@ -118,6 +118,9 @@ namespace System
 
 		public this()
 		{
+			if (Compiler.IsComptime)
+				return;
+
 #if BF_PLATFORM_WINDOWS
 			bool isWinSrv()
 			{

+ 6 - 0
BeefySysLib/platform/win/Platform.cpp

@@ -2909,6 +2909,12 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
 			//TODO: this doesn't set file stream location.  It only works for streams like pipes, sockets, etc
 			if (::ReadFileEx(file->mHandle, buffer, (uint32)size, &overlapped, OverlappedReadComplete))
 			{
+				if (file->mAsyncData == NULL)
+				{
+					OUTRESULT(BfpFileResult_InvalidParameter);
+					return 0;
+				}
+
 				if (!file->mAsyncData->WaitAndResetEvent(timeoutMS))
 				{
 					::CancelIoEx(file->mHandle, &overlapped);

+ 11 - 1
BeefySysLib/util/String.h

@@ -622,7 +622,17 @@ public:
 		str.mLength = (int_strsize)strlen(charPtr);
 		str.mAllocSizeAndFlags = str.mLength | StrPtrFlag;
 		return str;
-	}	
+	}
+
+	static StringImpl MakeRef(const char* charPtr, intptr length)
+	{
+		StringImpl str;
+		// This is just a ref - called when we pass a literal to a method (for example)
+		str.mPtr = (char*)charPtr;
+		str.mLength = (int_strsize)length;
+		str.mAllocSizeAndFlags = str.mLength | StrPtrFlag;
+		return str;
+	}
 
 	static StringImpl MakeRef(const StringView& strView)
 	{

+ 4 - 4
IDE/src/Compiler/BfSystem.bf

@@ -51,7 +51,7 @@ namespace IDE.Compiler
 		static extern char8* BfSystem_GetNamespaceSearch(void* bfSystem, char8* typeName, void* project);
 
         [CallingConvention(.Stdcall), CLink]
-        static extern void* BfSystem_CreateProject(void* bfSystem, char8* projectName);
+        static extern void* BfSystem_CreateProject(void* bfSystem, char8* projectName, char8* projectDir);
 
 		[CallingConvention(.Stdcall), CLink]
 		static extern void BfSystem_ClearTypeOptions(void* bfSystem);
@@ -142,7 +142,7 @@ namespace IDE.Compiler
         {
             using (mMonitor.Enter())
             {
-                var bfProject = CreateProject(project.mProjectName);
+                var bfProject = CreateProject(project.mProjectName, project.mProjectDir);
                 mProjectMap[project] = bfProject;
             }
         }
@@ -188,10 +188,10 @@ namespace IDE.Compiler
 				outNamespaceSearch.Append(namespaceSearch);
 		}
 
-        public BfProject CreateProject(String projectName)
+        public BfProject CreateProject(String projectName, String projectDir)
         {
             BfProject project = new BfProject();
-            project.mNativeBfProject = BfSystem_CreateProject(mNativeBfSystem, projectName);
+            project.mNativeBfProject = BfSystem_CreateProject(mNativeBfSystem, projectName, projectDir);
             return project;
         }
 

+ 13 - 0
IDE/src/IDEApp.bf

@@ -9167,6 +9167,13 @@ namespace IDE
 			bool doCompile = false;
 			if (lastCompileHadMessages)
 				doCompile = true;
+
+			bool needsComptime = true;
+			for (var project in mWorkspace.mProjects)
+			{
+				//Set needsComptime
+			}
+
 			if ((!workspaceOptions.mIncrementalBuild) && (!lastCompileHadMessages))
 			{
 				tryQueueFiles = false;
@@ -9177,6 +9184,9 @@ namespace IDE
 				}
 			}
 
+			if (needsComptime)
+				tryQueueFiles = true;
+
 			if (hotProject != null)
 			{
 				mWorkspace.mHadHotCompileSinceLastFullCompile = true;
@@ -9210,6 +9220,9 @@ namespace IDE
 	            }
 			}
 
+			if (needsComptime)
+				doCompile = true;
+
             if (!success)
 			{
 				bfCompiler.QueueDeletePassInstance(passInstance);

+ 1 - 1
IDEHelper/Compiler/BfCompiler.cpp

@@ -2927,7 +2927,7 @@ void BfCompiler::GenerateDynCastData()
 
 void BfCompiler::UpdateRevisedTypes()
 {
-	BfLogSysM("UpdateRevisedTypes\n");
+	BfLogSysM("BfCompiler::UpdateRevisedTypes\n");
 	BP_ZONE("BfCompiler::UpdateRevisedTypes");	
 		
 	// See if we have any name conflicts and remove those

+ 58 - 15
IDEHelper/Compiler/BfContext.cpp

@@ -26,6 +26,7 @@
 #include "BfSourceClassifier.h"
 #include "BfAutoComplete.h"
 #include "BfResolvePass.h"
+#include "CeMachine.h"
 
 #pragma warning(pop)
 
@@ -1850,6 +1851,7 @@ void BfContext::PreUpdateRevisedTypes()
 void BfContext::UpdateRevisedTypes()
 {
 	BP_ZONE("BfContext::UpdateRevisedTypes");
+	BfLogSysM("BfContext::UpdateRevisedTypes\n");
 
 	int wantPtrSize;
 	if ((mCompiler->mOptions.mMachineType == BfMachineType_x86) |
@@ -1889,6 +1891,9 @@ void BfContext::UpdateRevisedTypes()
 	bool wantsDebugInfo = (mCompiler->mOptions.mEmitDebugInfo);
 	
 	Array<BfTypeInstance*> defStateChangedQueue;
+	Array<BfTypeInstance*> defEmitParentCheckQueue;
+
+	Dictionary<String, uint64> lastWriteTimeMap;
 
 	// Do primary 'rebuild' scan
 	for (auto type : mResolvedTypes)
@@ -1917,20 +1922,8 @@ void BfContext::UpdateRevisedTypes()
 
 		auto typeDef = typeInst->mTypeDef;		
 
-		if (typeDef->mEmitParent != NULL)
-		{
-			if (typeDef->mDefState == BfTypeDef::DefState_Deleted)
-			{
-				typeInst->mTypeDef = typeDef->mEmitParent;
-			}
-			else
-			{
-				auto emitTypeDef = typeDef;
-				typeDef = typeDef->mEmitParent;
-				if (typeDef->mNextRevision != NULL)
-					emitTypeDef->mDefState = BfTypeDef::DefState_EmittedDirty;
-			}
-		}
+		if (typeDef->mEmitParent != NULL)		
+			defEmitParentCheckQueue.Add(typeInst);
 
 		if (typeDef->mProject->mDisabled)
 		{
@@ -1939,7 +1932,7 @@ void BfContext::UpdateRevisedTypes()
 		}
 		
 		typeInst->mRebuildFlags = BfTypeRebuildFlag_None;
-						
+		
 		if (typeDef->mIsPartial)
 		{
 			// This was a type that wasn't marked as partial before but now it is, so it doesn't need its own typedef
@@ -1948,6 +1941,31 @@ void BfContext::UpdateRevisedTypes()
 			continue;
 		}
 
+		if (typeInst->mCeTypeInfo != NULL)
+		{
+			bool changed = false;
+
+			for (auto& kv : typeInst->mCeTypeInfo->mRebuildMap)
+			{
+				if (kv.mKey.mKind == CeRebuildKey::Kind_File)
+				{
+					String* keyPtr = NULL;
+					uint64* valuePtr = NULL;
+					if (lastWriteTimeMap.TryAdd(kv.mKey.mString, &keyPtr, &valuePtr))
+					{
+						*valuePtr = BfpFile_GetTime_LastWrite(kv.mKey.mString.c_str());
+					}
+					if (*valuePtr != kv.mValue.mInt)
+						changed = true;
+				}
+			}
+
+			if (changed)
+			{
+				RebuildType(typeInst);
+			}
+		}
+
 		if ((typeInst->mHotTypeData != NULL) && (!mCompiler->IsHotCompile()))
 		{
 			if (typeInst->mHotTypeData->GetLatestVersion()->mDeclHotCompileIdx != 0)
@@ -2042,6 +2060,31 @@ void BfContext::UpdateRevisedTypes()
 		}
 	}
 
+	for (auto typeInst : defEmitParentCheckQueue)
+	{
+		if (typeInst->IsDeleting())
+			continue;
+		auto typeDef = typeInst->mTypeDef;
+		if (typeDef->mEmitParent != NULL)
+		{
+			if (typeDef->mDefState == BfTypeDef::DefState_Deleted)
+			{
+				BfLogSysM("Type %p typeDef %p deleted, setting to emitParent %p\n", typeInst, typeDef, typeDef->mEmitParent);
+				typeInst->mTypeDef = typeDef->mEmitParent;
+			}
+			else
+			{
+				auto emitTypeDef = typeDef;
+				typeDef = typeDef->mEmitParent;
+				if (typeDef->mNextRevision != NULL)
+				{
+					BfLogSysM("Type %p typeDef %p emitparent %p has next revision, setting emittedDirty\n", typeInst, emitTypeDef, typeDef);
+					emitTypeDef->mDefState = BfTypeDef::DefState_EmittedDirty;
+				}
+			}
+		}
+	}
+
 	//
 	{
 		AutoCrit autoCrit(mSystem->mDataLock);

+ 4 - 2
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -14543,8 +14543,10 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
 		{				
 			if (typeInstance->IsObject())
 			{	
+				bool hasRealtimeLeakCheck = (mModule->mCompiler->mOptions.mEnableRealtimeLeakCheck) && (!IsComptime());
+
 				bool wantsCtorClear = true;
-				if (mModule->mCompiler->mOptions.mEnableRealtimeLeakCheck)
+				if (hasRealtimeLeakCheck)
 				{
 					// Dbg_ObjectAlloc clears internally so we don't need to call CtorClear for those
 					if ((!isStackAlloc) && (!allocTarget.mCustomAllocator) && (allocTarget.mScopedInvocationTarget == NULL))
@@ -14566,7 +14568,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
 					}
 				}
 
-				if ((!mModule->mIsComptimeModule) && (isStackAlloc) && (mModule->mCompiler->mOptions.mEnableRealtimeLeakCheck))
+				if ((!mModule->mIsComptimeModule) && (isStackAlloc) && (hasRealtimeLeakCheck))
 				{
 					BfMethodInstance* markMethod = mModule->GetRawMethodByName(mModule->mContext->mBfObjectType, "GCMarkMembers");
 					BF_ASSERT(markMethod != NULL);

+ 1 - 1
IDEHelper/Compiler/BfModule.cpp

@@ -4297,7 +4297,7 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance,
 			int ceExecuteId = -1;
 			if (mCompiler->mCEMachine != NULL)
 				ceExecuteId = mCompiler->mCEMachine->mExecuteId;
-
+			
 			BfTypeState typeState;			
 			typeState.mType = mCurTypeInstance;
 			typeState.mCurTypeDef = fieldDef->mDeclaringType;

+ 8 - 9
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -1189,7 +1189,10 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType
 			}
 		}
 
-		BfLogSysM("PopulateType: %p %s populateType:%d ResolveOnly:%d Reified:%d AutoComplete:%d Ctx:%p Mod:%p TypeId:%d\n", resolvedTypeRef, TypeToString(resolvedTypeRef, BfTypeNameFlags_None).c_str(), populateType, mCompiler->mIsResolveOnly, mIsReified, mCompiler->IsAutocomplete(), mContext, this, resolvedTypeRef->mTypeId);
+		BfTypeDef* typeDef = NULL;
+		if (typeInstance != NULL)
+			typeDef = typeInstance->mTypeDef;
+		BfLogSysM("PopulateType: %p %s populateType:%d ResolveOnly:%d Reified:%d AutoComplete:%d Ctx:%p Mod:%p TypeId:%d TypeDef:%p\n", resolvedTypeRef, TypeToString(resolvedTypeRef, BfTypeNameFlags_None).c_str(), populateType, mCompiler->mIsResolveOnly, mIsReified, mCompiler->IsAutocomplete(), mContext, this, resolvedTypeRef->mTypeId, typeDef);
 
 		BF_ASSERT(!resolvedTypeRef->IsDeleting());
 	}
@@ -2078,7 +2081,7 @@ void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeIn
 {
 	for (int ifaceTypeId : ceEmitContext->mInterfaces)
 		typeInstance->mCeTypeInfo->mPendingInterfaces.Add(ifaceTypeId);
-	
+		
 	if (ceEmitContext->mEmitData.IsEmpty())
 		return;
 		
@@ -2166,12 +2169,7 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance*
 			if (!attrType->IsValuelessType())
 				args.Add(attrVal);
 			args.Add(mBfIRBuilder->CreateTypeOf(typeInstance));
-			
-			//TESTING
-// 			mCompiler->mCEMachine->ReleaseContext(ceContext);			
-// 			ceContext = mCompiler->mCEMachine->AllocContext();
-// 			ceContext->mMemory.mSize = ceContext->mMemory.mAllocSize;
-			
+
 			auto result = ceContext->Call(customAttribute.mRef, this, methodInstance, args, CeEvalFlags_None, NULL);
 
 			if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotted)
@@ -2182,7 +2180,7 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance*
 				// We populated before we could finish
 				AssertErrorState();
 			}
-			else 
+			else
 			{
 				auto owner = methodInstance->GetOwner();
 				int typeId = owner->mTypeId;
@@ -3732,6 +3730,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 			if (typeInstance->mTypeDef != innerTypeInst->mTypeDef)
 			{
 				// Rebuild with proper typedef (generally from inner type comptime emission)
+				BfLogSysM("Boxed type %p overriding typeDef to %p from inner type %p\n", typeInstance, innerTypeInst->mTypeDef, innerType);
 				typeInstance->mTypeDef = innerTypeInst->mTypeDef;
 				DoPopulateType(resolvedTypeRef, populateType);
 				return;

+ 1 - 17
IDEHelper/Compiler/BfResolvedTypeUtils.h

@@ -1823,23 +1823,7 @@ public:
 	String mEmitData;
 };
 
-class BfCeTypeInfo
-{
-public:	
-	Dictionary<int, BfCeTypeEmitEntry> mOnCompileMap;
-	Dictionary<int, BfCeTypeEmitEntry> mTypeIFaceMap;
-	Array<int> mPendingInterfaces;
-	Val128 mHash;
-	bool mFailed;
-	BfCeTypeInfo* mNext;
-
-public:
-	BfCeTypeInfo()
-	{
-		mFailed = false;
-		mNext = NULL;		
-	}
-};
+class BfCeTypeInfo;
 
 // Instance of struct or class
 class BfTypeInstance : public BfDependedType

+ 2 - 1
IDEHelper/Compiler/BfSystem.cpp

@@ -4159,11 +4159,12 @@ BF_EXPORT const char* BF_CALLTYPE BfSystem_GetNamespaceSearch(BfSystem* bfSystem
 	return outString.c_str();
 }
 
-BF_EXPORT BfProject* BF_CALLTYPE BfSystem_CreateProject(BfSystem* bfSystem, const char* projectName)
+BF_EXPORT BfProject* BF_CALLTYPE BfSystem_CreateProject(BfSystem* bfSystem, const char* projectName, const char* projectDir)
 {
 	AutoCrit autoCrit(bfSystem->mDataLock);
 	BfProject* bfProject = new BfProject();
 	bfProject->mName = projectName;
+	bfProject->mDirectory = projectDir;
 	bfProject->mSystem = bfSystem;
 	bfProject->mIdx = (int)bfSystem->mProjects.size();
 	bfSystem->mProjects.push_back(bfProject);	

+ 1 - 0
IDEHelper/Compiler/BfSystem.h

@@ -1180,6 +1180,7 @@ public:
 	BfSystem* mSystem;
 	String mName;
 	String mSafeName;
+	String mDirectory;
 	Array<BfProject*> mDependencies;
 	BfTargetType mTargetType;
 	BfCodeGenOptions mCodeGenOptions;	

+ 457 - 32
IDEHelper/Compiler/CeMachine.cpp

@@ -6,6 +6,8 @@
 #include "BfReducer.h"
 #include "BfExprEvaluator.h"
 #include "../Backend/BeIRCodeGen.h"
+#include "BeefySysLib/platform/PlatformHelper.h"
+
 extern "C"
 {
 #include "BeefySysLib/third_party/utf8proc/utf8proc.h"
@@ -301,6 +303,24 @@ static int DoubleToString(double d, char* outStr)
 
 //////////////////////////////////////////////////////////////////////////
 
+CeInternalData::~CeInternalData()
+{
+	switch (mKind)
+	{
+	case Kind_File:
+		BfpFile_Release(mFile);
+		break;
+	case Kind_FindFileData:
+		BfpFindFileData_Release(mFindFileData);
+		break;
+	case Kind_Spawn:
+		BfpSpawn_Release(mSpawn);
+		break;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+
 CeFunction::~CeFunction()
 {
 	BF_ASSERT(mId == -1);
@@ -1316,6 +1336,11 @@ void CeBuilder::Build()
 		if (mCeFunction->mInitializeState == CeFunction::InitializeState_Initialized)
 			return;
 
+		if (methodInstance->mMethodDef->mName == "DecodeToUTF8")
+		{
+			NOP;
+		}
+
 		if (!dupMethodInstance.mIRFunction)
 		{
 			mCeFunction->mFailed = true;
@@ -2886,6 +2911,7 @@ CeContext::CeContext()
 CeContext::~CeContext()
 {
 	delete mHeap;
+	BF_ASSERT(mInternalDataMap.IsEmpty());
 }
 
 BfError* CeContext::Fail(const StringImpl& error)
@@ -2996,6 +3022,17 @@ BfError* CeContext::Fail(const CeFrame& curFrame, const StringImpl& str)
 //////////////////////////////////////////////////////////////////////////
 
 
+void CeContext::AddRebuild(const CeRebuildKey& key, const CeRebuildValue& value)
+{
+	if (mCurModule == NULL)
+		return;
+	if (mCurModule->mCurTypeInstance == NULL)
+		return;
+	if (mCurModule->mCurTypeInstance->mCeTypeInfo == NULL)
+		mCurModule->mCurTypeInstance->mCeTypeInfo = new BfCeTypeInfo();
+	mCurModule->mCurTypeInstance->mCeTypeInfo->mRebuildMap[key] = value;
+}
+
 uint8* CeContext::CeMalloc(int size)
 {
 #ifdef CE_ENABLE_HEAP
@@ -4308,6 +4345,59 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 		return false; \
 	}
 
+#define CE_CHECKADDR_STR(STRNAME, ADDR) \
+	{ \
+		addr_ce checkAddr = ADDR; \
+		while (true) \
+		{ \
+			if ((uintptr)checkAddr >= (uintptr)memSize) \
+			{ \
+				break; \
+			} \
+			if (memStart[checkAddr] == 0) \
+			{ \
+				CE_CHECKADDR(ADDR, checkAddr - ADDR + 1); \
+				STRNAME = String::MakeRef((char*)memStart + ADDR, checkAddr - ADDR + 1); \
+				break; \
+			} \
+			checkAddr++; \
+		} \
+	}
+
+#define CE_GET_INTERNAL(VAR, ID, KIND) \
+	if (!mInternalDataMap.TryGetValue((int)ID, &VAR)) \
+	{ \
+		_Fail("Invalid internal resource id"); \
+		return false; \
+	} \
+	if (VAR->mKind != KIND) \
+	{ \
+		_Fail("Invalid internal resource kind"); \
+		return false; \
+	} \
+	if (VAR->mReleased) \
+	{ \
+		_Fail("Resource already released"); \
+		return false; \
+	}
+
+#define CE_REMOVE_INTERNAL(VAR, ID, KIND) \
+	if (!mInternalDataMap.Remove((int)ID, &VAR)) \
+	{ \
+		_Fail("Invalid internal resource id"); \
+		return false; \
+	} \
+	if (VAR->mKind != KIND) \
+	{ \
+		_Fail("Invalid internal resource kind"); \
+		return false; \
+	} \
+	if (VAR->mReleased) \
+	{ \
+		_Fail("Resource already released"); \
+		return false; \
+	}
+
 #define CE_GETINST(T) *((T*)(instPtr += sizeof(T)) - 1)
 #define CE_GETFRAME(T) *(T*)(framePtr + *((int32*)(instPtr += sizeof(int32)) - 1))
 #define CEOP_BIN(OP, T) \
@@ -4422,7 +4512,7 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 	instPtr = &ceFunction->mCode[0]; \
 	CE_CHECKSTACK();
 
-static void CeSetAddrVal(void* ptr, addr_ce val, int32 ptrSize)
+static void CeSetAddrVal(void* ptr, int64 val, int32 ptrSize)
 {
 	if (ptrSize == 4)
 		*(int32*)(ptr) = (int32)val;
@@ -4559,8 +4649,16 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 			}
 			else if (checkFunction->mFunctionKind == CeFunctionKind_DebugWrite_Int)
 			{
-				int32 intVal = *(int32*)((uint8*)stackPtr + 0);
-				OutputDebugStrF("Debug Val: %d\n", intVal);
+				if (ceModule->mSystem->mPtrSize == 4)
+				{
+					int32 intVal = *(int32*)((uint8*)stackPtr + 0);
+					OutputDebugStrF("Debug Val: %d\n", intVal);
+				}
+				else
+				{
+					int64 intVal = *(int64*)((uint8*)stackPtr + 0);
+					OutputDebugStrF("Debug Val: %lld\n", intVal);
+				}
 			}
 			else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectType)
 			{
@@ -4911,7 +5009,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 				addr_ce strAddr = *(addr_ce*)((uint8*)stackPtr + 8);
 				addr_ce endAddr = *(addr_ce*)((uint8*)stackPtr + 8 + ptrSize);
 								
-				addr_ce checkAddr = strAddr;				
+				addr_ce checkAddr = strAddr;		
 				while (true)
 				{
 					if ((uintptr)checkAddr >= (uintptr)memSize)
@@ -4921,10 +5019,11 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 					}
 					if (memStart[checkAddr] == 0)
 						break;
+					checkAddr++;
 				}
 				CE_CHECKADDR(strAddr, checkAddr - strAddr + 1);
 
-				char* strPtr = (char*)(memStart + strAddr);								
+				char* strPtr = (char*)(memStart + strAddr);
 				char** endPtr = NULL;
 				if (endAddr != NULL)
 					endPtr = (char**)(memStart + endAddr);
@@ -4959,7 +5058,281 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 				memcpy(memStart + strAddr, str, count + 1);
 				result = count;
 			}
-			else			
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Close)
+			{
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + 0);	
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				
+				CE_CHECKADDR(outResultAddr, 4);
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				BfpFile_Close(internalData->mFile, (BfpFileResult*)(memStart + outResultAddr));
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Create)
+			{
+				void* resultPtr = ((uint8*)stackPtr + 0);
+				addr_ce nameAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				int createKind = *(int*)((uint8*)stackPtr + ptrSize + ptrSize);
+				int createFlags = *(int*)((uint8*)stackPtr + ptrSize + ptrSize + 4);
+				int createFileAttrs = *(int*)((uint8*)stackPtr + ptrSize + ptrSize + 4 + 4);
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + 4 + 4 + 4);
+
+				String path;
+				CE_CHECKADDR_STR(path, nameAddr);
+				CE_CHECKADDR(outResultAddr, 4);
+				
+				BfProject* activeProject = NULL;
+				auto activeTypeDef = mCurModule->GetActiveTypeDef();
+				if (activeTypeDef != NULL)
+					activeProject = activeTypeDef->mProject;
+				if (activeProject != NULL)				
+					path = GetAbsPath(path, activeProject->mDirectory);
+
+				auto bfpFile = BfpFile_Create(path.c_str(), (BfpFileCreateKind)createKind, (BfpFileCreateFlags)createFlags, (BfpFileAttributes)createFileAttrs, (BfpFileResult*)(memStart + outResultAddr));
+				if (bfpFile != NULL)
+				{
+					if ((createKind == BfpFileCreateKind_OpenExisting) || (createKind == BfpFileCreateKind_OpenAlways))
+					{
+						auto timeStamp = BfpFile_GetTime_LastWrite(path.c_str());
+						if (timeStamp != 0)
+						{							
+							CeRebuildKey rebuildKey;
+							rebuildKey.mKind = CeRebuildKey::Kind_File;
+							rebuildKey.mString = path;
+
+							CeRebuildValue rebuildValue;
+							rebuildValue.mInt = timeStamp;
+
+							AddRebuild(rebuildKey, rebuildValue);
+						}
+					}
+
+					CeInternalData* internalData = new CeInternalData();
+					internalData->mKind = CeInternalData::Kind_File;
+					internalData->mFile = bfpFile;
+					mInternalDataMap[++mCurHandleId] = internalData;
+					CeSetAddrVal(resultPtr, mCurHandleId, ptrSize);
+				}
+				else
+					CeSetAddrVal(resultPtr, 0, ptrSize);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Flush)
+			{
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + 0);
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				BfpFile_Flush(internalData->mFile);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_GetFileSize)
+			{
+				int64& result = *(int64*)((uint8*)stackPtr + 0);
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + 8);
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				result = BfpFile_GetFileSize(internalData->mFile);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Read)
+			{
+				void* resultPtr = ((uint8*)stackPtr + 0);
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				addr_ce bufferPtr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize);
+				intptr bufferSize = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize);
+				int timeoutMS = *(int32*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize);
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize + ptrSize);
+
+				CE_CHECKADDR(bufferPtr, bufferSize);
+				CE_CHECKADDR(outResultAddr, 4);
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				int64 result = BfpFile_Read(internalData->mFile, memStart + bufferPtr, bufferSize, timeoutMS, (BfpFileResult*)(memStart + outResultAddr));
+				CeSetAddrVal(resultPtr, result, ptrSize);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Release)
+			{
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + 0);
+
+				CeInternalData* internalData = NULL;
+				CE_REMOVE_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				delete internalData;
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Seek)
+			{
+				int64& result = *(int64*)((uint8*)stackPtr + 0);
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + 8);
+				int64 offset = *(int64*)((uint8*)stackPtr + 8 + ptrSize);
+				int seekKind = *(int*)((uint8*)stackPtr + 8 + ptrSize + 8);
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				result = BfpFile_Seek(internalData->mFile, offset, (BfpFileSeekKind)seekKind);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Truncate)
+			{
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + 0);	
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				
+				CE_CHECKADDR(outResultAddr, 4);
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				BfpFile_Truncate(internalData->mFile, (BfpFileResult*)(memStart + outResultAddr));
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpFile_Write)
+			{
+				void* resultPtr = ((uint8*)stackPtr + 0);
+				addr_ce fileId = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				addr_ce bufferPtr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize);
+				intptr bufferSize = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize);
+				int timeoutMS = *(int32*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize);
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize + ptrSize);
+
+				CE_CHECKADDR(bufferPtr, bufferSize);
+				CE_CHECKADDR(outResultAddr, 4);
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)fileId, CeInternalData::Kind_File);
+				int64 result = BfpFile_Write(internalData->mFile, memStart + bufferPtr, bufferSize, timeoutMS, (BfpFileResult*)(memStart + outResultAddr));
+				CeSetAddrVal(resultPtr, result, ptrSize);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpSpawn_Create)
+			{
+				void* resultPtr = ((uint8*)stackPtr + 0);
+				addr_ce targetPathAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				addr_ce argsAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize);
+				addr_ce workingDirAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize);
+				addr_ce envAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize);
+				int flags = *(int*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize + ptrSize);
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize + ptrSize + ptrSize + ptrSize);
+
+				String targetPath;
+				CE_CHECKADDR_STR(targetPath, targetPathAddr);
+				String args;
+				CE_CHECKADDR_STR(args, argsAddr);
+				String workingDir;
+				CE_CHECKADDR_STR(workingDir, workingDirAddr);
+				String env;
+				CE_CHECKADDR_STR(env, envAddr);
+				CE_CHECKADDR(outResultAddr, 4);
+
+				BfProject* activeProject = NULL;
+				auto activeTypeDef = mCurModule->GetActiveTypeDef();
+				if (activeTypeDef != NULL)
+					activeProject = activeTypeDef->mProject;
+				if (activeProject != NULL)
+					targetPath = GetAbsPath(targetPath, activeProject->mDirectory);
+
+				auto bfpSpawn = BfpSpawn_Create(targetPath.c_str(), args.c_str(), workingDir.c_str(), env.c_str(), (BfpSpawnFlags)flags, (BfpSpawnResult*)(memStart + outResultAddr));
+				if (bfpSpawn != NULL)
+				{					
+					CeInternalData* internalData = new CeInternalData();
+					internalData->mKind = CeInternalData::Kind_Spawn;
+					internalData->mSpawn = bfpSpawn;
+					mInternalDataMap[++mCurHandleId] = internalData;
+					CeSetAddrVal(resultPtr, mCurHandleId, ptrSize);
+				}
+				else
+					CeSetAddrVal(resultPtr, 0, ptrSize);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpSpawn_GetStdHandles)
+			{
+				addr_ce spawnId = *(addr_ce*)((uint8*)stackPtr + 0);
+				addr_ce outStdInAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize);
+				addr_ce outStdOutAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize);
+				addr_ce outStdErrAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize);
+
+				CE_CHECKADDR(outStdInAddr, ptrSize);
+				CE_CHECKADDR(outStdOutAddr, ptrSize);
+				CE_CHECKADDR(outStdErrAddr, ptrSize);
+
+				BfpFile* outStdIn = NULL;
+				BfpFile* outStdOut = NULL;
+				BfpFile* outStdErr = NULL;
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)spawnId, CeInternalData::Kind_Spawn);				
+				BfpSpawn_GetStdHandles(internalData->mSpawn,
+					(outStdInAddr != 0) ? &outStdIn : NULL,
+					(outStdOutAddr != 0) ? &outStdOut : NULL,
+					(outStdErrAddr != 0) ? &outStdErr : NULL);
+				
+				auto _SetHandle = [&](addr_ce addr, BfpFile* file)
+				{
+					if (addr == 0)
+						return;
+					if (file != NULL)
+					{
+						CeInternalData* internalData = new CeInternalData();
+						internalData->mKind = CeInternalData::Kind_File;
+						internalData->mFile = file;
+						mInternalDataMap[++mCurHandleId] = internalData;
+						CeSetAddrVal(memStart + addr, mCurHandleId, ptrSize);
+					}
+				};
+				
+				_SetHandle(outStdInAddr, outStdIn);
+				_SetHandle(outStdOutAddr, outStdOut);
+				_SetHandle(outStdErrAddr, outStdErr);
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpSpawn_Kill)
+			{
+				addr_ce spawnId = *(addr_ce*)((uint8*)stackPtr + 0);
+				int exitCode = *(int*)((uint8*)stackPtr + ptrSize);
+				int killFlags = *(int*)((uint8*)stackPtr + ptrSize + ptrSize);
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + ptrSize + ptrSize + ptrSize);
+
+				CE_CHECKADDR(outResultAddr, 4);
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)spawnId, CeInternalData::Kind_Spawn);
+				BfpSpawn_Kill(internalData->mSpawn, exitCode, (BfpKillFlags)killFlags, (BfpSpawnResult*)(memStart + outResultAddr));
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpSpawn_Release)
+			{
+				addr_ce spawnId = *(addr_ce*)((uint8*)stackPtr + 0);
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)spawnId, CeInternalData::Kind_Spawn);
+				internalData->mReleased = true;
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_BfpSpawn_WaitFor)
+			{
+				bool& result = *(bool*)((uint8*)stackPtr + 0);
+				addr_ce spawnId = *(addr_ce*)((uint8*)stackPtr + 1);
+				int waitMS = *(int*)((uint8*)stackPtr + 1 + ptrSize);
+				addr_ce outExitCodeAddr = *(addr_ce*)((uint8*)stackPtr + 1 + ptrSize + ptrSize);
+				addr_ce outResultAddr = *(addr_ce*)((uint8*)stackPtr + 1 + ptrSize + ptrSize + ptrSize);
+
+				CE_CHECKADDR(outExitCodeAddr, ptrSize);
+				CE_CHECKADDR(outResultAddr, 4);
+
+				CeInternalData* internalData = NULL;
+				CE_GET_INTERNAL(internalData, (int)spawnId, CeInternalData::Kind_Spawn);
+				
+				int timeLeft = waitMS;
+				do
+				{
+					if (*fastFinishPtr)
+					{
+						result = false;
+						break;
+					}
+
+					int waitTime = 20;
+					if (timeLeft >= 0)
+					{
+						waitTime = BF_MIN(timeLeft, 20);
+						timeLeft -= waitTime;
+					}
+
+					int outExitCode = 0;
+					result = BfpSpawn_WaitFor(internalData->mSpawn, waitTime, &outExitCode, (BfpSpawnResult*)(memStart + outResultAddr));
+					if (result)
+						break;
+					if (waitTime == 0)
+						break;					
+				} while (true);
+			}
+			else
 			{
 				Fail(_GetCurFrame(), StrFormat("Unable to invoke extern method '%s'", ceModule->MethodToString(checkFunction->mMethodInstance).c_str()));
 				return false;
@@ -5005,6 +5378,17 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 
 		++instIdx;
 
+		if (instIdx >= /*0xBC0*/0xBA0)
+		{
+			NOP;
+		}
+
+// 		if (instIdx == 0x444)
+// 		{
+// 			_CrtCheckMemory();
+// 			NOP;
+// 		}
+
 		CeOp op = CE_GETINST(CeOp);
 		switch (op)
 		{
@@ -5580,7 +5964,12 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 
 			auto valueType = bfType->ToTypeInstance();
 			if (valueType->mVirtualMethodTable.IsEmpty())
-				ceModule->PopulateType(valueType, BfPopulateType_DataAndMethods);
+				ceModule->PopulateType(valueType, BfPopulateType_Full_Force);
+			if (valueType->mVirtualMethodTable.IsEmpty())
+			{
+				_Fail("Empty virtual table");
+				return false;
+			}
 			auto methodInstance = (BfMethodInstance*)valueType->mVirtualMethodTable[virtualIdx].mImplementingMethod;
 
 			auto callFunction = mCeMachine->GetPreparedFunction(methodInstance);
@@ -6338,6 +6727,8 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 			_Fail("Unhandled op");
 			return false;
 		}
+
+		//BF_ASSERT(_CrtCheckMemory() != 0);
 	}
 
 	return true;
@@ -6359,7 +6750,7 @@ CeMachine::CeMachine(BfCompiler* compiler)
 	mCurBuilder = NULL;
 	mPreparingFunction = NULL;
 
-	mCurEmitContext = NULL;
+	mCurEmitContext = NULL;	
 
 	mAppendAllocInfo = NULL;
 	mTempParser = NULL;
@@ -6893,6 +7284,34 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
 			{
 				if (methodDef->mName == "BfpSystem_GetTimeStamp")
 					ceFunction->mFunctionKind = CeFunctionKind_BfpSystem_GetTimeStamp;
+				else if (methodDef->mName == "BfpFile_Close")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Close;
+				else if (methodDef->mName == "BfpFile_Create")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Create;
+				else if (methodDef->mName == "BfpFile_Flush")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Flush;
+				else if (methodDef->mName == "BfpFile_GetFileSize")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_GetFileSize;
+				else if (methodDef->mName == "BfpFile_Read")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Read;
+				else if (methodDef->mName == "BfpFile_Release")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Release;
+				else if (methodDef->mName == "BfpFile_Seek")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Seek;
+				else if (methodDef->mName == "BfpFile_Truncate")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Truncate;
+				else if (methodDef->mName == "BfpFile_Write")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpFile_Write;
+				else if (methodDef->mName == "BfpSpawn_Create")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpSpawn_Create;
+				else if (methodDef->mName == "BfpSpawn_GetStdHandles")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpSpawn_GetStdHandles;
+				else if (methodDef->mName == "BfpSpawn_Kill")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpSpawn_Kill;
+				else if (methodDef->mName == "BfpSpawn_Release")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpSpawn_Release;
+				else if (methodDef->mName == "BfpSpawn_WaitFor")
+					ceFunction->mFunctionKind = CeFunctionKind_BfpSpawn_WaitFor;
 			}
 			else if (owner->IsInstanceOf(mCeModule->mCompiler->mChar32TypeDef))
 			{
@@ -6926,45 +7345,45 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
 			{
 				if (methodDef->mName == "Abs")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Abs;
-				if (methodDef->mName == "Acos")
+				else if (methodDef->mName == "Acos")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Acos;
-				if (methodDef->mName == "Asin")
+				else if (methodDef->mName == "Asin")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Asin;
-				if (methodDef->mName == "Atan")
+				else if (methodDef->mName == "Atan")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Atan;
-				if (methodDef->mName == "Atan2")
+				else if (methodDef->mName == "Atan2")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Atan2;
-				if (methodDef->mName == "Ceiling")
+				else if (methodDef->mName == "Ceiling")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Ceiling;
-				if (methodDef->mName == "Cos")
+				else if (methodDef->mName == "Cos")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Cos;
-				if (methodDef->mName == "Cosh")
+				else if (methodDef->mName == "Cosh")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Cosh;
-				if (methodDef->mName == "Exp")
+				else if (methodDef->mName == "Exp")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Exp;
-				if (methodDef->mName == "Floor")
+				else if (methodDef->mName == "Floor")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Floor;
-				if (methodDef->mName == "Log")
+				else if (methodDef->mName == "Log")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Log;
-				if (methodDef->mName == "Log10")
+				else if (methodDef->mName == "Log10")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Log10;
-				if (methodDef->mName == "Mod")
+				else if (methodDef->mName == "Mod")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Mod;
-				if (methodDef->mName == "Pow")
+				else if (methodDef->mName == "Pow")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Pow;
-				if (methodDef->mName == "Round")
+				else if (methodDef->mName == "Round")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Round;
-				if (methodDef->mName == "Sin")
+				else if (methodDef->mName == "Sin")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Sin;
-				if (methodDef->mName == "Sinh")
+				else if (methodDef->mName == "Sinh")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Sinh;
-				if (methodDef->mName == "Sqrt")
+				else if (methodDef->mName == "Sqrt")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Sqrt;
-				if (methodDef->mName == "Tan")
+				else if (methodDef->mName == "Tan")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Tan;
-				if (methodDef->mName == "Tanh")
+				else if (methodDef->mName == "Tanh")
 					ceFunction->mFunctionKind = CeFunctionKind_Math_Tanh;
-			}
+			}			
 
 			ceFunction->mInitializeState = CeFunction::InitializeState_Initialized;
 			return;
@@ -7214,11 +7633,12 @@ CeContext* CeMachine::AllocContext()
 		ceContext->mMemory.Reserve(BF_CE_INITIAL_MEMORY);
 	}
 
-	ceContext->mCurEmitContext = mCurEmitContext;
-	mCurEmitContext = NULL;
+	ceContext->mCurEmitContext = mCurEmitContext;	
+	mCurEmitContext = NULL;	
 	mExecuteId++;	
 	ceContext->mMemory.Resize(BF_CE_STACK_SIZE);
 	ceContext->mExecuteId = mExecuteId;
+	ceContext->mCurHandleId = 0;
 	return ceContext;
 }
 
@@ -7234,9 +7654,12 @@ void CeMachine::ReleaseContext(CeContext* ceContext)
 	ceContext->mStaticFieldMap.Clear();
 	ceContext->mHeap->Clear(BF_CE_MAX_CARRYOVER_HEAP);
 	ceContext->mReflectTypeIdOffset = -1;	
-	mCurEmitContext = ceContext->mCurEmitContext;
-	ceContext->mCurEmitContext = NULL;
+	mCurEmitContext = ceContext->mCurEmitContext;	
+	ceContext->mCurEmitContext = NULL;	
 	mContextList.Add(ceContext);
+	for (auto kv : ceContext->mInternalDataMap)	
+		delete kv.mValue;
+	ceContext->mInternalDataMap.Clear();
 }
 
 BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* methodInstance, const BfSizedArray<BfIRValue>& args, CeEvalFlags flags, BfType* expectingType)
@@ -7246,3 +7669,5 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 	ReleaseContext(ceContext);	
 	return result;
 }
+
+

+ 113 - 6
IDEHelper/Compiler/CeMachine.h

@@ -264,6 +264,37 @@ public:
 	}
 };
 
+class CeInternalData
+{
+public:
+	enum Kind
+	{
+		Kind_None,
+		Kind_File,
+		Kind_FindFileData,
+		Kind_Spawn
+	};
+
+public:
+	Kind mKind;
+	bool mReleased;
+
+	union
+	{
+		BfpFile* mFile;
+		BfpFindFileData* mFindFileData;
+		BfpSpawn* mSpawn;
+	};
+
+	CeInternalData()
+	{
+		mKind = Kind_None;
+		mReleased = false;
+	}
+
+	~CeInternalData();
+};
+
 enum CeFunctionKind
 {
 	CeFunctionKind_NotSet,
@@ -294,6 +325,21 @@ enum CeFunctionKind
 	CeFunctionKind_EmitMethodExit,
 	CeFunctionKind_EmitMixin,
 
+	CeFunctionKind_BfpFile_Close,
+	CeFunctionKind_BfpFile_Create,
+	CeFunctionKind_BfpFile_Flush,
+	CeFunctionKind_BfpFile_GetFileSize,
+	CeFunctionKind_BfpFile_Read,
+	CeFunctionKind_BfpFile_Release,
+	CeFunctionKind_BfpFile_Seek,
+	CeFunctionKind_BfpFile_Truncate,
+	CeFunctionKind_BfpFile_Write,
+	CeFunctionKind_BfpSpawn_Create,
+	CeFunctionKind_BfpSpawn_GetStdHandles,
+	CeFunctionKind_BfpSpawn_Kill,
+	CeFunctionKind_BfpSpawn_Release,
+	CeFunctionKind_BfpSpawn_WaitFor,
+
 	CeFunctionKind_BfpSystem_GetTimeStamp,
 	CeFunctionKind_Sleep,
 
@@ -328,7 +374,7 @@ enum CeFunctionKind
 	CeFunctionKind_Math_Sinh,
 	CeFunctionKind_Math_Sqrt,
 	CeFunctionKind_Math_Tan,
-	CeFunctionKind_Math_Tanh,
+	CeFunctionKind_Math_Tanh,	
 };
 
 class CeConstStructFixup
@@ -683,12 +729,40 @@ public:
 	BfIRValue mAppendSizeValue;
 };
 
+class CeRebuildKey
+{
+public:
+	enum Kind
+	{
+		Kind_None,
+		Kind_File
+	};
+
+public:
+	Kind mKind;
+	String mString;
+
+	bool operator==(const CeRebuildKey& other) const
+	{
+		return (mKind == other.mKind) && (mString == other.mString);
+	}
+};
+
+class CeRebuildValue
+{
+public:
+	union
+	{
+		uint64 mInt;
+	};
+};
+
 class CeEmitContext
 {
 public:
-	BfType* mType;
+	BfType* mType;	
 	BfMethodInstance* mMethodInstance;
-	Array<int32> mInterfaces;
+	Array<int32> mInterfaces;	
 	String mEmitData;
 	String mExitEmitData;
 	bool mFailed;
@@ -706,6 +780,25 @@ public:
 	}
 };
 
+class BfCeTypeInfo
+{
+public:
+	Dictionary<int, BfCeTypeEmitEntry> mOnCompileMap;
+	Dictionary<int, BfCeTypeEmitEntry> mTypeIFaceMap;
+	Array<int> mPendingInterfaces;
+	Dictionary<CeRebuildKey, CeRebuildValue> mRebuildMap;
+	Val128 mHash;
+	bool mFailed;
+	BfCeTypeInfo* mNext;
+
+public:
+	BfCeTypeInfo()
+	{
+		mFailed = false;
+		mNext = NULL;
+	}
+};
+
 class CeContext
 {
 public:
@@ -724,14 +817,15 @@ public:
 	Dictionary<Val128, addr_ce> mConstDataMap;	
 	HashSet<int> mStaticCtorExecSet;	
 	Dictionary<String, CeStaticFieldInfo> mStaticFieldMap;
-	Dictionary<void*, addr_ce> mMemToCtxMap;
+	Dictionary<int, CeInternalData*> mInternalDataMap;
+	int mCurHandleId;
 
 	BfMethodInstance* mCurMethodInstance;
 	BfType* mCurExpectingType;
 	BfAstNode* mCurTargetSrc;
 	BfModule* mCurModule;
 	CeFrame* mCurFrame;
-	CeEmitContext* mCurEmitContext;
+	CeEmitContext* mCurEmitContext;	
 
 public:
 	CeContext();
@@ -740,6 +834,7 @@ public:
 	BfError* Fail(const StringImpl& error);
 	BfError* Fail(const CeFrame& curFrame, const StringImpl& error);
 
+	void AddRebuild(const CeRebuildKey& key, const CeRebuildValue& value);
 	uint8* CeMalloc(int size);
 	bool CeFree(addr_ce addr);
 	addr_ce CeAllocArray(BfArrayType* arrayType, int count, addr_ce& elemsAddr);
@@ -791,7 +886,7 @@ public:
 	CeAppendAllocInfo* mAppendAllocInfo;
 	
 	CeContext* mCurContext;
-	CeEmitContext* mCurEmitContext;	
+	CeEmitContext* mCurEmitContext;
 	CeBuilder* mCurBuilder;
 	CeFunction* mPreparingFunction;		
 
@@ -838,3 +933,15 @@ public:
 };
 
 NS_BF_END
+
+namespace std
+{
+	template <>
+	struct hash<Beefy::CeRebuildKey>
+	{
+		size_t operator()(const Beefy::CeRebuildKey& key) const
+		{
+			return BeefHash<Beefy::String>()(key.mString) ^ (size_t)key.mKind;
+		}
+	};	
+}

+ 2 - 0
IDEHelper/Tests/Test0.txt

@@ -0,0 +1,2 @@
+Test
+0

+ 5 - 0
IDEHelper/Tests/src/Comptime.bf

@@ -229,6 +229,9 @@ namespace Tests
 				GC.Mark!((*ptr));
 			}
 		}
+
+		const String cTest0 = Compiler.ReadText("Test0.txt");
+
 		[Test]
 		public static void TestBasics()
 		{
@@ -269,6 +272,8 @@ namespace Tests
 			SerializationContext serCtx = scope .();
 			iSer.Serialize(serCtx);
 			Test.Assert(serCtx.mStr == "x 10\ny 2\n");
+
+			Test.Assert(cTest0 == "Test\n0");
 		}
 	}
 }