From 8f3060fd3ceb3efc048e6dc8e31b653ff5e1c8d9 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Wed, 13 Jan 2021 05:09:09 -0800 Subject: [PATCH] Comptime method reflection, method entry/exit emission --- BeefLibs/corlib/src/Attribute.bf | 3 +- BeefLibs/corlib/src/Compiler.bf | 30 +- .../corlib/src/Reflection/CEMethodInfo.bf | 153 +++++++++ .../corlib/src/Reflection/TypeInstance.bf | 22 ++ BeefLibs/corlib/src/Type.bf | 6 + IDE/src/ui/ClassViewPanel.bf | 7 +- IDEHelper/Compiler/BfExprEvaluator.cpp | 10 +- IDEHelper/Compiler/BfIRBuilder.cpp | 8 +- IDEHelper/Compiler/BfModule.cpp | 106 +++--- IDEHelper/Compiler/BfModule.h | 19 +- IDEHelper/Compiler/BfModuleTypeUtils.cpp | 283 +++++++++++++--- IDEHelper/Compiler/BfResolvedTypeUtils.cpp | 29 ++ IDEHelper/Compiler/BfResolvedTypeUtils.h | 1 + IDEHelper/Compiler/BfStmtEvaluator.cpp | 6 +- IDEHelper/Compiler/BfSystem.h | 13 + IDEHelper/Compiler/CeMachine.cpp | 304 +++++++++++++++++- IDEHelper/Compiler/CeMachine.h | 28 +- IDEHelper/Tests/src/Comptime.bf | 33 +- 18 files changed, 944 insertions(+), 117 deletions(-) create mode 100644 BeefLibs/corlib/src/Reflection/CEMethodInfo.bf diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf index c74fa94c..47a719c7 100644 --- a/BeefLibs/corlib/src/Attribute.bf +++ b/BeefLibs/corlib/src/Attribute.bf @@ -1,3 +1,4 @@ +using System.Reflection; namespace System { public struct Attribute @@ -542,6 +543,6 @@ namespace System interface IComptimeMethodApply { - void ApplyToMethod(Type type); + void ApplyToMethod(ComptimeMethodInfo methodInfo); } } diff --git a/BeefLibs/corlib/src/Compiler.bf b/BeefLibs/corlib/src/Compiler.bf index 052bee29..2ec01415 100644 --- a/BeefLibs/corlib/src/Compiler.bf +++ b/BeefLibs/corlib/src/Compiler.bf @@ -69,7 +69,10 @@ namespace System static extern void* Comptime_MethodBuilder_EmitStr(void* native, StringView str); static extern void* Comptime_CreateMethod(int32 typeId, StringView methodName, Type returnType, MethodFlags methodFlags); - static extern void Comptime_EmitDefinition(int32 typeId, StringView text); + static extern void Comptime_EmitTypeBody(int32 typeId, StringView text); + static extern void Comptime_EmitMethodEntry(int64 methodHandle, StringView text); + static extern void Comptime_EmitMethodExit(int64 methodHandle, StringView text); + static extern void Comptime_EmitMixin(StringView text); [Comptime(OnlyFromComptime=true)] public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags) @@ -80,9 +83,28 @@ namespace System } [Comptime(OnlyFromComptime=true)] - public static void EmitDefinition(Type owner, StringView text) + public static void EmitTypeBody(Type owner, StringView text) { - Comptime_EmitDefinition((.)owner.TypeId, text); - } + Comptime_EmitTypeBody((.)owner.TypeId, text); + } + + [Comptime(OnlyFromComptime=true)] + public static void EmitMethodEntry(ComptimeMethodInfo methodHandle, StringView text) + { + Comptime_EmitMethodEntry(methodHandle.mNativeMethodInstance, text); + } + + [Comptime(OnlyFromComptime=true)] + public static void EmitMethodExit(ComptimeMethodInfo methodHandle, StringView text) + { + Comptime_EmitMethodExit(methodHandle.mNativeMethodInstance, text); + } + + [Comptime] + public static void Mixin(StringView text) + { + if (Compiler.IsComptime) + Comptime_EmitMixin(text); + } } } diff --git a/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf b/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf new file mode 100644 index 00000000..bc68d801 --- /dev/null +++ b/BeefLibs/corlib/src/Reflection/CEMethodInfo.bf @@ -0,0 +1,153 @@ +using System.Diagnostics; +using System.Collections; + +namespace System.Reflection +{ + struct ComptimeMethodInfo + { + [CRepr, Packed] + public struct Info + { + public int32 mReturnTypeId; + public int32 mParamCount; + public MethodFlags mMethodFlags; + } + + [CRepr, Packed] + public struct ParamInfo + { + public int32 mParamTypeId; + public TypeInstance.ParamFlags mParamFlags; + public String mName; + } + + public int64 mNativeMethodInstance; + + public bool IsInitialized => true; + public StringView Name + { + get + { + if (Compiler.IsComptime) + Type.[Friend]Comptime_Method_GetName(mNativeMethodInstance); + return ""; + } + } + public int ParamCount + { + get + { + if (Compiler.IsComptime) + return Type.[Friend]Comptime_Method_GetInfo(mNativeMethodInstance).mParamCount; + return 0; + } + } + public bool IsConstructor => Name == "__BfCtor" || Name == "__BfStaticCtor"; + public bool IsDestructor => Name == "__BfStaticDtor" || Name == "__BfStaticDtor"; + public Type ReturnType + { + get + { + if (Compiler.IsComptime) + Type.[Friend]GetType((.)Type.[Friend]Comptime_Method_GetInfo(mNativeMethodInstance).mReturnTypeId); + return null; + } + } + + public this(int64 nativeMethodInstance) + { + mNativeMethodInstance = nativeMethodInstance; + } + + public Type GetParamType(int paramIdx) + { + if (Compiler.IsComptime) + return Type.[Friend]GetType((.)Type.[Friend]Comptime_Method_GetParamInfo(mNativeMethodInstance, (.)paramIdx).mParamTypeId); + return null; + } + + public StringView GetParamName(int paramIdx) + { + if (Compiler.IsComptime) + return Type.[Friend]Comptime_Method_GetParamInfo(mNativeMethodInstance, (.)paramIdx).mName; + return default; + } + + public override void ToString(String strBuffer) + { + if (Compiler.IsComptime) + { + String str = Type.[Friend]Comptime_Method_ToString(mNativeMethodInstance); + strBuffer.Append(str); + } + } + + public struct Enumerator : IEnumerator + { + BindingFlags mBindingFlags; + TypeInstance mTypeInstance; + int32 mIdx; + int32 mCount; + + public this(TypeInstance typeInst, BindingFlags bindingFlags) + { + //Debug.WriteLine($"this {typeInst}"); + + mTypeInstance = typeInst; + mBindingFlags = bindingFlags; + mIdx = -1; + if ((mTypeInstance == null) || (!Compiler.IsComptime)) + mCount = 0; + else + mCount = Type.[Friend]Comptime_GetMethodCount((.)mTypeInstance.TypeId); + } + + public void Reset() mut + { + mIdx = -1; + } + + public void Dispose() + { + } + + public bool MoveNext() mut + { + if (mTypeInstance == null) + return false; + + for (;;) + { + mIdx++; + if (mIdx == mCount) + return false; + + int64 nativeMethodHandle = Type.[Friend]Comptime_GetMethod((int32)mTypeInstance.TypeId, mIdx); + let info = Type.[Friend]Comptime_Method_GetInfo(nativeMethodHandle); + + bool matches = (mBindingFlags.HasFlag(BindingFlags.Static) && (info.mMethodFlags.HasFlag(.Static))); + matches |= (mBindingFlags.HasFlag(BindingFlags.Instance) && (!info.mMethodFlags.HasFlag(.Static))); + if (matches) + break; + } + return true; + } + + public ComptimeMethodInfo Current + { + get + { + int64 nativeMethodHandle = Type.[Friend]Comptime_GetMethod((int32)mTypeInstance.TypeId, mIdx); + return ComptimeMethodInfo(nativeMethodHandle); + } + } + + public Result GetNext() mut + { + if (!MoveNext()) + return .Err; + return Current; + } + } + } +} diff --git a/BeefLibs/corlib/src/Reflection/TypeInstance.bf b/BeefLibs/corlib/src/Reflection/TypeInstance.bf index 2c0e16de..eefc5f0a 100644 --- a/BeefLibs/corlib/src/Reflection/TypeInstance.bf +++ b/BeefLibs/corlib/src/Reflection/TypeInstance.bf @@ -16,6 +16,12 @@ namespace System return MethodInfo.Enumerator(null, bindingFlags); } + [Comptime] + public virtual ComptimeMethodInfo.Enumerator GetMethods(BindingFlags bindingFlags = cDefaultLookup) + { + return ComptimeMethodInfo.Enumerator(null, bindingFlags); + } + public virtual Result GetMethod(StringView methodName, BindingFlags bindingFlags = cDefaultLookup) { MethodInfo matched = default; @@ -40,6 +46,16 @@ namespace System return .Err(.NoResults); } + [Comptime] + public virtual Result GetMethod(int methodIdx) + { + int64 nativeMethod = Comptime_GetMethod((.)TypeId, (.)methodIdx); + if (nativeMethod == 0) + return .Err(.NoResults); + + return ComptimeMethodInfo(nativeMethod); + } + public virtual Result CreateObject() { return .Err; @@ -66,6 +82,12 @@ namespace System.Reflection return MethodInfo.Enumerator(this, bindingFlags); } + [Comptime] + public override ComptimeMethodInfo.Enumerator GetMethods(BindingFlags bindingFlags = cDefaultLookup) + { + return ComptimeMethodInfo.Enumerator(this, bindingFlags); + } + public override Result GetMethod(int methodIdx) { if ((methodIdx < 0) || (methodIdx >= mMethodDataCount)) diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index fb6cbed9..99855f97 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -475,6 +475,12 @@ namespace System static extern Type Comptime_GetTypeByName(StringView name); static extern Type Comptime_GetSpecializedType(Type unspecializedType, Span typeArgs); static extern bool Comptime_Type_GetCustomAttribute(int32 typeId, int32 attributeId, void* dataPtr); + static extern int32 Comptime_GetMethodCount(int32 typeId); + static extern int64 Comptime_GetMethod(int32 typeId, int32 methodIdx); + static extern String Comptime_Method_ToString(int64 methodHandle); + static extern String Comptime_Method_GetName(int64 methodHandle); + static extern ComptimeMethodInfo.Info Comptime_Method_GetInfo(int64 methodHandle); + static extern ComptimeMethodInfo.ParamInfo Comptime_Method_GetParamInfo(int64 methodHandle, int32 paramIdx); protected static Type GetType(TypeId typeId) { diff --git a/IDE/src/ui/ClassViewPanel.bf b/IDE/src/ui/ClassViewPanel.bf index c664e109..39e7a378 100644 --- a/IDE/src/ui/ClassViewPanel.bf +++ b/IDE/src/ui/ClassViewPanel.bf @@ -777,8 +777,11 @@ namespace IDE.ui if (itr.GetNext() case .Ok(let fileName)) { entry.mFile = new String(fileName); - entry.mLine = int.Parse(itr.GetNext()).Get(); - entry.mColumn = int.Parse(itr.GetNext()).Get(); + if (!entry.mFile.IsEmpty) + { + entry.mLine = int.Parse(itr.GetNext()).Get(); + entry.mColumn = int.Parse(itr.GetNext()).Get(); + } } } } diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 414d26ab..a3998adc 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -1111,6 +1111,11 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp RETURN_BETTER_OR_WORSE(newMethodDef->mDeclaringType == activeDef, prevMethodDef->mDeclaringType == activeDef); RETURN_BETTER_OR_WORSE(newMethodDef->mDeclaringType->IsExtension(), prevMethodDef->mDeclaringType->IsExtension()); RETURN_BETTER_OR_WORSE(newMethodDef->mIsMutating, prevMethodDef->mIsMutating); + if (newMethodDef->mHasComptime != prevMethodDef->mHasComptime) + { + bool isComptime = (mModule->mIsComptimeModule) || ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0); + RETURN_BETTER_OR_WORSE(newMethodDef->mHasComptime == isComptime, prevMethodDef->mHasComptime == isComptime); + } RETURN_RESULTS; } @@ -5189,10 +5194,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* { // We didn't properly resolve this so queue for a rebuild later mModule->DeferRebuildType(mModule->mCurTypeInstance); - } - - doConstReturn = true; + } } + doConstReturn = true; } } else if (mModule->mIsComptimeModule) diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index c28b6f01..16b62f9d 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -1989,6 +1989,12 @@ void BfIRBuilder::Write(const BfIRValue& irValue) Write(MapType(typeofConst->mType, BfIRPopulateType_Identity)); } break; + case (int)BfConstType_Undef: + { + auto undefConst = (BfConstantUndef*)constant; + Write(undefConst->mType); + } + break; case (int)BfConstType_TypeOf_WithData: { auto typeofConst = (BfTypeOf_WithData_Const*)constant; @@ -4478,7 +4484,7 @@ BfIRValue BfIRBuilder::CreateGlobalVariable(BfIRType varType, bool isConstant, B retVal.mId = chunkId; NEW_CMD_INSERTED_IRVALUE; return retVal; - } + } auto irValue = BfIRValue(BfIRValueFlags_Const, chunkId); return irValue; diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 23ae843b..8837efc6 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2774,7 +2774,7 @@ bool BfModule::IsSkippingExtraResolveChecks() } BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPersistent, bool deferError) -{ +{ BP_ZONE("BfModule::Fail"); if (mIgnoreErrors) @@ -2786,7 +2786,7 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers } if (!mReportErrors) - { + { mCompiler->mPassInstance->SilentFail(); return NULL; } @@ -2798,6 +2798,14 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers if (mIsComptimeModule) { + if ((mCompiler->mCEMachine->mCurContext != NULL) && (mCompiler->mCEMachine->mCurContext->mCurTargetSrc != NULL)) + { + BfError* bfError = mCompiler->mPassInstance->Fail("Comptime method generation had errors", mCompiler->mCEMachine->mCurContext->mCurTargetSrc); + if (bfError != NULL) + mCompiler->mPassInstance->MoreInfo(error, refNode); + return bfError; + } + mHadBuildError = true; return NULL; } @@ -2925,6 +2933,14 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers if (!mHadBuildError) mHadBuildError = true; + if ((mCurMethodState != NULL) && (mCurMethodState->mEmitRefNode != NULL)) + { + BfError* bfError = mCompiler->mPassInstance->Fail("Emitted code had errors", mCurMethodState->mEmitRefNode); + if (bfError != NULL) + mCompiler->mPassInstance->MoreInfo(errorString, refNode); + return bfError; + } + // Check mixins { auto checkMethodInstance = mCurMethodState; @@ -2934,7 +2950,8 @@ BfError* BfModule::Fail(const StringImpl& error, BfAstNode* refNode, bool isPers if (rootMixinState != NULL) { BfError* bfError = mCompiler->mPassInstance->Fail(StrFormat("Failed to inject mixin '%s'", MethodToString(rootMixinState->mMixinMethodInstance).c_str()), rootMixinState->mSource); - mCompiler->mPassInstance->MoreInfo(errorString, refNode); + if (bfError != NULL) + mCompiler->mPassInstance->MoreInfo(errorString, refNode); auto mixinState = checkMethodInstance->mMixinState; while ((mixinState != NULL) && (mixinState->mPrevMixinState != NULL)) @@ -3043,6 +3060,14 @@ BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* re return NULL; } + if ((mCurMethodState != NULL) && (mCurMethodState->mEmitRefNode != NULL)) + { + BfError* bfError = mCompiler->mPassInstance->Warn(warningNum, "Emitted code had errors", mCurMethodState->mEmitRefNode); + if (bfError != NULL) + mCompiler->mPassInstance->MoreInfo(warning, refNode); + return bfError; + } + BfError* bfError; if (refNode != NULL) bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength()); @@ -4973,6 +4998,8 @@ BfIRValue BfModule::CreateTypeDataRef(BfType* type) return mBfIRBuilder->Comptime_GetReflectType(type->mTypeId, mBfIRBuilder->MapType(typeTypeInst)); } + PopulateType(type); + BfIRValue globalVariable; BfIRValue* globalVariablePtr = NULL; @@ -4997,6 +5024,11 @@ BfIRValue BfModule::CreateTypeDataRef(BfType* type) BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type, this); } + if (typeDataName == "?sBfTypeData@Zoing@BeefTest@bf@@2HA") + { + NOP; + } + BfLogSysM("Creating TypeData %s\n", typeDataName.c_str()); globalVariable = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapTypeInst(typeTypeInst, BfIRPopulateType_Full), true, BfIRLinkageType_External, BfIRValue(), typeDataName); @@ -5110,6 +5142,11 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type, mContext->mScratchModule); } + if (typeDataName == "?sBfTypeData@@bf@@2HA") + { + NOP; + } + int typeCode = BfTypeCode_None; if (typeInstance != NULL) @@ -6401,6 +6438,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin auto methodDef = typeDef->mMethods[methodIdx]; if (methodDef->mIsNoReflect) continue; + if (methodDef->mHasComptime) + continue; auto defaultMethod = methodInstanceGroup->mDefault; if (defaultMethod == NULL) @@ -6480,44 +6519,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin } BfIRValue methodNameConst = GetStringObjectValue(methodDef->mName, true); - - enum MethodFlags - { - MethodFlags_Protected = 3, - MethodFlags_Public = 6, - MethodFlags_Static = 0x10, - MethodFlags_Virtual = 0x40, - MethodFlags_StdCall = 0x1000, - MethodFlags_FastCall = 0x2000, - MethodFlags_ThisCall = 0x3000, - MethodFlags_Mutating = 0x4000, - MethodFlags_Constructor = 0x8000, - }; - - MethodFlags methodFlags = (MethodFlags)0; - - if (methodDef->mProtection == BfProtection_Protected) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_Protected); - if (methodDef->mProtection == BfProtection_Public) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_Public); - if (methodDef->mIsStatic) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_Static); - if ((methodDef->mIsVirtual) || (moduleMethodInstance.mMethodInstance->mVirtualTableIdx != -1)) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_Virtual); - if (methodDef->mCallingConvention == BfCallingConvention_Fastcall) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_FastCall); - if (methodDef->mIsMutating) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_Mutating); - if (methodDef->mMethodType == BfMethodType_Ctor) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_Constructor); - - auto callingConvention = GetIRCallingConvention(defaultMethod); - if (callingConvention == BfIRCallingConv_ThisCall) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_ThisCall); - else if (callingConvention == BfIRCallingConv_StdCall) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_StdCall); - else if (callingConvention == BfIRCallingConv_FastCall) - methodFlags = (MethodFlags)(methodFlags | MethodFlags_FastCall); + + BfMethodFlags methodFlags = moduleMethodInstance.mMethodInstance->GetMethodFlags(); int customAttrIdx = _HandleCustomAttrs(methodCustomAttributes); @@ -9830,8 +9833,11 @@ BfMethodInstance* BfModule::GetUnspecializedMethodInstance(BfMethodInstance* met return methodInstance; if ((owner->IsDelegateFromTypeRef()) || + (owner->IsFunctionFromTypeRef()) || (owner->IsTuple())) + { return methodInstance; + } auto genericType = (BfTypeInstance*)owner; if ((genericType->IsUnspecializedType()) && (!genericType->IsUnspecializedTypeVariation())) @@ -10451,11 +10457,16 @@ StringT<128> BfModule::MethodToString(BfMethodInstance* methodInst, BfMethodName return methodName; } -void BfModule::pv(BfType* type) +void BfModule::pt(BfType* type) { OutputDebugStrF("%s\n", TypeToString(type).c_str()); } +void BfModule::pm(BfMethodInstance* type) +{ + OutputDebugStrF("%s\n", MethodToString(type).c_str()); +} + static void AddAttributeTargetName(BfAttributeTargets& flagsLeft, BfAttributeTargets checkFlag, String& str, String addString) { if ((flagsLeft & checkFlag) == 0) @@ -12077,9 +12088,11 @@ bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstan return false; } else if (methodA->mMethodDef->mName != methodB->mMethodDef->mName) - return false; + return false; if (methodA->mMethodDef->mCheckedKind != methodB->mMethodDef->mCheckedKind) return false; + if (methodA->mMethodDef->mHasComptime != methodB->mMethodDef->mHasComptime) + return false; if ((methodA->mMethodDef->mMethodType == BfMethodType_Mixin) != (methodB->mMethodDef->mMethodType == BfMethodType_Mixin)) return false; @@ -14270,7 +14283,8 @@ void BfModule::EmitDeferredScopeCalls(bool useSrcPositions, BfScopeData* scopeDa while (deferredCallEntry != NULL) { if (deferredCallEntry->mDeferredBlock != NULL) - { + { + SetAndRestoreValue prevCustomAttribute(mCurMethodState->mEmitRefNode, deferredCallEntry->mEmitRefNode); VisitEmbeddedStatement(deferredCallEntry->mDeferredBlock, NULL, BfEmbeddedStatementFlags_IsDeferredBlock); } deferredCallEntry = deferredCallEntry->mNext; @@ -19583,6 +19597,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) isExpressionBody = true; } + DoCEEmit(methodInstance); + if (auto fieldDtorBody = BfNodeDynCast(methodDef->mBody)) { while (fieldDtorBody != NULL) @@ -19790,7 +19806,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) } // Avoid linking any internal funcs that were just supposed to be comptime-accessible - if (((methodInstance->mComptimeFlags & BfComptimeFlag_OnlyFromComptime) != 0) && (!mIsComptimeModule)) + if ((methodDef->mHasComptime) && (!mIsComptimeModule)) wantsRemoveBody = true; if ((hasExternSpecifier) && (!skipBody)) diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 4bd35c13..6aa82d2b 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -273,6 +273,7 @@ public: SizedArray mScopeArgs; Array mCaptures; BfBlock* mDeferredBlock; + BfAstNode* mEmitRefNode; int64 mBlockId; int mHandlerCount; bool mBypassVirtual; @@ -292,6 +293,7 @@ public: mNext = NULL; mSrcNode = NULL; mDeferredBlock = NULL; + mEmitRefNode = NULL; mBlockId = -1; mHandlerCount = 0; mArgsNeedLoad = false; @@ -991,6 +993,7 @@ public: BfScopeData* mCurScope; BfScopeData* mTailScope; // Usually equals mCurScope BfScopeData* mOverrideScope; + BfAstNode* mEmitRefNode; TempKind mTempKind; // Used for var inference, etc bool mInDeferredBlock; bool mHadReturn; @@ -1024,6 +1027,7 @@ public: mHeadScope.mIsScopeHead = true; mCurScope = &mHeadScope; mTailScope = &mHeadScope; + mEmitRefNode = NULL; mOverrideScope = NULL; mHadReturn = false; mLeftBlockUncond = false; @@ -1370,6 +1374,12 @@ public: #define BFMODULE_FATAL(module, msg) (module)->FatalError((msg), __FILE__, __LINE__) +struct BfCEParseContext +{ + int mFailIdx; + int mWarnIdx; +}; + class BfModule : public BfStructuralVisitor { public: @@ -1523,7 +1533,8 @@ public: StringT<128> TypeToString(BfType* resolvedType, BfTypeNameFlags typeNameFlags, Array* genericMethodParamNameOverrides = NULL); void DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameFlags typeNameFlags = BfTypeNameFlags_None, Array* genericMethodParamNameOverrides = NULL); StringT<128> MethodToString(BfMethodInstance* methodInst, BfMethodNameFlags methodNameFlags = BfMethodNameFlag_ResolveGenericParamNames, BfTypeVector* typeGenericArgs = NULL, BfTypeVector* methodGenericArgs = NULL); - void pv(BfType* type); + void pt(BfType* type); + void pm(BfMethodInstance* type); void CurrentAddToConstHolder(BfIRValue& irVal); void ClearConstData(); BfTypedValue GetTypedValueFromConstant(BfConstant* constant, BfIRConstHolder* constHolder, BfType* wantType); @@ -1562,7 +1573,7 @@ public: void EmitDefaultReturn(); void EmitDeferredCall(BfModuleMethodInstance moduleMethodInstance, SizedArrayImpl& llvmArgs, BfDeferredBlockFlags flags = BfDeferredBlockFlag_None); bool AddDeferredCallEntry(BfDeferredCallEntry* deferredCallEntry, BfScopeData* scope); - void AddDeferredBlock(BfBlock* block, BfScopeData* scope, Array* captures = NULL); + BfDeferredCallEntry* AddDeferredBlock(BfBlock* block, BfScopeData* scope, Array* captures = NULL); BfDeferredCallEntry* AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scope, BfAstNode* srcNode = NULL, bool bypassVirtual = false, bool doNullCheck = false); void EmitDeferredCall(BfDeferredCallEntry& deferredCallEntry, bool moveBlocks); void EmitDeferredCallProcessor(SLIList& callEntries, BfIRValue callTail); @@ -1698,10 +1709,14 @@ public: void SetTypeOptions(BfTypeInstance* typeInstance); BfModuleOptions GetModuleOptions(); BfCheckedKind GetDefaultCheckedKind(); + void FinishCEParseContext(BfAstNode* refNode, BfTypeInstance* typeInstance, BfCEParseContext* ceParseContext); + BfCEParseContext CEEmitParse(BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& src); void UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& ctxString, BfAstNode* refNode); void HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* typeInst, BfCustomAttributes* customAttributes, HashSet foundAttributes); + void CEMixin(BfAstNode* refNode, const StringImpl& src); void ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* typeInst, BfCEOnCompileKind onCompileKind); void DoCEEmit(BfTypeInstance* typeInstance, bool& hadNewMembers); + void DoCEEmit(BfMethodInstance* methodInstance); void DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateType = BfPopulateType_Data); static BfModule* GetModuleFor(BfType* type); void DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 2ef0418a..f895b42c 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -1933,24 +1933,11 @@ void BfModule::SetTypeOptions(BfTypeInstance* typeInstance) typeInstance->mTypeOptionsIdx = GenerateTypeOptions(typeInstance->mCustomAttributes, typeInstance, true); } -void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& ctxString, BfAstNode* refNode) +BfCEParseContext BfModule::CEEmitParse(BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& src) { - if (ceEmitContext->mEmitData.IsEmpty()) - return; - - int prevFailIdx = mCompiler->mPassInstance->mFailedIdx; - int prevWarnIdx = mCompiler->mPassInstance->mWarnIdx; - - String src; - - if (activeTypeDef->mEmitParser != NULL) - src += "\n\n"; - - src += "// Code emission in "; - src += ctxString; - src += "\n\n"; - src += ceEmitContext->mEmitData; - ceEmitContext->mEmitData.Clear(); + BfCEParseContext ceParseContext; + ceParseContext.mFailIdx = mCompiler->mPassInstance->mFailedIdx; + ceParseContext.mWarnIdx = mCompiler->mPassInstance->mWarnIdx; bool createdParser = false; int startSrcIdx = 0; @@ -1972,7 +1959,7 @@ void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeIn if (activeTypeDef->mPartialIdx != -1) parser->mFileName + StrFormat(":%d", activeTypeDef->mPartialIdx); - parser->mFileName += StrFormat(".bf|%d", typeInstance->mRevision); + parser->mFileName += StrFormat(".bf|%d", typeInstance->mRevision); activeTypeDef->mEmitParser = parser; parser->mRefCount++; parser->SetSource(src.c_str(), src.mLength); @@ -1986,9 +1973,49 @@ void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeIn activeTypeDef->mEmitParser->mParserData->mSrcLength = activeTypeDef->mEmitParser->mSrcLength; } - activeTypeDef->mEmitParser->Parse(mCompiler->mPassInstance); + activeTypeDef->mEmitParser->Parse(mCompiler->mPassInstance); activeTypeDef->mEmitParser->FinishSideNodes(); - + + if (createdParser) + { + AutoCrit crit(mSystem->mDataLock); + mSystem->mParsers.Add(activeTypeDef->mEmitParser); + } + + return ceParseContext; +} + +void BfModule::FinishCEParseContext(BfAstNode* refNode, BfTypeInstance* typeInstance, BfCEParseContext* ceParseContext) +{ + if ((ceParseContext->mFailIdx != mCompiler->mPassInstance->mFailedIdx) && (refNode != NULL)) + Fail("Emitted code had errors", refNode); + else if ((ceParseContext->mWarnIdx != mCompiler->mPassInstance->mWarnIdx) && (refNode != NULL)) + Warn(0, "Emitted code had warnings", refNode); + else if ((ceParseContext->mFailIdx != mCompiler->mPassInstance->mFailedIdx) || + (ceParseContext->mWarnIdx != mCompiler->mPassInstance->mWarnIdx)) + { + AddFailType(typeInstance); + } +} + +void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfTypeDef* activeTypeDef, const StringImpl& ctxString, BfAstNode* refNode) +{ + if (ceEmitContext->mEmitData.IsEmpty()) + return; + + String src; + + if (activeTypeDef->mEmitParser != NULL) + src += "\n\n"; + + src += "// Code emission in "; + src += ctxString; + src += "\n\n"; + src += ceEmitContext->mEmitData; + ceEmitContext->mEmitData.Clear(); + + BfCEParseContext ceParseContext = CEEmitParse(typeInstance, activeTypeDef, src); + auto typeDeclaration = activeTypeDef->mEmitParser->mAlloc->Alloc(); BfReducer bfReducer; @@ -2008,21 +2035,7 @@ void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeIn defBuilder.DoVisitChild(typeDeclaration->mDefineNode); defBuilder.FinishTypeDef(typeInstance->mTypeDef->mTypeCode == BfTypeCode_Enum); - if (createdParser) - { - AutoCrit crit(mSystem->mDataLock); - mSystem->mParsers.Add(activeTypeDef->mEmitParser); - } - - if ((prevFailIdx != mCompiler->mPassInstance->mFailedIdx) && (refNode != NULL)) - Fail("Emitted code had errors", refNode); - else if ((prevWarnIdx != mCompiler->mPassInstance->mWarnIdx) && (refNode != NULL)) - Warn(0, "Emitted code had warnings", refNode); - else if ((prevFailIdx != mCompiler->mPassInstance->mFailedIdx) || - (prevWarnIdx != mCompiler->mPassInstance->mWarnIdx)) - { - AddFailType(typeInstance); - } + FinishCEParseContext(refNode, typeInstance, &ceParseContext); } void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfCustomAttributes* customAttributes, HashSet foundAttributes) @@ -2066,7 +2079,12 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* auto result = ceContext->Call(customAttribute.mRef, this, methodInstance, args, CeEvalFlags_None, NULL); - if (!ceEmitContext->mEmitData.IsEmpty()) + if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) + { + // We populated before we could finish + AssertErrorState(); + } + else if (!ceEmitContext->mEmitData.IsEmpty()) { String ctxStr = "comptime ApplyToType of "; ctxStr += TypeToString(attrType); @@ -2082,6 +2100,76 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* } } +void BfModule::CEMixin(BfAstNode* refNode, const StringImpl& code) +{ + auto activeTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType; + + String src; + if (activeTypeDef->mEmitParser != NULL) + src += "\n\n"; + src += "// Code emission in "; + src += MethodToString(mCurMethodInstance); + src += "\n"; + src += code; + + BfReducer bfReducer; + bfReducer.mSource = activeTypeDef->mEmitParser; + bfReducer.mPassInstance = mCompiler->mPassInstance; + bfReducer.mSystem = mSystem; + bfReducer.mCurTypeDecl = activeTypeDef->mTypeDeclaration; + bfReducer.mCurMethodDecl = BfNodeDynCast(mCurMethodInstance->mMethodDef->mMethodDeclaration); + + SetAndRestoreValue prevCustomAttribute(mCurMethodState->mEmitRefNode, refNode); + + EmitEnsureInstructionAt(); + bool wantsDIData = (mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo); + mBfIRBuilder->SaveDebugLocation(); + + BfCEParseContext ceParseContext = CEEmitParse(mCurTypeInstance, activeTypeDef, src); + bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; + bfReducer.HandleBlock(activeTypeDef->mEmitParser->mRootNode, false); + + SetAndRestoreValue prevInlinedAt(mCurMethodState->mCurScope->mDIInlinedAt); + SetAndRestoreValue prevDIScope(mCurMethodState->mCurScope->mDIScope); + SetAndRestoreValue prevAltDIFile(mCurMethodState->mCurScope->mAltDIFile); + + if (wantsDIData) + { + llvm::SmallVector diParams; + diParams.push_back(mBfIRBuilder->DbgGetType(GetPrimitiveType(BfTypeCode_None))); + BfIRMDNode diFuncType = mBfIRBuilder->DbgCreateSubroutineType(diParams); + + //int defLine = mModule->mCurFilePosition.mCurLine; + + int flags = 0; + mCurMethodState->mCurScope->mDIInlinedAt = mBfIRBuilder->DbgGetCurrentLocation(); + + // We used to have the "def" line be the inlining position, but the linker we de-duplicate instances of these functions without regard to their unique line + // definitions, so we need to be consistent and use the actual line + UpdateSrcPos(activeTypeDef->mEmitParser->mRootNode, BfSrcPosFlag_NoSetDebugLoc); + int defLine = mCurFilePosition.mCurLine; + auto diParentType = mBfIRBuilder->DbgGetTypeInst(mCurTypeInstance); + if (!mBfIRBuilder->mIgnoreWrites) + { + String methodName = "Comptime_Mixin"; + mCurMethodState->mCurScope->mDIScope = mBfIRBuilder->DbgCreateFunction(diParentType, methodName, "", mCurFilePosition.mFileInstance->mDIFile, + defLine + 1, diFuncType, false, true, mCurFilePosition.mCurLine + 1, flags, false, BfIRValue()); + mCurMethodState->mCurScope->mAltDIFile = mCurFilePosition.mFileInstance->mDIFile; + } + } + + UpdateSrcPos(activeTypeDef->mEmitParser->mRootNode); + + SetIllegalSrcPos(); + + Visit(activeTypeDef->mEmitParser->mRootNode); + + mBfIRBuilder->RestoreDebugLocation(); + mBfIRBuilder->DupDebugLocation(); + + FinishCEParseContext(refNode, mCurTypeInstance, &ceParseContext); +} + void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfCEOnCompileKind onCompileKind) { HashSet foundAttributes; @@ -2166,7 +2254,12 @@ void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* auto methodInstance = GetRawMethodInstanceAtIdx(typeInstance, methodDef->mIdx); auto result = mCompiler->mCEMachine->Call(methodDef->GetRefNode(), this, methodInstance, {}, (CeEvalFlags)(CeEvalFlags_PersistantError | CeEvalFlags_DeferIfNotOnlyError), NULL); - if (!ceEmitContext->mEmitData.IsEmpty()) + if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) + { + // We populated before we could finish + AssertErrorState(); + } + else if (!ceEmitContext->mEmitData.IsEmpty()) { String ctxStr = "OnCompile execution of "; ctxStr += MethodToString(methodInstance); @@ -2192,8 +2285,8 @@ void BfModule::DoCEEmit(BfTypeInstance* typeInstance, bool& hadNewMembers) CeEmitContext emitContext; emitContext.mType = typeInstance; - ExecuteCEOnCompile(&emitContext, typeInstance, BfCEOnCompileKind_TypeInit); - + ExecuteCEOnCompile(&emitContext, typeInstance, BfCEOnCompileKind_TypeInit); + if ((startMethodCount != typeInstance->mTypeDef->mMethods.mSize) || (startFieldCount != typeInstance->mTypeDef->mFields.mSize) || (startPropCount != typeInstance->mTypeDef->mProperties.mSize)) @@ -2203,6 +2296,113 @@ void BfModule::DoCEEmit(BfTypeInstance* typeInstance, bool& hadNewMembers) } } +void BfModule::DoCEEmit(BfMethodInstance* methodInstance) +{ + auto customAttributes = methodInstance->GetCustomAttributes(); + if (customAttributes == NULL) + return; + + auto typeInstance = methodInstance->GetOwner(); + + CeEmitContext ceEmitContext; + ceEmitContext.mMethodInstance = methodInstance; + + BfTypeInstance* iComptimeMethodApply = NULL; + for (auto& customAttribute : customAttributes->mAttributes) + { + auto attrType = customAttribute.mType; + PopulateType(attrType, BfPopulateType_DataAndMethods); + if (attrType->mDefineState < BfTypeDefineState_DefinedAndMethodsSlotted) + continue; + + for (auto& ifaceEntry : attrType->mInterfaces) + { + if (iComptimeMethodApply == NULL) + iComptimeMethodApply = ResolveTypeDef(mCompiler->mIComptimeMethodApply)->ToTypeInstance(); + if (ifaceEntry.mInterfaceType != iComptimeMethodApply) + continue; + +// if (!foundAttributes.Add(attrType)) +// continue; + + BfMethodInstance* applyMethodInstance = attrType->mInterfaceMethodTable[ifaceEntry.mStartInterfaceTableIdx].mMethodRef; + if (applyMethodInstance == NULL) + continue; + + SetAndRestoreValue prevEmitContext(mCompiler->mCEMachine->mCurEmitContext, &ceEmitContext); + auto ceContext = mCompiler->mCEMachine->AllocContext(); + + BfIRValue attrVal = ceContext->CreateAttribute(customAttribute.mRef, this, typeInstance->mConstHolder, &customAttribute); + + SizedArray args; + if (!attrType->IsValuelessType()) + args.Add(attrVal); + args.Add(mBfIRBuilder->CreateConst(BfTypeCode_UInt64, (uint64)(intptr)methodInstance)); + mCompiler->mCEMachine->mMethodInstanceSet.Add(methodInstance); + + //TESTING +// mCompiler->mCEMachine->ReleaseContext(ceContext); +// ceContext = mCompiler->mCEMachine->AllocContext(); +// ceContext->mMemory.mSize = ceContext->mMemory.mAllocSize; + + auto activeTypeDef = typeInstance->mTypeDef; + auto result = ceContext->Call(customAttribute.mRef, this, applyMethodInstance, args, CeEvalFlags_None, NULL); + + if ((!ceEmitContext.mEmitData.IsEmpty()) || (!ceEmitContext.mExitEmitData.IsEmpty())) + { + String src; + src += "// Code emission in comptime ApplyToMethod of "; + src += TypeToString(attrType); + src += " to "; + src += MethodToString(methodInstance); + src += " "; + src += customAttribute.mRef->LocationToString(); + src += "\n"; + + BfReducer bfReducer; + bfReducer.mSource = activeTypeDef->mEmitParser; + bfReducer.mPassInstance = mCompiler->mPassInstance; + bfReducer.mSystem = mSystem; + bfReducer.mCurTypeDecl = activeTypeDef->mTypeDeclaration; + bfReducer.mCurMethodDecl = BfNodeDynCast(methodInstance->mMethodDef->mMethodDeclaration); + + if (!ceEmitContext.mEmitData.IsEmpty()) + { + SetAndRestoreValue prevCustomAttribute(mCurMethodState->mEmitRefNode, customAttribute.mRef); + + String entrySrc = src; + if (activeTypeDef->mEmitParser != NULL) + entrySrc += "\n\n"; + entrySrc += src; + entrySrc += ceEmitContext.mEmitData; + BfCEParseContext ceParseContext = CEEmitParse(typeInstance, activeTypeDef, entrySrc); + bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; + bfReducer.HandleBlock(activeTypeDef->mEmitParser->mRootNode, false); + Visit(activeTypeDef->mEmitParser->mRootNode); + FinishCEParseContext(customAttribute.mRef, typeInstance, &ceParseContext); + } + + if (!ceEmitContext.mExitEmitData.IsEmpty()) + { + String exitSrc; + if (activeTypeDef->mEmitParser != NULL) + exitSrc += "\n\n"; + exitSrc += src; + exitSrc += ceEmitContext.mExitEmitData; + BfCEParseContext ceParseContext = CEEmitParse(typeInstance, activeTypeDef, exitSrc); + bfReducer.mAlloc = activeTypeDef->mEmitParser->mAlloc; + bfReducer.HandleBlock(activeTypeDef->mEmitParser->mRootNode, false); + auto deferredBlock = AddDeferredBlock(activeTypeDef->mEmitParser->mRootNode, &mCurMethodState->mHeadScope); + deferredBlock->mEmitRefNode = customAttribute.mRef; + FinishCEParseContext(customAttribute.mRef, typeInstance, &ceParseContext); + } + } + + mCompiler->mCEMachine->ReleaseContext(ceContext); + } + } +} + void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateType) { auto typeInstance = resolvedTypeRef->ToTypeInstance(); @@ -6316,6 +6516,11 @@ BfPointerType* BfModule::CreatePointerType(BfTypeReference* typeRef) BfType* BfModule::ResolveTypeDef(BfTypeDef* typeDef, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags) { + if (typeDef->mTypeDeclaration == NULL) + { + BF_ASSERT(!typeDef->mIsDelegate && !typeDef->mIsFunction); + } + //BF_ASSERT(typeDef->mTypeCode != BfTypeCode_Extension); BF_ASSERT(!typeDef->mIsPartial || typeDef->mIsCombinedPartial); diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index da7d2c50..b01758ad 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -630,6 +630,35 @@ BfImportKind BfMethodInstance::GetImportKind() return BfMethodDef::GetImportKindFromPath(*filePath); } +BfMethodFlags BfMethodInstance::GetMethodFlags() +{ + BfMethodFlags methodFlags = (BfMethodFlags)0; + + if (mMethodDef->mProtection == BfProtection_Protected) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_Protected); + if (mMethodDef->mProtection == BfProtection_Public) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_Public); + if (mMethodDef->mIsStatic) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_Static); + if ((mMethodDef->mIsVirtual) || (mVirtualTableIdx != -1)) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_Virtual); + if (mMethodDef->mCallingConvention == BfCallingConvention_Fastcall) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_FastCall); + if (mMethodDef->mIsMutating) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_Mutating); + if (mMethodDef->mMethodType == BfMethodType_Ctor) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_Constructor); + + auto callingConvention = GetOwner()->mModule->GetIRCallingConvention(this); + if (callingConvention == BfIRCallingConv_ThisCall) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_ThisCall); + else if (callingConvention == BfIRCallingConv_StdCall) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_StdCall); + else if (callingConvention == BfIRCallingConv_FastCall) + methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_FastCall); + return methodFlags; +} + void BfMethodInstance::UndoDeclaration(bool keepIRFunction) { diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 2c6e35e5..4c7a87c7 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -899,6 +899,7 @@ public: } BfImportKind GetImportKind(); + BfMethodFlags GetMethodFlags(); void UndoDeclaration(bool keepIRFunction = false); BfTypeInstance* GetOwner(); BfModule* GetModule(); diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index b5ddaef9..1e70fecf 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -512,13 +512,14 @@ bool BfModule::AddDeferredCallEntry(BfDeferredCallEntry* deferredCallEntry, BfSc return true; } -void BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* scopeData, Array* captures) +BfDeferredCallEntry* BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* scopeData, Array* captures) { BfDeferredCallEntry* deferredCallEntry = new BfDeferredCallEntry(); deferredCallEntry->mDeferredBlock = block; if (captures != NULL) deferredCallEntry->mCaptures = *captures; AddDeferredCallEntry(deferredCallEntry, scopeData); + return deferredCallEntry; } BfDeferredCallEntry* BfModule::AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scopeData, BfAstNode* srcNode, bool bypassVirtual, bool doNullCheck) @@ -826,6 +827,7 @@ void BfModule::EmitDeferredCall(BfDeferredCallEntry& deferredCallEntry, bool mov AddLocalVariableDef(localVar, true); } + SetAndRestoreValue prevCustomAttribute(mCurMethodState->mEmitRefNode, deferredCallEntry.mEmitRefNode); VisitEmbeddedStatement(deferredCallEntry.mDeferredBlock, NULL, BfEmbeddedStatementFlags_IsDeferredBlock); RestoreScopeState(); return; @@ -6684,7 +6686,7 @@ void BfModule::Visit(BfDeferStatement* deferStmt) } } AddDeferredBlock(block, scope, &captures); - } + } else AddDeferredBlock(block, scope); } diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index 9f88840f..4a094206 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -187,6 +187,19 @@ enum BfTypeFlags BfTypeFlags_HasDestructor = 0x40000, }; +enum BfMethodFlags +{ + BfMethodFlags_Protected = 3, + BfMethodFlags_Public = 6, + BfMethodFlags_Static = 0x10, + BfMethodFlags_Virtual = 0x40, + BfMethodFlags_StdCall = 0x1000, + BfMethodFlags_FastCall = 0x2000, + BfMethodFlags_ThisCall = 0x3000, + BfMethodFlags_Mutating = 0x4000, + BfMethodFlags_Constructor = 0x8000, +}; + enum BfObjectFlags : uint8 { BfObjectFlag_None = 0, diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index 2eef6ead..c16a7f3d 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -1172,6 +1172,8 @@ void CeBuilder::HandleParams() void CeBuilder::ProcessMethod(BfMethodInstance* methodInstance, BfMethodInstance* dupMethodInstance) { + SetAndRestoreValue prevMethodStateInConstEval(mCeMachine->mCeModule->mCurMethodState, NULL); + auto irCodeGen = mCeMachine->mCeModule->mBfIRBuilder->mBeIRCodeGen; auto irBuilder = mCeMachine->mCeModule->mBfIRBuilder; auto beModule = irCodeGen->mBeModule; @@ -2680,7 +2682,7 @@ BfError* CeContext::Fail(const StringImpl& error) auto bfError = mCurModule->Fail(StrFormat("Unable to comptime %s", mCurModule->MethodToString(mCurMethodInstance).c_str()), mCurTargetSrc, (mCurEvalFlags & CeEvalFlags_PersistantError) != 0); if (bfError == NULL) return NULL; - mCeMachine->mCompiler->mPassInstance->MoreInfo(error, mCeMachine->mCeModule->mCompiler->GetAutoComplete() != NULL); + mCeMachine->mCompiler->mPassInstance->MoreInfo(error, mCeMachine->mCompiler->GetAutoComplete() != NULL); return bfError; } @@ -3020,6 +3022,12 @@ addr_ce CeContext::GetString(int stringId) return *ceAddrPtr; } +addr_ce CeContext::GetString(const StringImpl& str) +{ + int stringId = mCeMachine->mCeModule->mContext->GetStringLiteralId(str); + return GetString(stringId); +} + BfType* CeContext::GetBfType(int typeId) { if ((uintptr)typeId < (uintptr)mCeMachine->mCeModule->mContext->mTypes.size()) @@ -3616,14 +3624,15 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns AutoTimer autoTimer(mCeMachine->mRevisionExecuteTime); + SetAndRestoreValue prevContext(mCeMachine->mCurContext, this); SetAndRestoreValue prevEvalFlags(mCurEvalFlags, flags); SetAndRestoreValue prevTargetSrc(mCurTargetSrc, targetSrc); SetAndRestoreValue prevModule(mCurModule, module); SetAndRestoreValue prevMethodInstance(mCurMethodInstance, methodInstance); - SetAndRestoreValue prevExpectingType(mCurExpectingType, expectingType); + SetAndRestoreValue prevExpectingType(mCurExpectingType, expectingType); // Reentrancy may occur as methods need defining - SetAndRestoreValue prevMethodStateInConstEval(module->mCurMethodState, NULL); + //SetAndRestoreValue prevMethodStateInConstEval(module->mCurMethodState, NULL); if (mCeMachine->mAppendAllocInfo != NULL) { @@ -3887,7 +3896,7 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns } #define CE_CHECKALLOC(SIZE) \ - if ((uintptr)memSize + (uintptr)SIZE > BF_CE_MAX_MEMORY) \ + if ((SIZE < 0) || (uintptr)memSize + (uintptr)SIZE > BF_CE_MAX_MEMORY) \ { \ _Fail("Maximum memory size exceeded"); \ } @@ -4006,6 +4015,14 @@ BfTypedValue CeContext::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns instPtr = &ceFunction->mCode[0]; \ CE_CHECKSTACK(); +static void CeSetAddrVal(void* ptr, addr_ce val, int32 ptrSize) +{ + if (ptrSize == 4) + *(int32*)(ptr) = (int32)val; + else + *(int64*)(ptr) = (int64)val; +} + bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* startFramePtr, BfType*& returnType) { auto ceModule = mCeMachine->mCeModule; @@ -4208,7 +4225,119 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* handled = true; return true; } - else if (checkFunction->mFunctionKind == CeFunctionKind_EmitDefinition) + else if (checkFunction->mFunctionKind == CeFunctionKind_GetMethodCount) + { + int32 typeId = *(int32*)((uint8*)stackPtr + 4); + + CeTypeInfo* typeInfo = mCeMachine->GetTypeInfo(GetBfType(typeId)); + if (typeInfo == NULL) + { + _Fail("Invalid type"); + return false; + } + + *(int32*)(stackPtr + 0) = (int)typeInfo->mMethodInstances.size(); + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetMethod) + { + int32 typeId = *(int32*)((uint8*)stackPtr + 8); + int32 methodIdx = *(int32*)((uint8*)stackPtr + 8+4); + + CeTypeInfo* typeInfo = mCeMachine->GetTypeInfo(GetBfType(typeId)); + if (typeInfo == NULL) + { + _Fail("Invalid type"); + return false; + } + if ((methodIdx < 0) || (methodIdx >= typeInfo->mMethodInstances.mSize)) + { + _Fail("Method out of bounds"); + return false; + } + + *(int64*)(stackPtr + 0) = (int64)(intptr)typeInfo->mMethodInstances[methodIdx]; + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_Method_ToString) + { + int64 methodHandle = *(int64*)((uint8*)stackPtr + ptrSize); + + auto methodInstance = mCeMachine->GetMethodInstance(methodHandle); + if (methodInstance == NULL) + { + _Fail("Invalid method instance"); + return false; + } + + CeSetAddrVal(stackPtr + 0, GetString(mCeMachine->mCeModule->MethodToString(methodInstance)), ptrSize); + _FixVariables(); + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_Method_GetName) + { + int64 methodHandle = *(int64*)((uint8*)stackPtr + ptrSize); + + auto methodInstance = mCeMachine->GetMethodInstance(methodHandle); + if (methodInstance == NULL) + { + _Fail("Invalid method instance"); + return false; + } + + CeSetAddrVal(stackPtr + 0, GetString(methodInstance->mMethodDef->mName), ptrSize); + _FixVariables(); + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_Method_GetInfo) + { + // int32 mReturnType + // int32 mParamCount + // int16 mFlags + + int64 methodHandle = *(int64*)((uint8*)stackPtr + 4+4+2); + + auto methodInstance = mCeMachine->GetMethodInstance(methodHandle); + if (methodInstance == NULL) + { + _Fail("Invalid method instance"); + return false; + } + + *(int32*)(stackPtr + 0) = methodInstance->mReturnType->mTypeId; + *(int32*)(stackPtr + 4) = methodInstance->GetParamCount(); + *(int16*)(stackPtr + 4+4) = methodInstance->GetMethodFlags(); + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_Method_GetParamInfo) + { + // int32 mParamType + // int16 mFlags + // str mName + + int64 methodHandle = *(int64*)((uint8*)stackPtr + 4+2+ptrSize); + int32 paramIdx = *(int32*)((uint8*)stackPtr + 4+2+ptrSize+8); + + auto methodInstance = mCeMachine->GetMethodInstance(methodHandle); + if (methodInstance == NULL) + { + _Fail("Invalid method instance"); + return false; + } + + *(int32*)(stackPtr + 0) = methodInstance->GetParamType(paramIdx)->mTypeId; + *(int16*)(stackPtr + 4) = 0; // Flags + CeSetAddrVal(stackPtr + 4+2, GetString(methodInstance->GetParamName(paramIdx)), ptrSize); + _FixVariables(); + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_EmitTypeBody) { int32 typeId = *(int32*)((uint8*)stackPtr); addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + sizeof(int32)); @@ -4226,6 +4355,60 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* handled = true; return true; } + else if (checkFunction->mFunctionKind == CeFunctionKind_EmitMethodEntry) + { + int64 methodHandle = *(int64*)((uint8*)stackPtr); + addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + sizeof(int64)); + + if ((mCurEmitContext == NULL) || (mCurEmitContext->mMethodInstance == NULL) || + (methodHandle != (int64)mCurEmitContext->mMethodInstance)) + { + _Fail("Code cannot be emitted for this method in this context"); + return false; + } + if (!GetStringFromStringView(strViewPtr, mCurEmitContext->mEmitData)) + { + _Fail("Invalid StringView"); + return false; + } + + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_EmitMethodExit) + { + int64 methodHandle = *(int64*)((uint8*)stackPtr); + addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + sizeof(int64)); + if ((mCurEmitContext == NULL) || (mCurEmitContext->mMethodInstance == NULL) || + (methodHandle != (int64)mCurEmitContext->mMethodInstance)) + { + _Fail("Code cannot be emitted for this method in this context"); + return false; + } + if (!GetStringFromStringView(strViewPtr, mCurEmitContext->mExitEmitData)) + { + _Fail("Invalid StringView"); + return false; + } + + handled = true; + return true; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_EmitMixin) + { + addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr); + String emitStr; + if (!GetStringFromStringView(strViewPtr, emitStr)) + { + _Fail("Invalid StringView"); + return false; + } + + mCurModule->CEMixin(mCurTargetSrc, emitStr); + + handled = true; + return true; + } else if (checkFunction->mFunctionKind == CeFunctionKind_Sleep) { int32 sleepMS = *(int32*)((uint8*)stackPtr); @@ -4461,7 +4644,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* if (valueAddr == 0) { - result = 0; + CeSetAddrVal(&result, 0, ptrSize); } else { @@ -4477,10 +4660,11 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* } if (ceModule->TypeIsSubTypeOf(valueType->ToTypeInstance(), ifaceType->ToTypeInstance(), false)) - result = valueAddr; + CeSetAddrVal(&result, valueAddr, ptrSize); else - result = 0; - } + CeSetAddrVal(&result, 0, ptrSize); + + } } break; case CeOp_GetReflectType: @@ -4669,7 +4853,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* CE_LOAD(uint32); break; case CeOp_Load_64: - CE_LOAD(uint64); + CE_LOAD(uint64); break; case CeOp_Load_X: { @@ -4973,7 +5157,15 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* CE_CHECKADDR(valueAddr, sizeof(int32)); int32 objTypeId = *(int32*)(memStart + valueAddr); - auto valueType = ceModule->mContext->mTypes[objTypeId]->ToTypeInstance(); + + auto bfObjectType = GetBfType(objTypeId); + if ((bfObjectType == NULL) || (!bfObjectType->IsTypeInstance())) + { + _Fail("Invalid object"); + return false; + } + + auto valueType = bfObjectType->ToTypeInstance(); BfMethodInstance* methodInstance = NULL; if (valueType != NULL) @@ -5671,8 +5863,10 @@ void CeMachine::CompileStarted() void CeMachine::CompileDone() { - // So things like delted local methods get recheckeds + // So things like deleted local methods get recheckeds mRevision++; + mTypeInfoMap.Clear(); + mMethodInstanceSet.Clear(); } void CeMachine::DerefMethodInfo(CeFunctionInfo* ceFunctionInfo) @@ -6008,12 +6202,48 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder { ceFunction->mFunctionKind = CeFunctionKind_Type_GetCustomAttribute; } + else if (methodDef->mName == "Comptime_GetMethod") + { + ceFunction->mFunctionKind = CeFunctionKind_GetMethod; + } + else if (methodDef->mName == "Comptime_GetMethodCount") + { + ceFunction->mFunctionKind = CeFunctionKind_GetMethodCount; + } + else if (methodDef->mName == "Comptime_Method_ToString") + { + ceFunction->mFunctionKind = CeFunctionKind_Method_ToString; + } + else if (methodDef->mName == "Comptime_Method_GetName") + { + ceFunction->mFunctionKind = CeFunctionKind_Method_GetName; + } + else if (methodDef->mName == "Comptime_Method_GetInfo") + { + ceFunction->mFunctionKind = CeFunctionKind_Method_GetInfo; + } + else if (methodDef->mName == "Comptime_Method_GetParamInfo") + { + ceFunction->mFunctionKind = CeFunctionKind_Method_GetParamInfo; + } } else if (owner->IsInstanceOf(mCeModule->mCompiler->mCompilerTypeDef)) { - if (methodDef->mName == "Comptime_EmitDefinition") + if (methodDef->mName == "Comptime_EmitTypeBody") { - ceFunction->mFunctionKind = CeFunctionKind_EmitDefinition; + ceFunction->mFunctionKind = CeFunctionKind_EmitTypeBody; + } + else if (methodDef->mName == "Comptime_EmitMethodEntry") + { + ceFunction->mFunctionKind = CeFunctionKind_EmitMethodEntry; + } + else if (methodDef->mName == "Comptime_EmitMethodExit") + { + ceFunction->mFunctionKind = CeFunctionKind_EmitMethodExit; + } + else if (methodDef->mName == "Comptime_EmitMixin") + { + ceFunction->mFunctionKind = CeFunctionKind_EmitMixin; } } else if (owner->IsInstanceOf(mCeModule->mCompiler->mDiagnosticsDebugTypeDef)) @@ -6188,6 +6418,52 @@ CeFunction* CeMachine::GetPreparedFunction(BfMethodInstance* methodInstance) return ceFunction; } +CeTypeInfo* CeMachine::GetTypeInfo(BfType* type) +{ + if (type == NULL) + return NULL; + + auto typeInstance = type->ToTypeInstance(); + if (typeInstance == NULL) + return NULL; + + CeTypeInfo* ceTypeInfo = NULL; + if (!mTypeInfoMap.TryAdd(type, NULL, &ceTypeInfo)) + { + if (ceTypeInfo->mRevision == typeInstance->mRevision) + return ceTypeInfo; + ceTypeInfo->mMethodInstances.Clear(); + } + + mCeModule->PopulateType(typeInstance, BfPopulateType_DataAndMethods); + ceTypeInfo->mRevision = typeInstance->mRevision; + for (auto& methodGroup : typeInstance->mMethodInstanceGroups) + { + if (methodGroup.mDefault != NULL) + { + mMethodInstanceSet.Add(methodGroup.mDefault); + ceTypeInfo->mMethodInstances.Add(methodGroup.mDefault); + } + if (methodGroup.mMethodSpecializationMap != NULL) + { + for (auto& kv : *methodGroup.mMethodSpecializationMap) + { + mMethodInstanceSet.Add(kv.mValue); + ceTypeInfo->mMethodInstances.Add(kv.mValue); + } + } + } + return ceTypeInfo; +} + +BfMethodInstance* CeMachine::GetMethodInstance(int64 methodHandle) +{ + BfMethodInstance* methodInstance = (BfMethodInstance*)(intptr)methodHandle; + if (!mMethodInstanceSet.Contains(methodInstance)) + return false; + return methodInstance; +} + void CeMachine::QueueMethod(BfMethodInstance* methodInstance, BfIRValue func) { if (mPreparingFunction != NULL) diff --git a/IDEHelper/Compiler/CeMachine.h b/IDEHelper/Compiler/CeMachine.h index ab475f5d..7b5040ea 100644 --- a/IDEHelper/Compiler/CeMachine.h +++ b/IDEHelper/Compiler/CeMachine.h @@ -254,7 +254,17 @@ enum CeFunctionKind CeFunctionKind_GetReflectTypeByName, CeFunctionKind_GetReflectSpecializedType, CeFunctionKind_Type_GetCustomAttribute, - CeFunctionKind_EmitDefinition, + CeFunctionKind_GetMethodCount, + CeFunctionKind_GetMethod, + CeFunctionKind_Method_ToString, + CeFunctionKind_Method_GetName, + CeFunctionKind_Method_GetInfo, + CeFunctionKind_Method_GetParamInfo, + + CeFunctionKind_EmitTypeBody, + CeFunctionKind_EmitMethodEntry, + CeFunctionKind_EmitMethodExit, + CeFunctionKind_EmitMixin, CeFunctionKind_Sleep, CeFunctionKind_Char32_ToLower, CeFunctionKind_Char32_ToUpper, @@ -612,12 +622,15 @@ public: class CeEmitContext { public: - BfType* mType; + BfType* mType; + BfMethodInstance* mMethodInstance; String mEmitData; + String mExitEmitData; CeEmitContext() { mType = NULL; + mMethodInstance = NULL; } }; @@ -662,6 +675,7 @@ public: int GetTypeIdFromType(addr_ce typeAddr); addr_ce GetReflectSpecializedType(addr_ce unspecializedType, addr_ce typeArgsSpanAddr); addr_ce GetString(int stringId); + addr_ce GetString(const StringImpl& str); addr_ce GetConstantData(BeConstant* constant); BfType* GetBfType(int typeId); void PrepareConstStructEntry(CeConstStructData& constStructData); @@ -677,12 +691,20 @@ public: BfTypedValue Call(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* methodInstance, const BfSizedArray& args, CeEvalFlags flags, BfType* expectingType); }; +struct CeTypeInfo +{ + Array mMethodInstances; + int mRevision; +}; + class CeMachine { public: Dictionary mFunctions; Dictionary mNamedFunctionMap; Dictionary mFunctionIdMap; // Only used for 32-bit + Dictionary mTypeInfoMap; + HashSet mMethodInstanceSet; Array mContextList; @@ -722,6 +744,8 @@ public: void CheckFunctions(); CeFunction* GetFunction(BfMethodInstance* methodInstance, BfIRValue func, bool& added); CeFunction* GetPreparedFunction(BfMethodInstance* methodInstance); + CeTypeInfo* GetTypeInfo(BfType* type); + BfMethodInstance* GetMethodInstance(int64 methodHandle); public: void CompileStarted(); diff --git a/IDEHelper/Tests/src/Comptime.bf b/IDEHelper/Tests/src/Comptime.bf index e06cc829..7a8f4569 100644 --- a/IDEHelper/Tests/src/Comptime.bf +++ b/IDEHelper/Tests/src/Comptime.bf @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using System.Reflection; namespace Tests { @@ -27,11 +29,26 @@ namespace Tests [Comptime] public void ApplyToType(Type type) { - Compiler.EmitDefinition(type, scope $""" + Compiler.EmitTypeBody(type, scope $""" public int32 m{mMemberName} = {mInitVal}; public int32 GetVal{mMemberName}() => mC; """); + } + } + [AttributeUsage(.Method)] + struct LogAttribute : Attribute, IComptimeMethodApply + { + public static String gLog = new .() ~ delete _; + + [Comptime] + public void ApplyToMethod(ComptimeMethodInfo method) + { + String emit = scope $"LogAttribute.gLog.AppendF($\"Called {method}"; + for (var fieldIdx < method.ParamCount) + emit.AppendF($" {{ {method.GetParamName(fieldIdx)} }}"); + emit.Append("\");"); + Compiler.EmitMethodEntry(method, emit); } } @@ -43,13 +60,19 @@ namespace Tests [OnCompile(.TypeInit), Comptime] public static void Generate() { - Compiler.EmitDefinition(typeof(Self), """ + Compiler.EmitTypeBody(typeof(Self), """ public int32 mB = 234; public int32 GetValB() => mB; """); } } + [Log] + public static void MethodA(int a, int b) + { + + } + [Test] public static void TestBasics() { @@ -59,6 +82,12 @@ namespace Tests Test.Assert(ca.GetValB() == 234); Test.Assert(ca.mC == 345); Test.Assert(ca.GetValC() == 345); + + Compiler.Mixin("int val = 99;"); + Test.Assert(val == 99); + + MethodA(34, 45); + Debug.Assert(LogAttribute.gLog == "Called Tests.Comptime.MethodA(int a, int b) 34 45"); } } }