diff --git a/BeefLibs/corlib/src/IRefCounted.bf b/BeefLibs/corlib/src/IRefCounted.bf index 1a0b7ca3..a62a01d0 100644 --- a/BeefLibs/corlib/src/IRefCounted.bf +++ b/BeefLibs/corlib/src/IRefCounted.bf @@ -6,7 +6,7 @@ namespace System interface IRefCounted { void AddRef(); - void ReleaseRef(); + void Release(); } class RefCounted : IRefCounted @@ -63,5 +63,167 @@ namespace System Debug.Assert(refCount >= 0); return refCount; } + + void IRefCounted.Release() + { + ReleaseRef(); + } + + struct Alloc + { + public void* Alloc(Type type, int size, int align) + { + int sizeAdd = size + Math.Max(align, sizeof(int)); + + void* data = Internal.Malloc(sizeAdd); + return (uint8*)data + sizeAdd; + } + } + } + + class RefCounted : IRefCounted where T : class, delete + { + public T mVal; + public int mRefCount = 1; + + public int RefCount => mRefCount; + public T Value => mVal; + + protected this() + { + + } + + protected ~this() + { + Debug.Assert(mRefCount == 0); + delete mVal; + } + + [OnCompile(.TypeInit), Comptime] + static void Init() + { + String emitStr = scope .(); + + for (var methodInfo in typeof(T).GetMethods(.Public)) + { + if (methodInfo.IsStatic) + continue; + if (!methodInfo.IsConstructor) + continue; + + emitStr.AppendF("public static RefCounted Create("); + methodInfo.GetParamsDecl(emitStr); + emitStr.AppendF(")\n"); + emitStr.AppendF("{{\n"); + emitStr.AppendF("\treturn new [Friend] RefCountedAppend("); + methodInfo.GetArgsList(emitStr); + emitStr.AppendF(");\n}}\n"); + } + + Compiler.EmitTypeBody(typeof(Self), emitStr); + } + + public static RefCounted Attach(T val) + { + return new Self() { mVal = val }; + } + + public virtual void DeleteSelf() + { + delete this; + } + + public void DeleteUnchecked() + { + mRefCount = 0; + DeleteSelf(); + } + + public void AddRef() + { + Interlocked.Increment(ref mRefCount); + } + + public void Release() + { + int refCount = Interlocked.Decrement(ref mRefCount); + Debug.Assert(refCount >= 0); + if (refCount == 0) + DeleteSelf(); + } + + public void ReleaseLastRef() + { + int refCount = Interlocked.Decrement(ref mRefCount); + Debug.Assert(refCount == 0); + if (refCount == 0) + DeleteSelf(); + } + + public int ReleaseRefNoDelete() + { + int refCount = Interlocked.Decrement(ref mRefCount); + Debug.Assert(refCount >= 0); + return refCount; + } + + public virtual T Detach() + { + var val = mVal; + mVal = null; + return val; + } + + public static T operator->(Self self) + { + return self.mVal; + } + + public static T operator implicit(Self self) + { + return self.mVal; + } + } + + class RefCountedAppend : RefCounted where T : class, new, delete + { + protected ~this() + { + Debug.Assert(mRefCount == 0); + delete:append mVal; + mVal = null; + } + + [OnCompile(.TypeInit), Comptime] + static void Init() + { + String emitStr = scope .(); + + for (var methodInfo in typeof(T).GetMethods(.Public)) + { + if (methodInfo.IsStatic) + continue; + if (!methodInfo.IsConstructor) + continue; + + emitStr.AppendF("[AllowAppend]\nprotected this("); + methodInfo.GetParamsDecl(emitStr); + emitStr.AppendF(")\n"); + emitStr.AppendF("{{\n"); + emitStr.AppendF("\tvar val = append T("); + methodInfo.GetArgsList(emitStr); + emitStr.AppendF(");\n"); + emitStr.AppendF("\tmVal = val;\n"); + emitStr.AppendF("}}\n"); + } + + Compiler.EmitTypeBody(typeof(Self), emitStr); + } + + public override T Detach() + { + Runtime.FatalError("Can only detach from objects created via RefCounted.Attach"); + } } } diff --git a/BeefLibs/corlib/src/Reflection/MethodInfo.bf b/BeefLibs/corlib/src/Reflection/MethodInfo.bf index cf814559..8ec9dd4f 100644 --- a/BeefLibs/corlib/src/Reflection/MethodInfo.bf +++ b/BeefLibs/corlib/src/Reflection/MethodInfo.bf @@ -50,6 +50,9 @@ namespace System.Reflection public bool IsReadOnly => Compiler.IsComptime ? Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mMethodFlags.HasFlag(.ReadOnly) : mData.mMethodData.[Friend]mFlags.HasFlag(.ReadOnly); + public bool IsStatic => Compiler.IsComptime ? + Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mMethodFlags.HasFlag(.Static) : + mData.mMethodData.[Friend]mFlags.HasFlag(.Static); public StringView Name => Compiler.IsComptime ? Type.[Friend]Comptime_Method_GetName(mData.mComptimeMethodInstance) : @@ -112,6 +115,19 @@ namespace System.Reflection } } + public TypeInstance.ParamFlags GetParamFlags(int paramIdx) + { + if (Compiler.IsComptime) + { + return Type.[Friend]Comptime_Method_GetParamInfo(mData.mComptimeMethodInstance, (.)paramIdx).mParamFlags; + } + else + { + Debug.Assert((uint)paramIdx < (uint)mData.mMethodData.mParamCount); + return mData.mMethodData.mParamData[paramIdx].mParamFlags; + } + } + public Result GetParamCustomAttribute(int paramIdx) where T : Attribute { if (Compiler.IsComptime) @@ -967,19 +983,60 @@ namespace System.Reflection strBuffer.Append(' '); strBuffer.Append(mData.mMethodData.mName); strBuffer.Append('('); + + int useParamIdx = 0; for (int paramIdx < mData.mMethodData.mParamCount) { - if (paramIdx > 0) - strBuffer.Append(", "); let paramData = mData.mMethodData.mParamData[paramIdx]; let paramType = Type.[Friend]GetType(paramData.mType); + if (paramData.mParamFlags.HasFlag(.Implicit)) + continue; + if (useParamIdx > 0) + strBuffer.Append(", "); paramType.ToString(strBuffer); strBuffer.Append(' '); strBuffer.Append(paramData.mName); + useParamIdx++; } strBuffer.Append(')'); } + public void GetParamsDecl(String strBuffer) + { + int useParamIdx = 0; + for (int paramIdx < ParamCount) + { + var flag = GetParamFlags(paramIdx); + if (flag.HasFlag(.Implicit)) + continue; + if (useParamIdx > 0) + strBuffer.Append(", "); + if (flag.HasFlag(.Params)) + strBuffer.Append("params "); + strBuffer.Append(GetParamType(paramIdx)); + strBuffer.Append(" "); + strBuffer.Append(GetParamName(paramIdx)); + useParamIdx++; + } + } + + public void GetArgsList(String strBuffer) + { + int useParamIdx = 0; + for (int paramIdx < ParamCount) + { + var flag = GetParamFlags(paramIdx); + if (flag.HasFlag(.Implicit)) + continue; + if (useParamIdx > 0) + strBuffer.Append(", "); + if (flag.HasFlag(.Params)) + strBuffer.Append("params "); + strBuffer.Append(GetParamName(paramIdx)); + useParamIdx++; + } + } + public struct Enumerator : IEnumerator { BindingFlags mBindingFlags; diff --git a/BeefLibs/corlib/src/String.bf b/BeefLibs/corlib/src/String.bf index 033dd439..da97b9cd 100644 --- a/BeefLibs/corlib/src/String.bf +++ b/BeefLibs/corlib/src/String.bf @@ -40,7 +40,7 @@ namespace System [Ordered] class String : IHashable, IFormattable, IPrintable { - enum CreateFlags + public enum CreateFlags { None = 0, NullTerminate = 1 diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index f4159dfa..a36f5eb0 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -852,7 +852,8 @@ namespace System.Reflection None = 0, Splat = 1, Implicit = 2, - AppendIdx = 4 + AppendIdx = 4, + Params = 8 } [CRepr, AlwaysInclude] diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index 24e4e955..299ff4d8 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -1768,6 +1768,7 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp) { case BfUnaryOp_None: return ""; case BfUnaryOp_AddressOf: return "&"; + case BfUnaryOp_Arrow: return "->"; case BfUnaryOp_Dereference: return "*"; case BfUnaryOp_Negate: return "-"; case BfUnaryOp_Not: return "!"; @@ -1862,6 +1863,8 @@ BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token) return BfUnaryOp_Dereference; case BfToken_Ampersand: return BfUnaryOp_AddressOf; + case BfToken_Arrow: + return BfUnaryOp_Arrow; case BfToken_Minus: return BfUnaryOp_Negate; case BfToken_Bang: diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 2c2eac7d..94538ed0 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -1901,6 +1901,7 @@ enum BfUnaryOp { BfUnaryOp_None, BfUnaryOp_AddressOf, + BfUnaryOp_Arrow, BfUnaryOp_Dereference, BfUnaryOp_Negate, BfUnaryOp_Not, diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 525ab000..9692ff8f 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1857,6 +1857,14 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken bool isStatic = false; BfTypedValue targetValue = LookupTypeRefOrIdentifier(target, &isStatic, (BfEvalExprFlags)(BfEvalExprFlags_IgnoreNullConditional | BfEvalExprFlags_NoCast), expectingType); + if ((targetValue) && (dotToken->mToken == BfToken_Arrow)) + { + SetAndRestoreValue prevIgnoreClassifying(mModule->mIsInsideAutoComplete, true); + BfExprEvaluator exprEvaluator(mModule); + auto arrowValue = exprEvaluator.PerformUnaryOperation_TryOperator(targetValue, NULL, BfUnaryOp_Arrow, BfNodeDynCast(dotToken), BfUnaryOpFlag_None); + if (arrowValue) + targetValue = arrowValue; + } bool hadResults = false; bool doAsNamespace = true; diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 591cca23..159e359a 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -2527,8 +2527,7 @@ void BfMethodMatcher::FlushAmbiguityError() error = mModule->Fail("Ambiguous method call", mTargetSrc); if (error != NULL) { - auto unspecializedType = mModule->GetUnspecializedTypeInstance(mBestMethodTypeInstance); - BfMethodInstance* bestMethodInstance = mModule->GetRawMethodInstance(unspecializedType, mBestMethodDef); + BfMethodInstance* bestMethodInstance = mModule->GetUnspecializedMethodInstance(mBestRawMethodInstance, true); BfTypeVector* typeGenericArguments = NULL; if (mBestMethodTypeInstance->mGenericTypeInfo != NULL) typeGenericArguments = &mBestMethodTypeInstance->mGenericTypeInfo->mTypeGenericArguments; @@ -4580,6 +4579,16 @@ void BfExprEvaluator::FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType mModule->mCompiler->mResolvePassData->mAutoComplete->FixitAddMember(typeInst, fieldType, fieldName, isStatic, mModule->mCurTypeInstance); } +BfTypedValue BfExprEvaluator::TryArrowLookup(BfTypedValue typedValue, BfTokenNode* arrowToken) +{ + auto arrowValue = PerformUnaryOperation_TryOperator(typedValue, NULL, BfUnaryOp_Arrow, arrowToken, BfUnaryOpFlag_None); + if (arrowValue) + return arrowValue; + if (mModule->PreFail()) + mModule->Fail(StrFormat("Type '%s' does not contain a '->' operator", mModule->TypeToString(typedValue.mType).c_str()), arrowToken); + return typedValue; +} + BfTypedValue BfExprEvaluator::LoadProperty(BfAstNode* targetSrc, BfTypedValue target, BfTypeInstance* typeInstance, BfPropertyDef* prop, BfLookupFieldFlags flags, BfCheckedKind checkedKind, bool isInlined) { if ((flags & BfLookupFieldFlag_IsAnonymous) == 0) @@ -17635,6 +17644,9 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m } thisValue = mResult; mResult = BfTypedValue(); + + if ((thisValue) && (memberRefExpression->mDotToken != NULL) && (memberRefExpression->mDotToken->mToken == BfToken_Arrow)) + thisValue = TryArrowLookup(thisValue, memberRefExpression->mDotToken); } else if (auto qualifiedName = BfNodeDynCast(target)) { @@ -20795,10 +20807,11 @@ void BfExprEvaluator::DoMemberReference(BfMemberReferenceExpression* memberRefEx bool isNullCondLookup = (memberRefExpr->mDotToken != NULL) && (memberRefExpr->mDotToken->GetToken() == BfToken_QuestionDot); bool isCascade = ((memberRefExpr->mDotToken != NULL) && (memberRefExpr->mDotToken->GetToken() == BfToken_DotDot)); + bool isArrowLookup = ((memberRefExpr->mDotToken != NULL) && (memberRefExpr->mDotToken->GetToken() == BfToken_Arrow)); BfIdentifierNode* nameLeft = BfNodeDynCast(memberRefExpr->mTarget); BfIdentifierNode* nameRight = BfIdentifierCast(memberRefExpr->mMemberName); - if ((nameLeft != NULL) && (nameRight != NULL) && (!isNullCondLookup) && (!isCascade)) + if ((nameLeft != NULL) && (nameRight != NULL) && (!isNullCondLookup) && (!isCascade) && (!isArrowLookup)) { bool hadError = false; LookupQualifiedName(memberRefExpr, nameLeft, nameRight, true, &hadError); @@ -20856,6 +20869,9 @@ void BfExprEvaluator::DoMemberReference(BfMemberReferenceExpression* memberRefEx if (isNullCondLookup) thisValue = SetupNullConditional(thisValue, memberRefExpr->mDotToken); + if ((isArrowLookup) && (thisValue)) + thisValue = TryArrowLookup(thisValue, memberRefExpr->mDotToken); + mResult = LookupField(nameRefNode, thisValue, findName); if ((!mResult) && (mPropDef == NULL)) @@ -21093,7 +21109,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) mPropDef = foundProp; if (foundProp->mIsStatic) { - mPropTarget = BfTypedValue(curCheckType); + mPropTarget = BfTypedValue(foundPropTypeInst); } else { diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index c4e7b825..ee3566d2 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -508,6 +508,7 @@ public: BfLambdaInstance* GetLambdaInstance(BfLambdaBindExpression* lambdaBindExpr, BfAllocTarget& allocTarget); void VisitLambdaBodies(BfAstNode* body, BfFieldDtorDeclaration* fieldDtor); void FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, const StringImpl& fieldName, bool isStatic); + BfTypedValue TryArrowLookup(BfTypedValue typedValue, BfTokenNode* arrowToken); void PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags); BfTypedValue PerformUnaryOperation_TryOperator(const BfTypedValue& inValue, BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags); void PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index fe5be56d..6aca6dd5 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -7136,7 +7136,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin ParamFlag_None = 0, ParamFlag_Splat = 1, ParamFlag_Implicit = 2, - ParamFlag_AppendIdx = 4 + ParamFlag_AppendIdx = 4, + ParamFlag_Params = 8 }; SizedArray paramVals; @@ -7150,6 +7151,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin paramFlags = (ParamFlags)(paramFlags | ParamFlag_Splat); if (defaultMethod->GetParamKind(paramIdx) == BfParamKind_AppendIdx) paramFlags = (ParamFlags)(paramFlags | ParamFlag_Implicit | ParamFlag_AppendIdx); + if (defaultMethod->GetParamKind(paramIdx) == BfParamKind_Params) + paramFlags = (ParamFlags)(paramFlags | ParamFlag_Params); BfIRValue paramNameConst = GetStringObjectValue(paramName, !mIsComptimeModule); diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index 426c54d6..a204f437 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -2556,9 +2556,12 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate, bool disablePrepro mToken = BfToken_MinusEquals; mSrcIdx++; } - else if ((mCompatMode) && (mSrc[mSrcIdx] == '>')) + else if (mSrc[mSrcIdx] == '>') { - mToken = BfToken_Dot; + if (mCompatMode) + mToken = BfToken_Dot; + else + mToken = BfToken_Arrow; mSrcIdx++; } else diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 8e9fb70d..1d3f7014 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -2913,7 +2913,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat { exprLeft = CreateIndexerExpression(exprLeft); } - else if ((token == BfToken_Dot) || (token == BfToken_DotDot) || (token == BfToken_QuestionDot)) + else if ((token == BfToken_Dot) || (token == BfToken_DotDot) || (token == BfToken_QuestionDot) || (token == BfToken_Arrow)) { if ((token == BfToken_DotDot) && ((createExprFlags & CreateExprFlags_BreakOnCascade) != 0)) return exprLeft; @@ -5506,7 +5506,10 @@ BfIdentifierNode* BfReducer::CompactQualifiedName(BfAstNode* leftNode) // If the previous dotted span failed (IE: had chevrons) then don't insert qualified names in the middle of it auto prevNodeToken = BfNodeDynCast(prevNode); - if ((prevNodeToken != NULL) && ((prevNodeToken->GetToken() == BfToken_Dot) || (prevNodeToken->GetToken() == BfToken_QuestionDot))) + if ((prevNodeToken != NULL) && + ((prevNodeToken->GetToken() == BfToken_Dot) || + (prevNodeToken->GetToken() == BfToken_QuestionDot) || + (prevNodeToken->GetToken() == BfToken_Arrow))) return leftIdentifier; mVisitorPos.MoveNext(); // past . @@ -8120,7 +8123,7 @@ BfExpression* BfReducer::CreateIndexerExpression(BfExpression* target) BfMemberReferenceExpression* BfReducer::CreateMemberReferenceExpression(BfAstNode* target) { - auto tokenNode = ExpectTokenAfter(target, BfToken_Dot, BfToken_DotDot, BfToken_QuestionDot); + auto tokenNode = ExpectTokenAfter(target, BfToken_Dot, BfToken_DotDot, BfToken_QuestionDot, BfToken_Arrow); auto memberReferenceExpr = mAlloc->Alloc(); if (target != NULL) @@ -9356,7 +9359,8 @@ BfTokenNode* BfReducer::ParseMethodParams(BfAstNode* node, SizedArrayImplGetToken(); if ((token == BfToken_Var) || (token == BfToken_LParen) || (token == BfToken_Delegate) || (token == BfToken_Function) || - (token == BfToken_Comptype) || (token == BfToken_Decltype) || + (token == BfToken_Comptype) || (token == BfToken_Decltype) || + (token == BfToken_AllocType) || (token == BfToken_RetType) || (token == BfToken_DotDotDot)) { mVisitorPos.MoveNext(); diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index e7c393b6..0e54ff3c 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -6011,11 +6011,28 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _Fail("paramIdx is out of range"); return false; } + + enum ParamFlags + { + ParamFlag_None = 0, + ParamFlag_Splat = 1, + ParamFlag_Implicit = 2, + ParamFlag_AppendIdx = 4, + ParamFlag_Params = 8 + }; + + ParamFlags paramFlags = ParamFlag_None; + if (methodInstance->GetParamIsSplat(paramIdx)) + paramFlags = (ParamFlags)(paramFlags | ParamFlag_Splat); + if (methodInstance->GetParamKind(paramIdx) == BfParamKind_AppendIdx) + paramFlags = (ParamFlags)(paramFlags | ParamFlag_Implicit | ParamFlag_AppendIdx); + if (methodInstance->GetParamKind(paramIdx) == BfParamKind_Params) + paramFlags = (ParamFlags)(paramFlags | ParamFlag_Params); addr_ce stringAddr = GetString(methodInstance->GetParamName(paramIdx)); _FixVariables(); *(int32*)(stackPtr + 0) = methodInstance->GetParamType(paramIdx)->mTypeId; - *(int16*)(stackPtr + 4) = 0; // Flags + *(int16*)(stackPtr + 4) = (int16)paramFlags; CeSetAddrVal(stackPtr + 4+2, stringAddr, ptrSize); } else if (checkFunction->mFunctionKind == CeFunctionKind_Method_GetGenericArg) diff --git a/IDEHelper/Tests/src/Operators.bf b/IDEHelper/Tests/src/Operators.bf index f92600b2..20ce3867 100644 --- a/IDEHelper/Tests/src/Operators.bf +++ b/IDEHelper/Tests/src/Operators.bf @@ -709,6 +709,13 @@ namespace Tests Test.Assert(sA == 222); Val += 1000; Test.Assert(sA == 1222); + + RefCounted rcStr = .Create("Abc"); + Test.Assert(rcStr->Length == 3); + rcStr->Clear(); + rcStr.Release(); + + //RefCounted rcB = .Create(); } struct IntStruct @@ -740,8 +747,6 @@ namespace Tests } } - - [Test] public static void TestCompareWithCastOperator() {