diff --git a/.devcontainer/wasm/Dockerfile b/.devcontainer/wasm/Dockerfile index ffb5b4f494b1ed..17ceadc668b0bf 100644 --- a/.devcontainer/wasm/Dockerfile +++ b/.devcontainer/wasm/Dockerfile @@ -27,9 +27,12 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ zlib1g-dev \ ninja-build -# Install V8 Engine SHELL ["/bin/bash", "-c"] +# Install LTS npm and node +RUN source /usr/local/share/nvm/nvm.sh && nvm install --lts + +# Install V8 Engine RUN curl -sSL "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/linux/chromium-v8/v8-linux64-rel-8.5.183.zip" -o ./v8.zip \ && unzip ./v8.zip -d /usr/local/v8 \ && echo $'#!/usr/bin/env bash\n\ diff --git a/eng/Subsets.props b/eng/Subsets.props index 093120feb24111..2906f49cd76d73 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -357,9 +357,6 @@ - - - diff --git a/eng/pipelines/common/templates/runtimes/send-to-helix-inner-step.yml b/eng/pipelines/common/templates/runtimes/send-to-helix-inner-step.yml index 504f921e9de1f5..bd147de7e77264 100644 --- a/eng/pipelines/common/templates/runtimes/send-to-helix-inner-step.yml +++ b/eng/pipelines/common/templates/runtimes/send-to-helix-inner-step.yml @@ -10,7 +10,7 @@ parameters: steps: - ${{ if eq(parameters.osGroup, 'windows') }}: # TODO: Remove and consolidate this when we move to arcade via init-tools.cmd. - - powershell: $(Build.SourcesDirectory)\eng\common\build.ps1 -ci ${{ parameters.restoreParams }} + - powershell: $(Build.SourcesDirectory)\eng\common\build.ps1 -ci -warnaserror 0 ${{ parameters.restoreParams }} displayName: Restore blob feed tasks (Windows) condition: and(succeeded(), ${{ and(ne(parameters.condition, false), ne(parameters.restoreParams, '')) }}) @@ -30,7 +30,7 @@ steps: # Arcade uses this SDK instead of trying to restore one. DotNetCoreSdkDir: /usr/local/dotnet - - script: $(Build.SourcesDirectory)/eng/common/msbuild.sh --ci ${{ parameters.sendParams }} + - script: $(Build.SourcesDirectory)/eng/common/msbuild.sh --ci --warnaserror false ${{ parameters.sendParams }} displayName: ${{ parameters.displayName }} (Unix) condition: and(succeeded(), ${{ and(ne(parameters.condition, false), ne(parameters.sendParams, '')) }}) env: ${{ parameters.environment }} diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index 7be878d4e19abf..0ee3d5185c1934 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -1,72 +1,85 @@ - - - $(TargetOS).$(TargetArchitecture).$(Configuration) - $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts')) - $([MSBuild]::NormalizeDirectory('$(RootBinDir)', 'log')) - $([MSBuild]::NormalizeDirectory('$(RootBinDir)', 'bin', 'coreclr', $(OSPlatformConfig))) - $([MSBuild]::NormalizeDirectory('$(RootBinDir)', 'obj', 'coreclr', $(OSPlatformConfig))) - $([MSBuild]::NormalizePath('$(RepoRoot)', 'dotnet.sh')) - $([MSBuild]::NormalizePath('$(RepoRoot)', 'dotnet.cmd')) - + + + + + + + + + $(TargetOS).$(TargetArchitecture).$(Configuration) + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts')) + $([MSBuild]::NormalizeDirectory('$(RootBinDir)', 'log')) + $([MSBuild]::NormalizeDirectory('$(RootBinDir)', 'bin', 'coreclr', $(OSPlatformConfig))) + $([MSBuild]::NormalizeDirectory('$(RootBinDir)', 'obj', 'coreclr', $(OSPlatformConfig))) + $([MSBuild]::NormalizePath('$(RepoRoot)', 'dotnet.sh')) + $([MSBuild]::NormalizePath('$(RepoRoot)', 'dotnet.cmd')) + + + + true + false + false + + false + true + + false + true + + + + + + + + System.Private.CoreLib + $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).dll')) + + + $([MSBuild]::NormalizePath('$(BinDir)', 'PDB', '$(CoreLibAssemblyName).ni.pdb')) + $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).ni.r2rmap')) + $([MSBuild]::NormalizePath('$(BinDir)', 'StandardOptimizationData.mibc')) + + + - - $(BuildArchitecture) - - true - false - false - - false - true - - false - true + $(DotNetCli) @(DotNetPgo) merge + $(DotNetPgoCmd) -o:$(MergedMibcPath) + $(DotNetPgoCmd) @(OptimizationMibcFiles->'-i:%(Identity)', ' ') + $(DotNetPgoCmd) --inherit-timestamp + $(DotNetPgoCmd) --compressed false - - - - - - - - - - - System.Private.CoreLib - $([MSBuild]::NormalizePath('$(BinDir)', 'IL', '$(CoreLibAssemblyName).dll')) - $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).dll')) - - - $([MSBuild]::NormalizePath('$(BinDir)', 'PDB', '$(CoreLibAssemblyName).ni.pdb')) - $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).ni.r2rmap')) - $([MSBuild]::NormalizePath('$(BinDir)', 'StandardOptimizationData.mibc')) - + + + DependsOnTargets="ResolveProjectReferences;MergeMibcFiles" /> + + + + + + - $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', 'dotnet-pgo', 'dotnet-pgo.dll')) merge - $(DotNetPgoCmd) -o:$(MergedMibcPath) - $(DotNetPgoCmd) @(OptimizationMibcFiles->'-i:%(Identity)', ' ') - $(DotNetPgoCmd) --inherit-timestamp - $(DotNetPgoCmd) --compressed false + %(Crossgen2.RootDir)%(Crossgen2.Directory) - - - + + + @@ -77,7 +90,7 @@ Text="Generating native image of System.Private.CoreLib for $(OSPlatformConfig). Logging to $(CrossGenCoreLibLog)" /> - $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', '$(CrossDir)', 'crossgen2', 'crossgen2.dll')) + $(DotNetCli) @(Crossgen2) $(CrossGenDllCmd) -o:$(CoreLibOutputPath) $(CrossGenDllCmd) -r:$([MSBuild]::NormalizePath('$(BinDir)', 'IL', '*.dll')) $(CrossGenDllCmd) --targetarch:$(TargetArchitecture) @@ -85,7 +98,7 @@ $(CrossGenDllCmd) -m:$(MergedMibcPath) --embed-pgo-data $(CrossGenDllCmd) -O $(CrossGenDllCmd) --verify-type-and-field-layout - $(CrossGenDllCmd) $(CoreLibInputPath) + $(CrossGenDllCmd) @(CoreLib) @@ -111,7 +124,7 @@ - + diff --git a/src/coreclr/gc/env/gcenv.structs.h b/src/coreclr/gc/env/gcenv.structs.h index 0019ae6c988672..9f287ec7bf8c26 100644 --- a/src/coreclr/gc/env/gcenv.structs.h +++ b/src/coreclr/gc/env/gcenv.structs.h @@ -17,20 +17,6 @@ typedef void * HANDLE; #ifdef TARGET_UNIX -typedef char TCHAR; -#define _T(s) s - -#else - -#ifndef _INC_WINDOWS -typedef wchar_t TCHAR; -#define _T(s) L##s -#endif - -#endif - -#ifdef TARGET_UNIX - class EEThreadId { pthread_t m_id; diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index aec68a7bff2a73..5d89ef8d01832c 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -901,7 +901,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genCodeForCompare(GenTreeOp* tree); #ifdef TARGET_ARM64 void genCodeForCCMP(GenTreeCCMP* ccmp); - void genCodeForCinc(GenTreeOp* cinc); #endif void genCodeForSelect(GenTreeOp* select); void genIntrinsic(GenTreeIntrinsic* treeNode); @@ -1250,7 +1249,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #if defined(TARGET_ARM64) void genCodeForJumpCompare(GenTreeOpCC* tree); void genCodeForBfiz(GenTreeOp* tree); - void genCodeForCond(GenTreeOp* tree); #endif // TARGET_ARM64 #if defined(FEATURE_EH_FUNCLETS) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 256b766679545d..8e0faf1f05e381 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -4683,32 +4683,49 @@ void CodeGen::genCodeForCCMP(GenTreeCCMP* ccmp) } //------------------------------------------------------------------------ -// genCodeForSelect: Produce code for a GT_SELECT node. +// genCodeForSelect: Produce code for a GT_SELECT/GT_SELECT_INV/GT_SELECT_NEG node. // // Arguments: // tree - the node // void CodeGen::genCodeForSelect(GenTreeOp* tree) { - assert(tree->OperIs(GT_SELECT, GT_SELECTCC)); - GenTree* opcond = nullptr; - if (tree->OperIs(GT_SELECT)) + assert(tree->OperIs(GT_SELECT, GT_SELECTCC, GT_SELECT_INC, GT_SELECT_INCCC, GT_SELECT_INV, GT_SELECT_INVCC, + GT_SELECT_NEG, GT_SELECT_NEGCC)); + GenTree* opcond = nullptr; + instruction ins = INS_csel; + GenTree* op1 = tree->gtOp1; + GenTree* op2 = tree->gtOp2; + + if (tree->OperIs(GT_SELECT_INV, GT_SELECT_INVCC)) + { + ins = (op2 == nullptr) ? INS_cinv : INS_csinv; + } + else if (tree->OperIs(GT_SELECT_NEG, GT_SELECT_NEGCC)) + { + ins = (op2 == nullptr) ? INS_cneg : INS_csneg; + } + else if (tree->OperIs(GT_SELECT_INC, GT_SELECT_INCCC)) + { + ins = (op2 == nullptr) ? INS_cinc : INS_csinc; + } + + if (tree->OperIs(GT_SELECT, GT_SELECT_INV, GT_SELECT_NEG)) { opcond = tree->AsConditional()->gtCond; genConsumeRegs(opcond); } - emitter* emit = GetEmitter(); - - GenTree* op1 = tree->gtOp1; - GenTree* op2 = tree->gtOp2; - var_types op1Type = genActualType(op1); - var_types op2Type = genActualType(op2); - emitAttr attr = emitActualTypeSize(tree); + if (op2 != nullptr) + { + var_types op1Type = genActualType(op1); + var_types op2Type = genActualType(op2); + assert(genTypeSize(op1Type) == genTypeSize(op2Type)); + } assert(!op1->isUsedFromMemory()); - assert(genTypeSize(op1Type) == genTypeSize(op2Type)); + emitter* emit = GetEmitter(); GenCondition cond; if (opcond != nullptr) @@ -4719,92 +4736,62 @@ void CodeGen::genCodeForSelect(GenTreeOp* tree) } else { - assert(tree->OperIs(GT_SELECTCC)); + assert(tree->OperIs(GT_SELECTCC, GT_SELECT_INCCC, GT_SELECT_INVCC, GT_SELECT_NEGCC)); cond = tree->AsOpCC()->gtCondition; } assert(!op1->isContained() || op1->IsIntegralConst(0)); - assert(!op2->isContained() || op2->IsIntegralConst(0)); + assert(op2 == nullptr || !op2->isContained() || op2->IsIntegralConst(0)); regNumber targetReg = tree->GetRegNum(); regNumber srcReg1 = op1->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op1); - regNumber srcReg2 = op2->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op2); const GenConditionDesc& prevDesc = GenConditionDesc::Get(cond); + emitAttr attr = emitActualTypeSize(tree); + regNumber srcReg2; - emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1)); - - // Some conditions require an additional condition check. - if (prevDesc.oper == GT_OR) - { - emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, srcReg1, targetReg, JumpKindToInsCond(prevDesc.jumpKind2)); - } - else if (prevDesc.oper == GT_AND) - { - emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, targetReg, srcReg2, JumpKindToInsCond(prevDesc.jumpKind2)); - } - - regSet.verifyRegUsed(targetReg); - genProduceReg(tree); -} - -//------------------------------------------------------------------------ -// genCodeForCinc: Produce code for a GT_CINC/GT_CINCCC node. -// -// Arguments: -// tree - the node -// -void CodeGen::genCodeForCinc(GenTreeOp* cinc) -{ - assert(cinc->OperIs(GT_CINC, GT_CINCCC)); - - GenTree* opcond = nullptr; - GenTree* op = cinc->gtOp1; - if (cinc->OperIs(GT_CINC)) + if (op2 == nullptr) { - opcond = cinc->gtOp1; - op = cinc->gtOp2; - genConsumeRegs(opcond); - } - - emitter* emit = GetEmitter(); - var_types opType = genActualType(op->TypeGet()); - emitAttr attr = emitActualTypeSize(cinc->TypeGet()); - - assert(!op->isUsedFromMemory()); - genConsumeRegs(op); - - GenCondition cond; - - if (cinc->OperIs(GT_CINC)) - { - assert(!opcond->isContained()); - // Condition has been generated into a register - move it into flags. - emit->emitIns_R_I(INS_cmp, emitActualTypeSize(opcond), opcond->GetRegNum(), 0); - cond = GenCondition::NE; + srcReg2 = srcReg1; + emit->emitIns_R_R_COND(ins, attr, targetReg, srcReg1, JumpKindToInsCond(prevDesc.jumpKind1)); } else { - assert(cinc->OperIs(GT_CINCCC)); - cond = cinc->AsOpCC()->gtCondition; + srcReg2 = (op2->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op2)); + emit->emitIns_R_R_R_COND(ins, attr, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1)); } - const GenConditionDesc& prevDesc = GenConditionDesc::Get(cond); - regNumber targetReg = cinc->GetRegNum(); - regNumber srcReg; - if (op->isContained()) + // Some floating point comparision conditions require an additional condition check. + // These checks are emitted as a subsequent check using GT_AND or GT_OR nodes. + // e.g., using GT_OR => `dest = (cond1 || cond2) ? src1 : src2` + // GT_AND => `dest = (cond1 && cond2) ? src1 : src2` + // The GT_OR case results in emitting the following sequence of two csel instructions. + // csel dest, src1, src2, cond1 # emitted previously + // csel dest, src1, dest, cond2 + // + if (prevDesc.oper == GT_AND) { - assert(op->IsIntegralConst(0)); - srcReg = REG_ZR; + // To ensure correctness with invert and negate variants of conditional select, the second instruction needs to + // be csinv or csneg respectively. + // dest = (cond1 && cond2) ? src1 : ~src2 + // csinv dest, src1, src2, cond1 + // csinv dest, dest, src2, cond2 + // + // However, the other variants - increment and select, the second instruction needs to be csel. + // dest = (cond1 && cond2) ? src1 : src2++ + // csinc dest, src1, src2, cond1 + // csel dest, dest, src1 cond2 + ins = ((ins == INS_csinv) || (ins == INS_csneg)) ? ins : INS_csel; + emit->emitIns_R_R_R_COND(ins, attr, targetReg, targetReg, srcReg2, JumpKindToInsCond(prevDesc.jumpKind2)); } - else + else if (prevDesc.oper == GT_OR) { - srcReg = op->GetRegNum(); + // Similarly, the second instruction needs to be csinc while emitting conditional increment. + ins = (ins == INS_csinc) ? ins : INS_csel; + emit->emitIns_R_R_R_COND(ins, attr, targetReg, srcReg1, targetReg, JumpKindToInsCond(prevDesc.jumpKind2)); } - assert(prevDesc.oper != GT_OR && prevDesc.oper != GT_AND); - emit->emitIns_R_R_COND(INS_cinc, attr, targetReg, srcReg, JumpKindToInsCond(prevDesc.jumpKind1)); regSet.verifyRegUsed(targetReg); - genProduceReg(cinc); + genProduceReg(tree); } //------------------------------------------------------------------------ @@ -10365,53 +10352,6 @@ void CodeGen::genCodeForBfiz(GenTreeOp* tree) genProduceReg(tree); } -//------------------------------------------------------------------------ -// genCodeForCond: Generates the code sequence for a GenTree node that -// represents a conditional instruction. -// -// Arguments: -// tree - conditional op -// -void CodeGen::genCodeForCond(GenTreeOp* tree) -{ - assert(tree->OperIs(GT_CSNEG_MI, GT_CNEG_LT)); - assert(!(tree->gtFlags & GTF_SET_FLAGS)); - genConsumeOperands(tree); - - switch (tree->OperGet()) - { - case GT_CSNEG_MI: - { - instruction ins = INS_csneg; - insCond cond = INS_COND_MI; - - regNumber dstReg = tree->GetRegNum(); - regNumber op1Reg = tree->gtGetOp1()->GetRegNum(); - regNumber op2Reg = tree->gtGetOp2()->GetRegNum(); - - GetEmitter()->emitIns_R_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, op2Reg, cond); - break; - } - - case GT_CNEG_LT: - { - instruction ins = INS_cneg; - insCond cond = INS_COND_LT; - - regNumber dstReg = tree->GetRegNum(); - regNumber op1Reg = tree->gtGetOp1()->GetRegNum(); - - GetEmitter()->emitIns_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, cond); - break; - } - - default: - unreached(); - } - - genProduceReg(tree); -} - //------------------------------------------------------------------------ // JumpKindToInsCond: Convert a Jump Kind to a condition. // diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 80a62454618972..ce5b8d547503df 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -315,11 +315,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) case GT_BFIZ: genCodeForBfiz(treeNode->AsOp()); break; - - case GT_CSNEG_MI: - case GT_CNEG_LT: - genCodeForCond(treeNode->AsOp()); - break; #endif // TARGET_ARM64 case GT_JMP: @@ -355,15 +350,16 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) break; #ifdef TARGET_ARM64 + case GT_SELECT_NEG: + case GT_SELECT_INV: + case GT_SELECT_INC: case GT_SELECT: genCodeForSelect(treeNode->AsConditional()); break; - case GT_CINC: - case GT_CINCCC: - genCodeForCinc(treeNode->AsOp()); - break; - + case GT_SELECT_NEGCC: + case GT_SELECT_INVCC: + case GT_SELECT_INCCC: case GT_SELECTCC: genCodeForSelect(treeNode->AsOp()); break; diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 43a52fd8e8296b..b22bc7c519b0c9 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4290,11 +4290,7 @@ void GenTree::VisitOperands(TVisitor visitor) } FALLTHROUGH; -// Standard unary operators -#ifdef TARGET_ARM64 - case GT_CNEG_LT: - case GT_CINCCC: -#endif // TARGET_ARM64 + // Standard unary operators case GT_STORE_LCL_VAR: case GT_STORE_LCL_FLD: case GT_NOT: diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 8503a609e0c8bc..307909f257f339 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6361,11 +6361,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_IL_OFFSET: return false; -// Standard unary operators -#ifdef TARGET_ARM64 - case GT_CNEG_LT: - case GT_CINCCC: -#endif // TARGET_ARM64 + // Standard unary operators case GT_STORE_LCL_VAR: case GT_STORE_LCL_FLD: case GT_NOT: @@ -6554,7 +6550,11 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) } return false; } - +#ifdef TARGET_ARM64 + case GT_SELECT_NEG: + case GT_SELECT_INV: + case GT_SELECT_INC: +#endif case GT_SELECT: { GenTreeConditional* const conditional = this->AsConditional(); @@ -9713,11 +9713,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) m_state = -1; return; -// Standard unary operators -#ifdef TARGET_ARM64 - case GT_CNEG_LT: - case GT_CINCCC: -#endif // TARGET_ARM64 + // Standard unary operators case GT_STORE_LCL_VAR: case GT_STORE_LCL_FLD: case GT_NOT: @@ -9829,7 +9825,11 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) m_advance = &GenTreeUseEdgeIterator::AdvanceCall; AdvanceCall(); return; - +#ifdef TARGET_ARM64 + case GT_SELECT_NEG: + case GT_SELECT_INV: + case GT_SELECT_INC: +#endif case GT_SELECT: m_edge = &m_node->AsConditional()->gtCond; assert(*m_edge != nullptr); @@ -9955,8 +9955,15 @@ void GenTreeUseEdgeIterator::AdvanceConditional() switch (m_state) { case 0: - m_edge = &conditional->gtOp1; - m_state = 1; + m_edge = &conditional->gtOp1; + if (conditional->gtOp2 == nullptr) + { + m_advance = &GenTreeUseEdgeIterator::Terminate; + } + else + { + m_state = 1; + } break; case 1: m_edge = &conditional->gtOp2; @@ -12229,7 +12236,7 @@ void Compiler::gtDispTree(GenTree* tree, printf(" cond=%s", tree->AsOpCC()->gtCondition.Name()); } #ifdef TARGET_ARM64 - else if (tree->OperIs(GT_CINCCC)) + else if (tree->OperIs(GT_SELECT_INCCC, GT_SELECT_INVCC, GT_SELECT_NEGCC)) { printf(" cond=%s", tree->AsOpCC()->gtCondition.Name()); } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 9e5e13a1f02fc4..1a60f5fad0da14 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1679,7 +1679,7 @@ struct GenTree } #endif #if defined(TARGET_ARM64) - if (OperIs(GT_CCMP, GT_CINCCC)) + if (OperIs(GT_CCMP, GT_SELECT_INCCC, GT_SELECT_INVCC, GT_SELECT_NEGCC)) { return true; } @@ -1733,6 +1733,10 @@ struct GenTree #if defined(TARGET_ARM) case GT_PUTARG_REG: #endif // defined(TARGET_ARM) +#if defined(TARGET_ARM64) + case GT_SELECT_NEGCC: + case GT_SELECT_INCCC: +#endif // defined(TARGET_ARM64) return true; default: @@ -8637,7 +8641,11 @@ struct GenTreeOpCC : public GenTreeOp GenTreeOpCC(genTreeOps oper, var_types type, GenCondition condition, GenTree* op1 = nullptr, GenTree* op2 = nullptr) : GenTreeOp(oper, type, op1, op2 DEBUGARG(/*largeNode*/ FALSE)), gtCondition(condition) { +#ifdef TARGET_ARM64 + assert(OperIs(GT_SELECTCC, GT_SELECT_INCCC, GT_SELECT_INVCC, GT_SELECT_NEGCC)); +#else assert(OperIs(GT_SELECTCC)); +#endif } #if DEBUGGABLE_GENTREE diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 972fd9cd194186..33925ae801a87f 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -215,8 +215,6 @@ GTNODE(AND_NOT , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) #ifdef TARGET_ARM64 GTNODE(BFIZ , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Bitfield Insert in Zero. -GTNODE(CSNEG_MI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Conditional select, negate, minus result -GTNODE(CNEG_LT , GenTreeOp ,0,GTK_UNOP|DBK_NOTHIR) // Conditional, negate, signed less than result #endif //----------------------------------------------------------------------------- @@ -246,11 +244,21 @@ GTNODE(SELECTCC , GenTreeOpCC ,0,GTK_BINOP|DBK_NOTHIR) // operands and sets the condition flags according to the result. Otherwise // sets the condition flags to the specified immediate value. GTNODE(CCMP , GenTreeCCMP ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) -// Maps to arm64 cinc instruction. It returns the operand incremented by one when the condition is true. -// Otherwise returns the unchanged operand. Optimises for patterns such as, result = condition ? op1 + 1 : op1 -GTNODE(CINC , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) -// Variant of CINC that reuses flags computed by a previous node with the specified condition. -GTNODE(CINCCC , GenTreeOpCC ,0,GTK_UNOP|DBK_NOTHIR) +// Maps to arm64 csinc/cinc instruction. Computes result = condition ? op1 : op2 + 1. +// If op2 is null, computes result = condition ? op1 + 1 : op1. +GTNODE(SELECT_INC , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) +// Variant of SELECT_INC that reuses flags computed by a previous node with the specified condition. +GTNODE(SELECT_INCCC , GenTreeOpCC ,0,GTK_BINOP|DBK_NOTHIR) +// Maps to arm64 csinv/cinv instruction. Computes result = condition ? op1 : ~op2. +// If op2 is null, computes result = condition ? ~op1 : op1. +GTNODE(SELECT_INV , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) +// Variant of SELECT_INV that reuses flags computed by a previous node with the specified condition. +GTNODE(SELECT_INVCC , GenTreeOpCC ,0,GTK_BINOP|DBK_NOTHIR) +// Maps to arm64 csneg/cneg instruction.. Computes result = condition ? op1 : -op2. +// If op2 is null, computes result = condition ? -op1 : op1. +GTNODE(SELECT_NEG , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) +// Variant of SELECT_NEG that reuses flags computed by a previous node with the specified condition. +GTNODE(SELECT_NEGCC , GenTreeOpCC ,0,GTK_BINOP|DBK_NOTHIR) #endif //----------------------------------------------------------------------------- diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index b4d68694d9daee..317e0b3f071b77 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -94,7 +94,11 @@ GTSTRUCT_1(PhiArg , GT_PHI_ARG) GTSTRUCT_1(Phi , GT_PHI) GTSTRUCT_1(StoreInd , GT_STOREIND) GTSTRUCT_N(Indir , GT_STOREIND, GT_IND, GT_NULLCHECK, GT_BLK, GT_STORE_BLK, GT_STORE_DYN_BLK) +#ifdef TARGET_ARM64 +GTSTRUCT_N(Conditional , GT_SELECT, GT_SELECT_INC, GT_SELECT_INV, GT_SELECT_NEG) +#else GTSTRUCT_N(Conditional , GT_SELECT) +#endif //TARGET_ARM64 #if FEATURE_ARG_SPLIT GTSTRUCT_2_SPECIAL(PutArgStk, GT_PUTARG_STK, GT_PUTARG_SPLIT) GTSTRUCT_1(PutArgSplit , GT_PUTARG_SPLIT) @@ -111,7 +115,7 @@ GTSTRUCT_1(ArrAddr , GT_ARR_ADDR) GTSTRUCT_2(CC , GT_JCC, GT_SETCC) #ifdef TARGET_ARM64 GTSTRUCT_1(CCMP , GT_CCMP) -GTSTRUCT_4(OpCC , GT_SELECTCC, GT_CINCCC, GT_JCMP, GT_JTEST) +GTSTRUCT_N(OpCC , GT_SELECTCC, GT_SELECT_INCCC, GT_JCMP, GT_JTEST, GT_SELECT_INVCC, GT_SELECT_NEGCC) #else GTSTRUCT_3(OpCC , GT_SELECTCC, GT_JCMP, GT_JTEST) #endif diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 0af9216dc46618..536b4382d1450a 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3851,11 +3851,16 @@ GenTree* Lowering::LowerSelect(GenTreeConditional* select) } #ifdef TARGET_ARM64 - if (trueVal->IsCnsIntOrI() && falseVal->IsCnsIntOrI()) + if (trueVal->OperIs(GT_NOT, GT_NEG) || falseVal->OperIs(GT_NOT, GT_NEG)) + { + TryLowerCselToCinvOrCneg(select, cond); + } + else if (trueVal->IsCnsIntOrI() && falseVal->IsCnsIntOrI()) { TryLowerCselToCinc(select, cond); } #endif + return newSelect != nullptr ? newSelect->gtNext : select->gtNext; } diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 63f59208427fa5..aabb28592803be 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -89,6 +89,7 @@ class Lowering final : public Phase void ContainCheckConditionalCompare(GenTreeCCMP* ccmp); void ContainCheckNeg(GenTreeOp* neg); void TryLowerCselToCinc(GenTreeOp* select, GenTree* cond); + void TryLowerCselToCinvOrCneg(GenTreeOp* select, GenTree* cond); #endif void ContainCheckSelect(GenTreeOp* select); void ContainCheckBitCast(GenTree* node); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 83f7be1b91a016..64826c428b77fa 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -874,8 +874,11 @@ void Lowering::LowerModPow2(GenTree* node) BlockRange().InsertAfter(cnsZero, cmp); LowerNode(cmp); - mod->ChangeOper(GT_CNEG_LT); - mod->gtOp1 = trueExpr; + mod->ChangeOper(GT_SELECT_NEGCC); + GenTreeOpCC* node = mod->AsOpCC(); + node->gtOp1 = trueExpr; + node->gtOp2 = nullptr; + node->gtCondition = GenCondition::SLT; } else { @@ -900,9 +903,11 @@ void Lowering::LowerModPow2(GenTree* node) BlockRange().InsertAfter(cns2, falseExpr); LowerNode(falseExpr); - mod->ChangeOper(GT_CSNEG_MI); - mod->gtOp1 = trueExpr; - mod->gtOp2 = falseExpr; + mod->SetOper(GT_SELECT_NEGCC); + GenTreeOpCC* node = mod->AsOpCC(); + node->gtOp1 = trueExpr; + node->gtOp2 = falseExpr; + node->gtCondition = GenCondition::S; } ContainCheckNode(mod); @@ -2552,8 +2557,93 @@ void Lowering::ContainCheckNeg(GenTreeOp* neg) } //---------------------------------------------------------------------------------------------- -// Try converting SELECT/SELECTCC to CINC/CINCCC. Conversion is possible only if -// both the trueVal and falseVal are integral constants and abs(trueVal - falseVal) = 1. +// TryLowerCselToCinvOrCneg: Try converting SELECT/SELECTCC to SELECT_INV/SELECT_INVCC. Conversion is possible only if +// one of the operands of the select node is inverted. +// +// Arguments: +// select - The select node that is now SELECT or SELECTCC +// cond - The condition node that SELECT or SELECTCC uses +// +void Lowering::TryLowerCselToCinvOrCneg(GenTreeOp* select, GenTree* cond) +{ + assert(select->OperIs(GT_SELECT, GT_SELECTCC)); + + bool shouldReverseCondition; + GenTree* invertedOrNegatedVal; + GenTree* nonInvertedOrNegatedVal; + GenTree* nodeToRemove; + + GenTree* trueVal = select->gtOp1; + GenTree* falseVal = select->gtOp2; + const bool isCneg = trueVal->OperIs(GT_NEG) || falseVal->OperIs(GT_NEG); + + assert(trueVal->OperIs(GT_NOT, GT_NEG) || falseVal->OperIs(GT_NOT, GT_NEG)); + + if (trueVal->OperIs(GT_NOT) || trueVal->OperIs(GT_NEG)) + { + shouldReverseCondition = true; + invertedOrNegatedVal = trueVal->gtGetOp1(); + nonInvertedOrNegatedVal = falseVal; + nodeToRemove = trueVal; + } + else + { + shouldReverseCondition = false; + invertedOrNegatedVal = falseVal->gtGetOp1(); + nonInvertedOrNegatedVal = trueVal; + nodeToRemove = falseVal; + } + + if (shouldReverseCondition && !cond->OperIsCompare() && select->OperIs(GT_SELECT)) + { + // Non-compare nodes add additional GT_NOT node after reversing. + // This would remove gains from this optimisation so don't proceed. + return; + } + + if (!(IsInvariantInRange(invertedOrNegatedVal, select) && IsInvariantInRange(nonInvertedOrNegatedVal, select))) + { + return; + } + + // As the select node would handle the negation/inversion, the op is not required. + // If a value is contained in the negate/invert op, it cannot be contained anymore. + BlockRange().Remove(nodeToRemove); + invertedOrNegatedVal->ClearContained(); + select->gtOp1 = nonInvertedOrNegatedVal; + select->gtOp2 = invertedOrNegatedVal; + + if (select->OperIs(GT_SELECT)) + { + if (shouldReverseCondition) + { + GenTree* revCond = comp->gtReverseCond(cond); + assert(cond == revCond); // Ensure `gtReverseCond` did not create a new node. + } + select->SetOper(isCneg ? GT_SELECT_NEG : GT_SELECT_INV); + JITDUMP("Converted to: %s\n", isCneg ? "SELECT_NEG" : "SELECT_INV"); + DISPTREERANGE(BlockRange(), select); + JITDUMP("\n"); + } + else + { + GenTreeOpCC* selectcc = select->AsOpCC(); + GenCondition selectCond = selectcc->gtCondition; + if (shouldReverseCondition) + { + // Reverse the condition so that op2 will be selected + selectcc->gtCondition = GenCondition::Reverse(selectCond); + } + selectcc->SetOper(isCneg ? GT_SELECT_NEGCC : GT_SELECT_INVCC); + JITDUMP("Converted to: %s\n", isCneg ? "SELECT_NEGCC" : "SELECT_INVCC"); + DISPTREERANGE(BlockRange(), selectcc); + JITDUMP("\n"); + } +} + +//---------------------------------------------------------------------------------------------- +// TryLowerCselToCinc: Try converting SELECT/SELECTCC to SELECT_INC/SELECT_INCCC. Conversion is possible only if both +// the trueVal and falseVal are integral constants and abs(trueVal - falseVal) = 1. // // Arguments: // select - The select node that is now SELECT or SELECTCC @@ -2568,11 +2658,10 @@ void Lowering::TryLowerCselToCinc(GenTreeOp* select, GenTree* cond) size_t op1Val = (size_t)trueVal->AsIntCon()->IconValue(); size_t op2Val = (size_t)falseVal->AsIntCon()->IconValue(); - if (op1Val + 1 == op2Val || op2Val + 1 == op1Val) + if ((op1Val + 1 == op2Val) || (op2Val + 1 == op1Val)) { - const bool shouldReverseCondition = op1Val + 1 == op2Val; + const bool shouldReverseCondition = (op1Val + 1 == op2Val); - // Create a cinc node, insert it and update the use. if (select->OperIs(GT_SELECT)) { if (shouldReverseCondition) @@ -2584,18 +2673,21 @@ void Lowering::TryLowerCselToCinc(GenTreeOp* select, GenTree* cond) // This would remove gains from this optimisation so don't proceed. return; } - select->gtOp2 = select->gtOp1; GenTree* revCond = comp->gtReverseCond(cond); assert(cond == revCond); // Ensure `gtReverseCond` did not create a new node. } - select->gtOp1 = cond->AsOp(); - select->SetOper(GT_CINC); + BlockRange().Remove(select->gtOp2, true); + select->gtOp2 = nullptr; + select->SetOper(GT_SELECT_INC); + JITDUMP("Converted to: GT_SELECT_INC\n"); DISPTREERANGE(BlockRange(), select); + JITDUMP("\n"); } else { GenTreeOpCC* selectcc = select->AsOpCC(); GenCondition selectCond = selectcc->gtCondition; + if (shouldReverseCondition) { // Reverse the condition so that op2 will be selected @@ -2605,9 +2697,13 @@ void Lowering::TryLowerCselToCinc(GenTreeOp* select, GenTree* cond) { std::swap(selectcc->gtOp1, selectcc->gtOp2); } - BlockRange().Remove(selectcc->gtOp2); - selectcc->SetOper(GT_CINCCC); + + BlockRange().Remove(selectcc->gtOp2, true); + selectcc->gtOp2 = nullptr; + selectcc->SetOper(GT_SELECT_INCCC); + JITDUMP("Converted to: GT_SELECT_INCCC\n"); DISPTREERANGE(BlockRange(), selectcc); + JITDUMP("\n"); } } } diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 9452210abcd049..1bf1487d83ef10 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -62,6 +62,7 @@ struct Access weight_t CountWtd = 0; weight_t CountAssignmentSourceWtd = 0; weight_t CountAssignmentDestinationWtd = 0; + weight_t CountAssignedFromCallWtd = 0; weight_t CountCallArgsWtd = 0; weight_t CountReturnsWtd = 0; weight_t CountPassedAsRetbufWtd = 0; @@ -101,7 +102,8 @@ enum class AccessKindFlags : uint32_t IsAssignmentSource = 2, IsAssignmentDestination = 4, IsCallRetBuf = 8, - IsReturned = 16, + IsAssignedFromCall = 16, + IsReturned = 32, }; inline constexpr AccessKindFlags operator~(AccessKindFlags a) @@ -263,6 +265,11 @@ class LocalUses { access->CountAssignmentDestination++; access->CountAssignmentDestinationWtd += weight; + + if ((flags & AccessKindFlags::IsAssignedFromCall) != AccessKindFlags::None) + { + access->CountAssignedFromCallWtd += weight; + } } if ((flags & AccessKindFlags::IsCallArg) != AccessKindFlags::None) @@ -356,11 +363,11 @@ class LocalUses // bool EvaluateReplacement(Compiler* comp, unsigned lclNum, const Access& access) { - weight_t countOverlappedCallsWtd = 0; - weight_t countOverlappedReturnsWtd = 0; - weight_t countOverlappedRetbufsWtd = 0; - weight_t countOverlappedAssignmentDestinationWtd = 0; - weight_t countOverlappedAssignmentSourceWtd = 0; + weight_t countOverlappedCallArgWtd = 0; + weight_t countOverlappedReturnsWtd = 0; + weight_t countOverlappedRetbufsWtd = 0; + weight_t countOverlappedAssignedFromCallWtd = 0; + weight_t countOverlappedDecomposableAssignmentWtd = 0; bool overlap = false; for (const Access& otherAccess : m_accesses) @@ -378,52 +385,78 @@ class LocalUses return false; } - countOverlappedCallsWtd += otherAccess.CountCallArgsWtd; + countOverlappedCallArgWtd += otherAccess.CountCallArgsWtd; countOverlappedReturnsWtd += otherAccess.CountReturnsWtd; countOverlappedRetbufsWtd += otherAccess.CountPassedAsRetbufWtd; - countOverlappedAssignmentDestinationWtd += otherAccess.CountAssignmentDestinationWtd; - countOverlappedAssignmentSourceWtd += otherAccess.CountAssignmentSourceWtd; + countOverlappedAssignedFromCallWtd += otherAccess.CountAssignedFromCallWtd; + countOverlappedDecomposableAssignmentWtd += + (otherAccess.CountAssignmentDestinationWtd + otherAccess.CountAssignmentSourceWtd - + otherAccess.CountAssignedFromCallWtd); } - // TODO-CQ: Tune the following heuristics. Currently they are based on - // x64 code size although using BB weights when available. This mixing - // does not make sense. weight_t costWithout = 0; - // A normal access without promotion looks like: - // mov reg, [reg+offs] - // It may also be contained. Overall we are going to cost each use of - // an unpromoted local at 6.5 bytes. - // TODO-CQ: We can make much better guesses on what will and won't be contained. - costWithout += access.CountWtd * 6.5; + // We cost any normal access (which is a struct load or store) without promotion at 3 cycles. + costWithout += access.CountWtd * 3; weight_t costWith = 0; - // For any use we expect to just use the register directly. We will cost this at 3.5 bytes. - costWith += access.CountWtd * 3.5; + // For promoted accesses we expect these to turn into reg-reg movs (and in many cases be fully contained in the + // parent). + // We cost these at 0.5 cycles. + costWith += access.CountWtd * 0.5; + + // Now look at the overlapping struct uses that promotion will make more expensive. weight_t countReadBacksWtd = 0; LclVarDsc* lcl = comp->lvaGetDesc(lclNum); - // For parameters or OSR locals we need an initial read back + // For parameters or OSR locals we always need one read back. if (lcl->lvIsParam || lcl->lvIsOSRLocal) { countReadBacksWtd += comp->fgFirstBB->getBBWeight(comp); } + // If used as a retbuf we need a readback after. countReadBacksWtd += countOverlappedRetbufsWtd; - countReadBacksWtd += countOverlappedAssignmentDestinationWtd; - // A read back puts the value from stack back to (hopefully) register. We cost it at 5 bytes. - costWith += countReadBacksWtd * 5; + // The same if the struct was assigned from a call, since we don't + // currently have any "forwarding" optimization for this case. + countReadBacksWtd += countOverlappedAssignedFromCallWtd; + + // A readback turns into a stack load that we costed at 3 above. + costWith += countReadBacksWtd * 3; // Write backs with TYP_REFs when the base local is an implicit byref - // involves checked write barriers, so they are very expensive. + // involves checked write barriers, so they are very expensive. We cost that at 10 cycles. // TODO-CQ: This should be adjusted once we type implicit byrefs as TYP_I_IMPL. - weight_t writeBackCost = comp->lvaIsImplicitByRefLocal(lclNum) && (access.AccessType == TYP_REF) ? 15 : 5; - weight_t countWriteBacksWtd = - countOverlappedCallsWtd + countOverlappedReturnsWtd + countOverlappedAssignmentSourceWtd; + // Otherwise we cost it like a store to stack at 3 cycles. + weight_t writeBackCost = comp->lvaIsImplicitByRefLocal(lclNum) && (access.AccessType == TYP_REF) ? 10 : 3; + + // We write back before an overlapping struct use passed as an arg. + // TODO-CQ: A store-forwarding optimization in lowering could get rid + // of these copies; however, it requires lowering to be able to prove + // that not writing the fields into the struct local is ok. + weight_t countWriteBacksWtd = countOverlappedCallArgWtd; costWith += countWriteBacksWtd * writeBackCost; + // Overlapping assignments are decomposable so we don't cost them as + // being more expensive than their unpromoted counterparts (i.e. we + // don't consider them at all). However, we should do something more + // clever here, since: + // * We may still end up writing the full remainder as part of the + // decomposed assignment, in which case all the field writes are just + // added code size/perf cost. + // * Even if we don't, decomposing a single struct write into many + // field writes is not necessarily profitable (e.g. 16 byte field + // stores vs 1 XMM load/store). + // + // TODO-CQ: This ends up being a combinatorial optimization problem. We + // need to take a more "whole-struct" view here and look at sets of + // fields we are promoting together, evaluating all of them at once in + // comparison with the covering struct uses. This will also allow us to + // give a bonus to promoting remainders that may not have scalar uses + // but will allow fully decomposing assignments away. + JITDUMP(" Evaluating access %s @ %03u\n", varTypeName(access.AccessType), access.Offset); JITDUMP(" Single write-back cost: " FMT_WT "\n", writeBackCost); JITDUMP(" Write backs: " FMT_WT "\n", countWriteBacksWtd); @@ -611,6 +644,11 @@ class LocalsUseVisitor : public GenTreeVisitor if (lcl->OperIsLocalStore()) { flags |= AccessKindFlags::IsAssignmentDestination; + + if (lcl->AsLclVarCommon()->Data()->gtEffectiveVal()->IsCall()) + { + flags |= AccessKindFlags::IsAssignedFromCall; + } } if (user == nullptr) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 7cbcf4b6833dc5..2e124627d0a182 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -48,7 +48,7 @@ The .NET Foundation licenses this file to you under the MIT license. @executable_path libeventpipe-disabled - libeventpipe-enabled + libeventpipe-enabled diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props index 7fdf7430ac799d..76c5cb9eecbb72 100644 --- a/src/coreclr/nativeaot/Directory.Build.props +++ b/src/coreclr/nativeaot/Directory.Build.props @@ -66,7 +66,7 @@ false - true + true FEATURE_PERFTRACING;$(DefineConstants) diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index 81d08260015d95..a718812f23ddce 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -17,8 +17,9 @@ #include #include -#include "gcenv.structs.h" +#include "gcenv.structs.h" // CRITICAL_SECTION #include "IntrinsicConstants.h" +#include "PalRedhawkCommon.h" #ifndef PAL_REDHAWK_INCLUDED #define PAL_REDHAWK_INCLUDED @@ -50,7 +51,6 @@ #endif // !_MSC_VER #ifndef _INC_WINDOWS -//#ifndef DACCESS_COMPILE // There are some fairly primitive type definitions below but don't pull them into the rest of Redhawk unless // we have to (in which case these definitions will move to CommonTypes.h). @@ -65,14 +65,13 @@ typedef void * LPOVERLAPPED; #ifdef TARGET_UNIX #define __stdcall +typedef char TCHAR; +#define _T(s) s +#else +typedef wchar_t TCHAR; +#define _T(s) L##s #endif -#ifndef __GCENV_BASE_INCLUDED__ -#define CALLBACK __stdcall -#define WINAPI __stdcall -#define WINBASEAPI __declspec(dllimport) -#endif //!__GCENV_BASE_INCLUDED__ - #ifdef TARGET_UNIX #define DIRECTORY_SEPARATOR_CHAR '/' #else // TARGET_UNIX @@ -101,9 +100,6 @@ typedef struct _GUID { #define DECLARE_HANDLE(_name) typedef HANDLE _name -// defined in gcrhenv.cpp -bool __SwitchToThread(uint32_t dwSleepMSec, uint32_t dwSwitchCount); - struct FILETIME { uint32_t dwLowDateTime; @@ -502,7 +498,6 @@ typedef enum _EXCEPTION_DISPOSITION { #define NULL_AREA_SIZE (64*1024) #endif -//#endif // !DACCESS_COMPILE #endif // !_INC_WINDOWS @@ -510,10 +505,12 @@ typedef enum _EXCEPTION_DISPOSITION { #ifndef DACCESS_COMPILE #ifndef _INC_WINDOWS -#ifndef __GCENV_BASE_INCLUDED__ +#ifndef TRUE #define TRUE 1 +#endif +#ifndef FALSE #define FALSE 0 -#endif // !__GCENV_BASE_INCLUDED__ +#endif #define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1) @@ -750,6 +747,9 @@ REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalRegisterHijackCallback(_In_ PalH #ifdef FEATURE_ETW REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalEventEnabled(REGHANDLE regHandle, _In_ const EVENT_DESCRIPTOR* eventDescriptor); +REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalEventRegister(const GUID * arg1, void * arg2, void * arg3, REGHANDLE * arg4); +REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalEventUnregister(REGHANDLE arg1); +REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t arg3, EVENT_DATA_DESCRIPTOR * arg4); #endif REDHAWK_PALIMPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer); @@ -769,13 +769,15 @@ REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalCompatibleWaitAny(UInt32_BOOL alert REDHAWK_PALIMPORT void REDHAWK_PALAPI PalAttachThread(void* thread); REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalDetachThread(void* thread); -REDHAWK_PALIMPORT uint64_t PalGetCurrentThreadIdForLogging(); +REDHAWK_PALIMPORT uint64_t PalGetCurrentOSThreadId(); REDHAWK_PALIMPORT uint64_t PalQueryPerformanceCounter(); REDHAWK_PALIMPORT uint64_t PalQueryPerformanceFrequency(); REDHAWK_PALIMPORT void PalPrintFatalError(const char* message); +REDHAWK_PALIMPORT char* PalCopyTCharAsChar(const TCHAR* toCopy); + #ifdef TARGET_UNIX REDHAWK_PALIMPORT int32_t __cdecl _stricmp(const char *string1, const char *string2); #endif // TARGET_UNIX diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h b/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h index b03fc004c295ea..4c176593b9ff05 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h @@ -37,24 +37,6 @@ inline void PalEnterCriticalSection(CRITICAL_SECTION * arg1) EnterCriticalSection(arg1); } -extern "C" uint32_t __stdcall EventRegister(const GUID *, void *, void *, REGHANDLE *); -inline uint32_t PalEventRegister(const GUID * arg1, void * arg2, void * arg3, REGHANDLE * arg4) -{ - return EventRegister(arg1, arg2, arg3, arg4); -} - -extern "C" uint32_t __stdcall EventUnregister(REGHANDLE); -inline uint32_t PalEventUnregister(REGHANDLE arg1) -{ - return EventUnregister(arg1); -} - -extern "C" uint32_t __stdcall EventWrite(REGHANDLE, const EVENT_DESCRIPTOR *, uint32_t, EVENT_DATA_DESCRIPTOR *); -inline uint32_t PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t arg3, EVENT_DATA_DESCRIPTOR * arg4) -{ - return EventWrite(arg1, arg2, arg3, arg4); -} - extern "C" void __stdcall FlushProcessWriteBuffers(); inline void PalFlushProcessWriteBuffers() { diff --git a/src/coreclr/nativeaot/Runtime/RhConfig.cpp b/src/coreclr/nativeaot/Runtime/RhConfig.cpp index a321b37cff259a..928778b09f4650 100644 --- a/src/coreclr/nativeaot/Runtime/RhConfig.cpp +++ b/src/coreclr/nativeaot/Runtime/RhConfig.cpp @@ -11,60 +11,120 @@ #include -bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool decimal) +#define DOTNET_PREFIX _T("DOTNET_") +#define DOTNET_PREFIX_LEN STRING_LENGTH(DOTNET_PREFIX) + +namespace +{ + void GetEnvironmentConfigName(const char* name, TCHAR* buffer, uint32_t bufferSize) + { + assert(DOTNET_PREFIX_LEN + strlen(name) < bufferSize); + memcpy(buffer, DOTNET_PREFIX, (DOTNET_PREFIX_LEN) * sizeof(TCHAR)); + #ifdef TARGET_WINDOWS + size_t nameLen = strlen(name); + for (size_t i = 0; i < nameLen; i++) + { + buffer[DOTNET_PREFIX_LEN + i] = name[i]; + } + buffer[DOTNET_PREFIX_LEN + nameLen] = '\0'; + #else + strcpy(buffer + DOTNET_PREFIX_LEN, name); + #endif + } +} + +bool RhConfig::Environment::TryGetBooleanValue(const char* name, bool* value) +{ + uint64_t intValue; + if (!TryGetIntegerValue(name, &intValue)) + return false; + + *value = intValue != 0; + return true; +} + +bool RhConfig::Environment::TryGetIntegerValue(const char* name, uint64_t* value, bool decimal) { + TCHAR variableName[64]; + GetEnvironmentConfigName(name, variableName, ARRAY_SIZE(variableName)); + TCHAR buffer[CONFIG_VAL_MAXLEN + 1]; // hex digits plus a nul terminator. const uint32_t cchBuffer = ARRAY_SIZE(buffer); + uint32_t cchResult = PalGetEnvironmentVariable(variableName, buffer, cchBuffer); + if (cchResult == 0 || cchResult >= cchBuffer) + return false; - uint32_t cchResult = 0; - TCHAR variableName[64] = _T("DOTNET_"); - assert(ARRAY_SIZE("DOTNET_") - 1 + strlen(name) < ARRAY_SIZE(variableName)); -#ifdef TARGET_WINDOWS - for (size_t i = 0; i < strlen(name); i++) + // Environment variable was set. Convert it to an integer. + uint64_t uiResult = 0; + for (uint32_t i = 0; i < cchResult; i++) { - variableName[ARRAY_SIZE("DOTNET_") - 1 + i] = name[i]; - } -#else - strcat(variableName, name); -#endif + TCHAR ch = buffer[i]; - cchResult = PalGetEnvironmentVariable(variableName, buffer, cchBuffer); - if (cchResult != 0 && cchResult < cchBuffer) - { - // Environment variable was set. Convert it to an integer. - uint64_t uiResult = 0; - for (uint32_t i = 0; i < cchResult; i++) + if (decimal) { - TCHAR ch = buffer[i]; + uiResult *= 10; - if (decimal) - { - uiResult *= 10; - - if ((ch >= '0') && (ch <= '9')) - uiResult += ch - '0'; - else - return false; // parse error - } + if ((ch >= '0') && (ch <= '9')) + uiResult += ch - '0'; else - { - uiResult *= 16; - - if ((ch >= '0') && (ch <= '9')) - uiResult += ch - '0'; - else if ((ch >= 'a') && (ch <= 'f')) - uiResult += (ch - 'a') + 10; - else if ((ch >= 'A') && (ch <= 'F')) - uiResult += (ch - 'A') + 10; - else - return false; // parse error - } + return false; // parse error + } + else + { + uiResult *= 16; + + if ((ch >= '0') && (ch <= '9')) + uiResult += ch - '0'; + else if ((ch >= 'a') && (ch <= 'f')) + uiResult += (ch - 'a') + 10; + else if ((ch >= 'A') && (ch <= 'F')) + uiResult += (ch - 'A') + 10; + else + return false; // parse error } + } + + *value = uiResult; + return true; +} + +bool RhConfig::Environment::TryGetStringValue(const char* name, char** value) +{ + TCHAR variableName[64]; + GetEnvironmentConfigName(name, variableName, ARRAY_SIZE(variableName)); + + TCHAR buffer[260]; + uint32_t bufferLen = ARRAY_SIZE(buffer); + uint32_t actualLen = PalGetEnvironmentVariable(variableName, buffer, bufferLen); + if (actualLen == 0) + return false; - *pValue = uiResult; + if (actualLen < bufferLen) + { + *value = PalCopyTCharAsChar(buffer); return true; } + // Expand the buffer to get the value + bufferLen = actualLen + 1; + NewArrayHolder newBuffer {new (nothrow) TCHAR[bufferLen]}; + actualLen = PalGetEnvironmentVariable(variableName, newBuffer, bufferLen); + if (actualLen >= bufferLen) + return false; + +#ifdef TARGET_WINDOWS + *value = PalCopyTCharAsChar(newBuffer); +#else + *value = newBuffer.Extract(); +#endif + return true; +} + +bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool decimal) +{ + if (Environment::TryGetIntegerValue(name, pValue, decimal)) + return true; + // Check the embedded configuration const char *embeddedValue = nullptr; if (GetEmbeddedVariable(name, &embeddedValue)) diff --git a/src/coreclr/nativeaot/Runtime/RhConfig.h b/src/coreclr/nativeaot/Runtime/RhConfig.h index 86e8c38a52bcfd..214a79c5c93b24 100644 --- a/src/coreclr/nativeaot/Runtime/RhConfig.h +++ b/src/coreclr/nativeaot/Runtime/RhConfig.h @@ -14,13 +14,14 @@ #ifndef DACCESS_COMPILE +#include + class RhConfig { #define CONFIG_INI_NOT_AVAIL (void*)0x1 //signal for ini file failed to load #define CONFIG_KEY_MAXLEN 50 //arbitrary max length of config keys increase if needed #define CONFIG_VAL_MAXLEN 16 //64 bit uint in hex - private: struct ConfigPair { @@ -36,6 +37,15 @@ class RhConfig void* volatile g_embeddedSettings = NULL; public: + class Environment + { + public: // static + static bool TryGetBooleanValue(const char* name, bool* value); + static bool TryGetIntegerValue(const char* name, uint64_t* value, bool decimal = false); + + // Get environment variable configuration as a string. On success, the caller owns the returned string value. + static bool TryGetStringValue(const char* name, char** value); + }; bool ReadConfigValue(_In_z_ const char* wszName, uint64_t* pValue, bool decimal = false); diff --git a/src/coreclr/nativeaot/Runtime/SpinLock.h b/src/coreclr/nativeaot/Runtime/SpinLock.h index a343a1881f057c..56200d8b9398a6 100644 --- a/src/coreclr/nativeaot/Runtime/SpinLock.h +++ b/src/coreclr/nativeaot/Runtime/SpinLock.h @@ -3,6 +3,9 @@ #ifndef __SPINLOCK_H__ #define __SPINLOCK_H__ +// defined in gcrhenv.cpp +bool __SwitchToThread(uint32_t dwSleepMSec, uint32_t dwSwitchCount); + // #SwitchToThreadSpinning // // If you call __SwitchToThread in a loop waiting for a condition to be met, diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt index 287c50d0676550..d0b918e3aabc30 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt @@ -18,13 +18,25 @@ set (SHARED_EVENTPIPE_SOURCE_PATH "${CLR_SRC_NATIVE_DIR}/eventpipe") include (${SHARED_EVENTPIPE_SOURCE_PATH}/eventpipe.cmake) include (${SHARED_CONTAINERS_SOURCE_PATH}/containers.cmake) -list(APPEND SHARED_DIAGNOSTIC_SERVER_SOURCES - ds-ipc-pal-namedpipe.c -) +if(CLR_CMAKE_HOST_WIN32) + list(APPEND SHARED_DIAGNOSTIC_SERVER_SOURCES + ds-ipc-pal-namedpipe.c + ) -list(APPEND SHARED_DIAGNOSTIC_SERVER_HEADERS - ds-ipc-pal-namedpipe.h -) + list(APPEND SHARED_DIAGNOSTIC_SERVER_HEADERS + ds-ipc-pal-namedpipe.h + ) +endif(CLR_CMAKE_HOST_WIN32) + +if(CLR_CMAKE_HOST_UNIX) + list(APPEND SHARED_DIAGNOSTIC_SERVER_SOURCES + ds-ipc-pal-socket.c + ) + + list(APPEND SHARED_DIAGNOSTIC_SERVER_HEADERS + ds-ipc-pal-socket.h + ) +endif(CLR_CMAKE_HOST_UNIX) list(APPEND EVENTPIPE_SOURCES ${SHARED_EVENTPIPE_SOURCES} @@ -63,6 +75,7 @@ endif() list(APPEND AOT_EVENTPIPE_SHIM_SOURCES ${AOT_EVENTPIPE_SHIM_DIR}/ep-rt-aot.cpp + ${AOT_EVENTPIPE_SHIM_DIR}/ds-rt-aot.cpp ) list(APPEND AOT_EVENTPIPE_SHIM_HEADERS diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp new file mode 100644 index 00000000000000..9bb8d088a8d99b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#ifdef ENABLE_PERFTRACING +#include +#include +#include +#include + +#include "ds-rt-aot.h" + +bool aot_ipc_get_process_id_disambiguation_key(uint32_t process_id, uint64_t *key); + +bool +aot_ipc_get_process_id_disambiguation_key( + uint32_t process_id, + uint64_t *key) +{ + if (!key) { + EP_ASSERT (!"key argument cannot be null!"); + return false; + } + + *key = 0; + +// Mono implementation, restricted just to Unix +#ifdef TARGET_UNIX + + // Here we read /proc//stat file to get the start time for the process. + // We return this value (which is expressed in jiffies since boot time). + + // Making something like: /proc/123/stat + char stat_file_name [64]; + snprintf (stat_file_name, sizeof (stat_file_name), "/proc/%d/stat", process_id); + + FILE *stat_file = fopen (stat_file_name, "r"); + if (!stat_file) { + EP_ASSERT (!"Failed to get start time of a process, fopen failed."); + return false; + } + + bool result = false; + unsigned long long start_time = 0; + char *scan_start_position; + int result_sscanf; + + char *line = NULL; + size_t line_len = 0; + if (getline (&line, &line_len, stat_file) == -1) + { + EP_ASSERT (!"Failed to get start time of a process, getline failed."); + ep_raise_error (); + } + + + // According to `man proc`, the second field in the stat file is the filename of the executable, + // in parentheses. Tokenizing the stat file using spaces as separators breaks when that name + // has spaces in it, so we start using sscanf_s after skipping everything up to and including the + // last closing paren and the space after it. + scan_start_position = strrchr (line, ')'); + if (!scan_start_position || scan_start_position [1] == '\0') { + EP_ASSERT (!"Failed to parse stat file contents with strrchr."); + ep_raise_error (); + } + + scan_start_position += 2; + + // All the format specifiers for the fields in the stat file are provided by 'man proc'. + result_sscanf = sscanf (scan_start_position, + "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %llu \n", + &start_time); + + if (result_sscanf != 1) { + EP_ASSERT (!"Failed to parse stat file contents with sscanf."); + ep_raise_error (); + } + + free (line); + fclose (stat_file); + result = true; + +ep_on_exit: + *key = (uint64_t)start_time; + return result; + +ep_on_error: + free (line); + fclose (stat_file); + result = false; + ep_exit_error_handler (); + +#else + // If we don't have /proc, we just return false. + DS_LOG_WARNING_0 ("ipc_get_process_id_disambiguation_key was called but is not implemented on this platform!"); + return false; +#endif +} + +bool +ds_rt_aot_transport_get_default_name ( + ep_char8_t *name, + int32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix) +{ + STATIC_CONTRACT_NOTHROW; + +#ifdef TARGET_UNIX + + EP_ASSERT (name != NULL); + + bool result = false; + int32_t format_result = 0; + uint64_t disambiguation_key = 0; + ep_char8_t *format_buffer = NULL; + + *name = '\0'; + + format_buffer = (ep_char8_t *)malloc (name_len + 1); + ep_raise_error_if_nok (format_buffer != NULL); + + *format_buffer = '\0'; + + // If ipc_get_process_id_disambiguation_key failed for some reason, it should set the value + // to 0. We expect that anyone else making the pipe name will also fail and thus will + // also try to use 0 as the value. + if (!aot_ipc_get_process_id_disambiguation_key (id, &disambiguation_key)) + EP_ASSERT (disambiguation_key == 0); + + // Get a temp file location + format_result = ep_rt_temp_path_get (format_buffer, name_len); + if (format_result == 0) { + DS_LOG_ERROR_0 ("ep_rt_temp_path_get failed"); + ep_raise_error (); + } + + EP_ASSERT (format_result <= name_len); + + format_result = snprintf(name, name_len, "%s%s-%d-%llu-%s", format_buffer, prefix, id, (unsigned long long)disambiguation_key, suffix); + if (format_result <= 0 || format_result > name_len) { + DS_LOG_ERROR_0 ("name buffer too small"); + ep_raise_error (); + } + + result = true; + +ep_on_exit: + free (format_buffer); + return result; + +ep_on_error: + EP_ASSERT (!result); + name [0] = '\0'; + ep_exit_error_handler (); + +#else + return true; +#endif +} +#endif /* ENABLE_PERFTRACING */ diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index df255f7186e193..aaea67334eb0f4 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -191,11 +191,10 @@ ds_rt_transport_get_default_name ( const ep_char8_t *group_id, const ep_char8_t *suffix) { - STATIC_CONTRACT_NOTHROW; - - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: PAL_GetTransportName is defined in coreclr\pal\inc\pal.h - return true; + + extern bool ds_rt_aot_transport_get_default_name (ep_char8_t *name, int32_t name_len, const ep_char8_t *prefix, int32_t id, const ep_char8_t *group_id, const ep_char8_t *suffix); + + return ds_rt_aot_transport_get_default_name(name, name_len, prefix, id, group_id, suffix); } /* diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp index fea284db7f5df4..c629f2b86c5eb6 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp @@ -9,6 +9,14 @@ #include #include +#ifdef TARGET_WINDOWS +#include +#else +#include +#include +#include +#endif + // The regdisplay.h, StackFrameIterator.h, and thread.h includes are present only to access the Thread // class and can be removed if it turns out that the required ep_rt_thread_handle_t can be // implemented in some manner that doesn't rely on the Thread class. @@ -20,13 +28,19 @@ #include "holder.h" #include "SpinLock.h" +#ifdef TARGET_UNIX +// Per module (1 for NativeAOT), key that will be used to implement TLS in Unix +pthread_key_t eventpipe_tls_key; +__thread EventPipeThreadHolder* eventpipe_tls_instance; +#else +thread_local EventPipeAotThreadHolderTLS EventPipeAotThreadHolderTLS::g_threadHolderTLS; +#endif + // Uses _rt_aot_lock_internal_t that has CrstStatic as a field // This is initialized at the beginning and EventPipe library requires the lock handle to be maintained by the runtime ep_rt_lock_handle_t _ep_rt_aot_config_lock_handle; CrstStatic _ep_rt_aot_config_lock; -thread_local EventPipeAotThreadHolderTLS EventPipeAotThreadHolderTLS::g_threadHolderTLS; - ep_char8_t *volatile _ep_rt_aot_diagnostics_cmd_line; #ifndef TARGET_UNIX @@ -297,10 +311,7 @@ ep_rt_aot_current_thread_get_id (void) STATIC_CONTRACT_NOTHROW; #ifdef TARGET_UNIX - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: AOT doesn't have PAL_GetCurrentOSThreadId, as CoreCLR does. - // PalDebugBreak(); - return static_cast(0); + return static_cast(PalGetCurrentOSThreadId()); #else return static_cast(::GetCurrentThreadId ()); #endif @@ -330,6 +341,73 @@ ep_rt_aot_system_timestamp_get (void) return static_cast(((static_cast(value.dwHighDateTime)) << 32) | static_cast(value.dwLowDateTime)); } +ep_rt_file_handle_t +ep_rt_aot_file_open_write (const ep_char8_t *path) +{ + if (!path) + return INVALID_HANDLE_VALUE; + +#ifdef TARGET_WINDOWS + ep_char16_t *path_utf16 = ep_rt_utf8_to_utf16le_string (path, -1); + if (!path_utf16) + return INVALID_HANDLE_VALUE; + + HANDLE res = ::CreateFileW (reinterpret_cast(path_utf16), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ep_rt_utf16_string_free (path_utf16); + return static_cast(res); +#else + mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + int fd = creat (path, perms); + if (fd == -1) + return INVALID_HANDLE_VALUE; + + return (ep_rt_file_handle_t)(ptrdiff_t)fd; +#endif +} + +bool +ep_rt_aot_file_close (ep_rt_file_handle_t file_handle) +{ +#ifdef TARGET_WINDOWS + return ::CloseHandle (file_handle) != FALSE; +#else + int fd = (int)(ptrdiff_t)file_handle; + close (fd); + return true; +#endif +} + +bool +ep_rt_aot_file_write ( + ep_rt_file_handle_t file_handle, + const uint8_t *buffer, + uint32_t bytes_to_write, + uint32_t *bytes_written) +{ +#ifdef TARGET_WINDOWS + return ::WriteFile (file_handle, buffer, bytes_to_write, reinterpret_cast(bytes_written), NULL) != FALSE; +#else + int fd = (int)(ptrdiff_t)file_handle; + int ret; + do { + ret = write (fd, buffer, bytes_to_write); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + if (bytes_written != NULL) { + *bytes_written = 0; + } + + return false; + } + + if (bytes_written != NULL) + *bytes_written = ret; + + return true; +#endif +} + uint8_t * ep_rt_aot_valloc0 (size_t buffer_size) { @@ -378,7 +456,15 @@ ep_rt_aot_utf16_string_len (const ep_char16_t *str) STATIC_CONTRACT_NOTHROW; EP_ASSERT (str != NULL); - return wcslen (reinterpret_cast(str)); + #ifdef TARGET_UNIX + const uint16_t *a = (const uint16_t *)str; + size_t length = 0; + while (a [length]) + ++length; + return length; + #else + return wcslen (reinterpret_cast(str)); + #endif } uint32_t @@ -509,6 +595,17 @@ ep_rt_aot_volatile_store_ptr_without_barrier ( VolatileStoreWithoutBarrier ((void **)ptr, value); } +void unix_tls_callback_fn(void *value) +{ + if (value) { + // we need to do the unallocation here + EventPipeThreadHolder *thread_holder_old = static_cast(value); + // @TODO - inline + thread_holder_free_func (thread_holder_old); + value = NULL; + } +} + void ep_rt_aot_init (void) { extern ep_rt_lock_handle_t _ep_rt_aot_config_lock_handle; @@ -516,6 +613,11 @@ void ep_rt_aot_init (void) _ep_rt_aot_config_lock_handle.lock = &_ep_rt_aot_config_lock; _ep_rt_aot_config_lock_handle.lock->InitNoThrow (CrstType::CrstEventPipeConfig); + + // Initialize the pthread key used for TLS in Unix + #ifdef TARGET_UNIX + pthread_key_create(&eventpipe_tls_key, unix_tls_callback_fn); + #endif } bool ep_rt_aot_lock_acquire (ep_rt_lock_handle_t *lock) diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h index 786a67921b37cd..d17566514a379f 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h @@ -6,6 +6,10 @@ #define __EVENTPIPE_RT_AOT_H__ #include // For isspace +#ifdef TARGET_UNIX +#include +#include +#endif #include #ifdef ENABLE_PERFTRACING @@ -15,6 +19,7 @@ #include #include "rhassert.h" +#include #ifdef TARGET_UNIX #define sprintf_s snprintf @@ -42,6 +47,11 @@ #undef EP_ALIGN_UP #define EP_ALIGN_UP(val,align) _rt_aot_align_up(val,align) +#ifdef TARGET_UNIX +extern pthread_key_t eventpipe_tls_key; +extern __thread EventPipeThreadHolder* eventpipe_tls_instance; +#endif + // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase // TODO: The NativeAOT ALIGN_UP is defined in a tangled manner that generates linker errors if // it is used here; instead, define a version tailored to the existing usage in the shared @@ -62,7 +72,7 @@ ep_rt_lock_handle_t * ep_rt_aot_config_lock_get (void) { extern ep_rt_lock_handle_t _ep_rt_aot_config_lock_handle; - return &_ep_rt_aot_config_lock_handle; + return &_ep_rt_aot_config_lock_handle; } static @@ -406,11 +416,11 @@ inline bool ep_rt_config_value_get_enable (void) { - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: EventPipe Configuration values - RhConfig? - // (CLRConfig::INTERNAL_EnableEventPipe) != 0 - // If EventPipe environment variables are specified, parse them and start a session. - // TODO: Not start a session for now + // See https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables + bool value; + if (RhConfig::Environment::TryGetBooleanValue("EnableEventPipe", &value)) + return value; + return false; } @@ -420,12 +430,13 @@ ep_char8_t * ep_rt_config_value_get_config (void) { STATIC_CONTRACT_NOTHROW; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: EventPipe Configuration values - RhConfig? - // (CLRConfig::INTERNAL_EventPipeConfig) - // PalDebugBreak(); + + // See https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables + char* value; + if (RhConfig::Environment::TryGetStringValue("EventPipeConfig", &value)) + return (ep_char8_t*)value; + return nullptr; -// return ep_rt_utf16_to_utf8_string (reinterpret_cast(value.GetValue ()), -1); } static @@ -434,10 +445,12 @@ ep_char8_t * ep_rt_config_value_get_output_path (void) { STATIC_CONTRACT_NOTHROW; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: EventPipe Configuration values - RhConfig? - // (CLRConfig::INTERNAL_EventPipeOutputPath) - //PalDebugBreak(); + + // See https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables + char* value; + if (RhConfig::Environment::TryGetStringValue("EventPipeOutputPath", &value)) + return (ep_char8_t*)value; + return nullptr; } @@ -447,10 +460,15 @@ uint32_t ep_rt_config_value_get_circular_mb (void) { STATIC_CONTRACT_NOTHROW; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: EventPipe Configuration values - RhConfig? - // (CLRConfig::INTERNAL_EventPipeCircularMB) - //PalDebugBreak(); + + // See https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables + uint64_t value; + if (RhConfig::Environment::TryGetIntegerValue("EventPipeCircularMB", &value)) + { + EP_ASSERT(value <= UINT32_MAX); + return static_cast(value); + } + return 0; } @@ -460,10 +478,11 @@ bool ep_rt_config_value_get_output_streaming (void) { STATIC_CONTRACT_NOTHROW; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: EventPipe Configuration values - RhConfig? - // (CLRConfig::INTERNAL_EventPipeOutputStreaming) - //PalDebugBreak(); + + bool value; + if (RhConfig::Environment::TryGetBooleanValue("EventPipeOutputStreaming", &value)) + return value; + return false; } @@ -473,10 +492,11 @@ bool ep_rt_config_value_get_enable_stackwalk (void) { STATIC_CONTRACT_NOTHROW; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: EventPipe Configuration values - RhConfig? - // (CLRConfig::INTERNAL_EventPipeEnableStackwalk) - //PalDebugBreak(); + + bool value; + if (RhConfig::Environment::TryGetBooleanValue("EventPipeEnableStackwalk", &value)) + return value; + return true; } @@ -667,28 +687,28 @@ ep_rt_create_activity_id ( uint8_t data1[] = {0x67,0xac,0x33,0xf1,0x8d,0xed,0x41,0x01,0xb4,0x26,0xc9,0xb7,0x94,0x35,0xf7,0x8a}; memcpy (activity_id, data1, EP_ACTIVITY_ID_SIZE); - const uint16_t version_mask = 0xF000; - const uint16_t random_guid_version = 0x4000; - const uint8_t clock_seq_hi_and_reserved_mask = 0xC0; - const uint8_t clock_seq_hi_and_reserved_value = 0x80; + const uint16_t version_mask = 0xF000; + const uint16_t random_guid_version = 0x4000; + const uint8_t clock_seq_hi_and_reserved_mask = 0xC0; + const uint8_t clock_seq_hi_and_reserved_value = 0x80; - // Modify bits indicating the type of the GUID - uint8_t *activity_id_c = activity_id + sizeof (uint32_t) + sizeof (uint16_t); - uint8_t *activity_id_d = activity_id + sizeof (uint32_t) + sizeof (uint16_t) + sizeof (uint16_t); + // Modify bits indicating the type of the GUID + uint8_t *activity_id_c = activity_id + sizeof (uint32_t) + sizeof (uint16_t); + uint8_t *activity_id_d = activity_id + sizeof (uint32_t) + sizeof (uint16_t) + sizeof (uint16_t); - uint16_t c; - memcpy (&c, activity_id_c, sizeof (c)); + uint16_t c; + memcpy (&c, activity_id_c, sizeof (c)); - uint8_t d; - memcpy (&d, activity_id_d, sizeof (d)); + uint8_t d; + memcpy (&d, activity_id_d, sizeof (d)); - // time_hi_and_version - c = ((c & ~version_mask) | random_guid_version); - // clock_seq_hi_and_reserved - d = ((d & ~clock_seq_hi_and_reserved_mask) | clock_seq_hi_and_reserved_value); + // time_hi_and_version + c = ((c & ~version_mask) | random_guid_version); + // clock_seq_hi_and_reserved + d = ((d & ~clock_seq_hi_and_reserved_mask) | clock_seq_hi_and_reserved_value); - memcpy (activity_id_c, &c, sizeof (c)); - memcpy (activity_id_d, &d, sizeof (d)); + memcpy (activity_id_c, &c, sizeof (c)); + memcpy (activity_id_d, &d, sizeof (d)); } static @@ -898,19 +918,54 @@ ep_rt_system_time_get (EventPipeSystemTime *system_time) EP_ASSERT(system_time != NULL); ep_system_time_set ( - system_time, - value.wYear, - value.wMonth, - value.wDayOfWeek, - value.wDay, - value.wHour, - value.wMinute, - value.wSecond, - value.wMilliseconds); -#else - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Get System time - // PalDebugBreak(); + system_time, + value.wYear, + value.wMonth, + value.wDayOfWeek, + value.wDay, + value.wHour, + value.wMinute, + value.wSecond, + value.wMilliseconds); +#elif TARGET_UNIX + time_t tt; + struct tm *ut_ptr; + struct timeval time_val; + int timeofday_retval; + + EP_ASSERT (system_time != NULL); + + tt = time (NULL); + + timeofday_retval = gettimeofday (&time_val, NULL); + + ut_ptr = gmtime (&tt); + + uint16_t milliseconds = 0; + if (timeofday_retval != -1) { + int old_seconds; + int new_seconds; + + milliseconds = (uint16_t)(time_val.tv_usec / 1000); + + old_seconds = ut_ptr->tm_sec; + new_seconds = time_val.tv_sec % 60; + + /* just in case we reached the next second in the interval between time () and gettimeofday () */ + if (old_seconds != new_seconds) + milliseconds = 999; + } + + ep_system_time_set ( + system_time, + (uint16_t)(1900 + ut_ptr->tm_year), + (uint16_t)ut_ptr->tm_mon + 1, + (uint16_t)ut_ptr->tm_wday, + (uint16_t)ut_ptr->tm_mday, + (uint16_t)ut_ptr->tm_hour, + (uint16_t)ut_ptr->tm_min, + (uint16_t)ut_ptr->tm_sec, + milliseconds); #endif } @@ -952,14 +1007,8 @@ ep_rt_file_open_write (const ep_char8_t *path) { STATIC_CONTRACT_NOTHROW; - ep_char16_t *path_utf16 = ep_rt_utf8_to_utf16le_string (path, -1); - ep_return_null_if_nok (path_utf16 != NULL); - - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Find out the way to open a file in native - // PalDebugBreak(); - - return 0; + extern ep_rt_file_handle_t ep_rt_aot_file_open_write (const ep_char8_t *); + return ep_rt_aot_file_open_write (path); } static @@ -969,10 +1018,8 @@ ep_rt_file_close (ep_rt_file_handle_t file_handle) { STATIC_CONTRACT_NOTHROW; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Find out the way to close a file in native - // PalDebugBreak(); - return true; + extern bool ep_rt_aot_file_close (ep_rt_file_handle_t); + return ep_rt_aot_file_close (file_handle); } static @@ -987,11 +1034,8 @@ ep_rt_file_write ( STATIC_CONTRACT_NOTHROW; EP_ASSERT (buffer != NULL); - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Find out the way to write to a file in native - // PalDebugBreak(); - - return false; + extern bool ep_rt_aot_file_write (ep_rt_file_handle_t, const uint8_t*, uint32_t, uint32_t*); + return ep_rt_aot_file_write (file_handle, buffer, bytes_to_write, bytes_written); } static @@ -1030,9 +1074,37 @@ ep_rt_temp_path_get ( uint32_t buffer_len) { STATIC_CONTRACT_NOTHROW; -// EP_UNREACHABLE ("Can not reach here"); +#ifdef TARGET_UNIX + + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len > 0); + + const ep_char8_t *path = getenv ("TMPDIR"); + if (path == NULL){ + path = getenv ("TMP"); + if (path == NULL){ + path = getenv ("TEMP"); + if (path == NULL) + path = "/tmp/"; + } + } + + int32_t result = snprintf (buffer, buffer_len, path[strlen(path) - 1] == '/' ? "%s" : "%s/", path); + if (result <= 0 || (uint32_t)result >= buffer_len) + ep_raise_error (); + + +ep_on_exit: + return result; + +ep_on_error: + result = 0; + ep_exit_error_handler (); + +#else return 0; +#endif } static @@ -1313,8 +1385,9 @@ ep_rt_utf8_to_utf16le_string ( if (!str) return NULL; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Implementation would just use strlen and malloc to make a new buffer, and would then copy the string chars one by one + // Shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase + // Implementation would just use strlen and malloc to make a new buffer, and would then copy the string chars one by one. + // Assumes that only ASCII is used for ep_char8_t size_t len_utf8 = strlen(str); if (len_utf8 == 0) return NULL; @@ -1325,6 +1398,7 @@ ep_rt_utf8_to_utf16le_string ( for (size_t i = 0; i < len_utf8; i++) { + EP_ASSERT(isascii(str[i])); str_utf16[i] = str[i]; } @@ -1383,12 +1457,10 @@ ep_rt_utf16_to_utf8_string ( return NULL; // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Temp implementation that is the reverse of ep_rt_utf8_to_utf16le_string + // Simple implementation to create a utf8 string from a utf16 one size_t len_utf16 = len; if(len_utf16 == (size_t)-1) - { len_utf16 = ep_rt_utf16_string_len (str); - } ep_char8_t *str_utf8 = reinterpret_cast(malloc ((len_utf16 + 1) * sizeof (ep_char8_t))); if (!str_utf8) @@ -1531,6 +1603,44 @@ ep_rt_thread_setup (void) // EP_ASSERT (thread_handle != NULL); } +#ifdef TARGET_UNIX +static +inline +EventPipeThreadHolder * +pthread_getThreadHolder (void) +{ + void *value = eventpipe_tls_instance; + if (value) { + EventPipeThreadHolder *thread_holder = static_cast(value); + return thread_holder; + } + return NULL; +} + +static +inline +EventPipeThreadHolder * +pthread_createThreadHolder (void) +{ + void *value = eventpipe_tls_instance; + if (value) { + // we need to do the unallocation here + EventPipeThreadHolder *thread_holder_old = static_cast(value); + thread_holder_free_func(thread_holder_old); + eventpipe_tls_instance = NULL; + + value = NULL; + } + EventPipeThreadHolder *instance = thread_holder_alloc_func(); + if (instance){ + // We need to know when the thread is no longer in use to clean up EventPipeThreadHolder instance and will use pthread destructor function to get notification when that happens. + pthread_setspecific(eventpipe_tls_key, instance); + eventpipe_tls_instance = instance; + } + return instance; +} +#endif + static inline EventPipeThread * @@ -1538,7 +1648,11 @@ ep_rt_thread_get (void) { STATIC_CONTRACT_NOTHROW; +#ifdef TARGET_UNIX + EventPipeThreadHolder *thread_holder = pthread_getThreadHolder (); +#else EventPipeThreadHolder *thread_holder = EventPipeAotThreadHolderTLS::getThreadHolder (); +#endif return thread_holder ? ep_thread_holder_get_thread (thread_holder) : NULL; } @@ -1549,9 +1663,15 @@ ep_rt_thread_get_or_create (void) { STATIC_CONTRACT_NOTHROW; - EventPipeThreadHolder *thread_holder = EventPipeAotThreadHolderTLS::getThreadHolder (); +#ifdef TARGET_UNIX + EventPipeThreadHolder *thread_holder = pthread_getThreadHolder (); + if (!thread_holder) + thread_holder = pthread_createThreadHolder (); +#else + EventPipeThreadHolder *thread_holder = EventPipeAotThreadHolderTLS::getThreadHolder (); if (!thread_holder) thread_holder = EventPipeAotThreadHolderTLS::createThreadHolder (); +#endif return ep_thread_holder_get_thread (thread_holder); } diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-types-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-types-aot.h index df06d264c8d7b4..22c777f692812e 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-types-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-types-aot.h @@ -58,7 +58,7 @@ typedef class MethodDesc ep_rt_method_desc_t; */ #undef ep_rt_file_handle_t -typedef class CFileStream * ep_rt_file_handle_t; +typedef void * ep_rt_file_handle_t; #undef ep_rt_wait_event_handle_t typedef struct _rt_aot_event_internal_t ep_rt_wait_event_handle_t; diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index ed44c9e948a485..a178a11d102fae 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -474,22 +474,24 @@ static void UninitDLL() // the process is terminated via `exit()` or a signal. Thus there is no such distinction // between threads. Thread* g_threadPerformingShutdown = NULL; +#endif static void __cdecl OnProcessExit() { +#ifdef _WIN32 // The process is exiting and the current thread is performing the shutdown. // When this thread exits some threads may be already rudely terminated. // It would not be a good idea for this thread to wait on any locks // or run managed code at shutdown, so we will not try detaching it. Thread* currentThread = ThreadStore::RawGetCurrentThread(); g_threadPerformingShutdown = currentThread; +#endif #ifdef FEATURE_PERFTRACING EventPipeAdapter_Shutdown(); DiagnosticServerAdapter_Shutdown(); #endif } -#endif void RuntimeThreadShutdown(void* thread) { @@ -526,7 +528,7 @@ extern "C" bool RhInitialize() if (!PalInit()) return false; -#ifdef _WIN32 +#if defined(_WIN32) || defined(FEATURE_PERFTRACING) atexit(&OnProcessExit); #endif diff --git a/src/coreclr/nativeaot/Runtime/stressLog.cpp b/src/coreclr/nativeaot/Runtime/stressLog.cpp index 3c8dd0f80a0891..ed2c02a5ee58ee 100644 --- a/src/coreclr/nativeaot/Runtime/stressLog.cpp +++ b/src/coreclr/nativeaot/Runtime/stressLog.cpp @@ -334,7 +334,7 @@ void ThreadStressLog::LogMsg ( uint32_t facility, int cArgs, const char* format, msg->args[i] = data; } - ASSERT(IsValid() && threadId == PalGetCurrentThreadIdForLogging()); + ASSERT(IsValid() && threadId == PalGetCurrentOSThreadId()); } @@ -342,7 +342,7 @@ void ThreadStressLog::Activate (Thread * pThread) { _ASSERTE(pThread != NULL); //there is no need to zero buffers because we could handle garbage contents - threadId = PalGetCurrentThreadIdForLogging(); + threadId = PalGetCurrentOSThreadId(); isDead = FALSE; curWriteChunk = chunkListTail; curPtr = (StressMsg *)curWriteChunk->EndPtr (); diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 4d8c2c29d1a780..9eb428c3d0c402 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -1307,7 +1307,7 @@ COOP_PINVOKE_HELPER(uint8_t*, RhCurrentNativeThreadId, ()) // This function is used to get the OS thread identifier for the current thread. COOP_PINVOKE_HELPER(uint64_t, RhCurrentOSThreadId, ()) { - return PalGetCurrentThreadIdForLogging(); + return PalGetCurrentOSThreadId(); } // Standard calling convention variant and actual implementation for RhpReversePInvokeAttachOrTrapThread diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index 407adf5838626c..a856be48f4ab8c 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -738,6 +738,13 @@ REDHAWK_PALEXPORT void PalPrintFatalError(const char* message) (void)!write(STDERR_FILENO, message, strlen(message)); } +REDHAWK_PALEXPORT char* PalCopyTCharAsChar(const TCHAR* toCopy) +{ + NewArrayHolder copy {new (nothrow) char[strlen(toCopy) + 1]}; + strcpy(copy, toCopy); + return copy.Extract(); +} + static int W32toUnixAccessControl(uint32_t flProtect) { int prot = 0; @@ -1256,7 +1263,7 @@ extern "C" uint64_t PalQueryPerformanceFrequency() return GCToOSInterface::QueryPerformanceFrequency(); } -extern "C" uint64_t PalGetCurrentThreadIdForLogging() +extern "C" uint64_t PalGetCurrentOSThreadId() { #if defined(__linux__) return (uint64_t)syscall(SYS_gettid); diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 568d9c1cdb000c..c7b1f3e313fa39 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -24,11 +24,6 @@ #define PalRaiseFailFastException RaiseFailFastException -uint32_t PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t arg3, EVENT_DATA_DESCRIPTOR * arg4) -{ - return EventWrite(arg1, arg2, arg3, arg4); -} - #include "gcenv.h" #include "gcenv.ee.h" #include "gcconfig.h" @@ -227,7 +222,7 @@ extern "C" uint64_t PalQueryPerformanceFrequency() return GCToOSInterface::QueryPerformanceFrequency(); } -extern "C" uint64_t PalGetCurrentThreadIdForLogging() +extern "C" uint64_t PalGetCurrentOSThreadId() { return GetCurrentThreadId(); } @@ -634,6 +629,21 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalEventEnabled(REGHANDLE regHandle, _In_ return !!EventEnabled(regHandle, eventDescriptor); } +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalEventRegister(const GUID * arg1, void * arg2, void * arg3, REGHANDLE * arg4) +{ + return EventRegister(arg1, reinterpret_cast(arg2), arg3, arg4); +} + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalEventUnregister(REGHANDLE arg1) +{ + return EventUnregister(arg1); +} + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t arg3, EVENT_DATA_DESCRIPTOR * arg4) +{ + return EventWrite(arg1, arg2, arg3, arg4); +} + REDHAWK_PALEXPORT void REDHAWK_PALAPI PalTerminateCurrentProcess(uint32_t arg2) { TerminateProcess(GetCurrentProcess(), arg2); @@ -719,6 +729,18 @@ REDHAWK_PALEXPORT void PalPrintFatalError(const char* message) WriteFile(GetStdHandle(STD_ERROR_HANDLE), message, (DWORD)strlen(message), &dwBytesWritten, NULL); } +REDHAWK_PALEXPORT char* PalCopyTCharAsChar(const TCHAR* toCopy) +{ + int len = ::WideCharToMultiByte(CP_UTF8, 0, toCopy, -1, nullptr, 0, nullptr, nullptr); + if (len == 0) + return nullptr; + + char* converted = new (nothrow) char[len]; + int written = ::WideCharToMultiByte(CP_UTF8, 0, toCopy, -1, converted, len, nullptr, nullptr); + assert(len == written); + return converted; +} + REDHAWK_PALEXPORT _Ret_maybenull_ _Post_writable_byte_size_(size) void* REDHAWK_PALAPI PalVirtualAlloc(_In_opt_ void* pAddress, uintptr_t size, uint32_t allocationType, uint32_t protect) { return VirtualAlloc(pAddress, size, allocationType, protect); diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 1f1c6f8678ce88..3936253be7ace5 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -907,7 +907,20 @@ public void TotalProcessorTime_PerformLoop_TotalProcessorTimeValid() double cpuUsage = cpuTimeDiff / (timeDiff * Environment.ProcessorCount); - Assert.InRange(cpuUsage, 0, 1); + try + { + Assert.InRange(cpuUsage, 0, 1); // InRange is an inclusive test + } + catch (InRangeException) + { + string msg = $"Assertion failed. {cpuUsage} is not in range [0,1]. " + + $"proc time before:{processorTimeBeforeSpin.TotalMilliseconds} " + + $"proc time after:{processorTimeAfterSpin.TotalMilliseconds} " + + $"timeDiff:{timeDiff} " + + $"cpuTimeDiff:{cpuTimeDiff} " + + $"Environment.ProcessorCount:{Environment.ProcessorCount}"; + throw new XunitException(msg); + } } [Fact] diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs index bbf5b000423c25..5dfa892193d972 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs @@ -30,6 +30,8 @@ public BinaryData(string data) { } public static System.BinaryData FromString(string data) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override int GetHashCode() { throw null; } + public bool IsEmpty { get { throw null; } } + public int Length { get { throw null; } } public static implicit operator System.ReadOnlyMemory (System.BinaryData? data) { throw null; } public static implicit operator System.ReadOnlySpan (System.BinaryData? data) { throw null; } public byte[] ToArray() { throw null; } diff --git a/src/libraries/System.Memory.Data/src/System/BinaryData.cs b/src/libraries/System.Memory.Data/src/System/BinaryData.cs index 82615b3a61e03e..0832a5bd7bcfba 100644 --- a/src/libraries/System.Memory.Data/src/System/BinaryData.cs +++ b/src/libraries/System.Memory.Data/src/System/BinaryData.cs @@ -32,6 +32,18 @@ public class BinaryData /// public static BinaryData Empty { get; } = new BinaryData(ReadOnlyMemory.Empty); + /// + /// Gets the number of bytes of this data. + /// + /// The number of bytes of this data. + public int Length => _bytes.Length; + + /// + /// Gets a value that indicates whether this data is empty. + /// + /// if the data is empty (that is, its is 0); otherwise, . + public bool IsEmpty => _bytes.IsEmpty; + /// /// Creates a instance by wrapping the /// provided byte array. diff --git a/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs b/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs index aa2d18c8a3ca93..d3f1fe711bb9b1 100644 --- a/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs +++ b/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -614,6 +614,93 @@ public void ToStringReturnEmptyStringWhenBinaryDataEmpty() Assert.Equal(string.Empty, BinaryData.Empty.ToString()); } + [Theory] + [InlineData(0)] + [InlineData(4)] + [InlineData(7)] + public void LengthReturnsNumberOfBytesForBinaryDataFromReadOnlyMemory(int count) + { + var data = BinaryData.FromBytes(new ReadOnlyMemory(new byte[count])); + Assert.Equal(count, data.Length); + } + + [Theory] + [InlineData(0)] + [InlineData(4)] + [InlineData(7)] + public void LengthReturnsNumberOfBytesForBinaryDataFromArray(int count) + { + var data = BinaryData.FromBytes(new byte[count]); + Assert.Equal(count, data.Length); + } + + [Theory] + [InlineData(0)] + [InlineData(4)] + [InlineData(7)] + public void LengthReturnsNumberOfBytesForBinaryDataFromString(int count) + { + var data = BinaryData.FromString(new string('*', count)); + Assert.Equal(count, data.Length); + } + + [Fact] + public void BinaryDataEmptyIsEmpty() + { + Assert.True(BinaryData.Empty.IsEmpty); + } + + [Fact] + public void BinaryDataFromEmptyReadOnlyMemoryIsEmpty() + { + var data = BinaryData.FromBytes(ReadOnlyMemory.Empty); + Assert.True(data.IsEmpty); + } + + [Fact] + public void BinaryDataFromEmptyArrayIsEmpty() + { + var data = BinaryData.FromBytes(Array.Empty()); + Assert.True(data.IsEmpty); + } + + [Fact] + public void BinaryDataFromEmptyStringIsEmpty() + { + var data = BinaryData.FromString(""); + Assert.True(data.IsEmpty); + } + + [Theory] + [InlineData(1)] + [InlineData(4)] + [InlineData(7)] + public void NonEmptyBinaryDataFromReadOnlyMemoryIsNotEmpty(int count) + { + var data = BinaryData.FromBytes(new ReadOnlyMemory(new byte[count])); + Assert.False(data.IsEmpty); + } + + [Theory] + [InlineData(1)] + [InlineData(4)] + [InlineData(7)] + public void NonEmptyBinaryDataFromArrayIsNotEmpty(int count) + { + var data = BinaryData.FromBytes(new byte[count]); + Assert.False(data.IsEmpty); + } + + [Theory] + [InlineData(1)] + [InlineData(4)] + [InlineData(7)] + public void NonEmptyBinaryDataFromStringIsNotEmpty(int count) + { + var data = BinaryData.FromString(new string('*', count)); + Assert.False(data.IsEmpty); + } + [Fact] public void IsBinaryDataMemberPropertySerialized() { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs index 2752225c032653..6c8a00bd00eebc 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs @@ -11,22 +11,26 @@ using System.Runtime.CompilerServices; using QueueType = System.Threading.Channels.Channel; -namespace System.Runtime.InteropServices.JavaScript { +namespace System.Runtime.InteropServices.JavaScript +{ /// /// Provides a thread-safe default SynchronizationContext for the browser that will automatically /// route callbacks to the main browser thread where they can interact with the DOM and other /// thread-affinity-having APIs like WebSockets, fetch, WebGL, etc. /// Callbacks are processed during event loop turns via the runtime's background job system. /// - internal sealed unsafe class JSSynchronizationContext : SynchronizationContext { + internal sealed class JSSynchronizationContext : SynchronizationContext + { public readonly Thread MainThread; - internal readonly struct WorkItem { + internal readonly struct WorkItem + { public readonly SendOrPostCallback Callback; public readonly object? Data; public readonly ManualResetEventSlim? Signal; - public WorkItem (SendOrPostCallback callback, object? data, ManualResetEventSlim? signal) { + public WorkItem(SendOrPostCallback callback, object? data, ManualResetEventSlim? signal) + { Callback = callback; Data = data; Signal = signal; @@ -35,11 +39,11 @@ public WorkItem (SendOrPostCallback callback, object? data, ManualResetEventSlim private static JSSynchronizationContext? MainThreadSynchronizationContext; private readonly QueueType Queue; - private readonly Action _DataIsAvailable; + private readonly Action _DataIsAvailable;// don't allocate Action on each call to UnsafeOnCompleted - private JSSynchronizationContext (Thread mainThread) - : this ( - mainThread, + private JSSynchronizationContext() + : this( + Thread.CurrentThread, Channel.CreateUnbounded( new UnboundedChannelOptions { SingleWriter = false, SingleReader = true, AllowSynchronousContinuations = true } ) @@ -47,19 +51,23 @@ private JSSynchronizationContext (Thread mainThread) { } - private JSSynchronizationContext (Thread mainThread, QueueType queue) { + private JSSynchronizationContext(Thread mainThread, QueueType queue) + { MainThread = mainThread; Queue = queue; _DataIsAvailable = DataIsAvailable; } - public override SynchronizationContext CreateCopy () { + public override SynchronizationContext CreateCopy() + { return new JSSynchronizationContext(MainThread, Queue); } - private void AwaitNewData () { + private void AwaitNewData() + { var vt = Queue.Reader.WaitToReadAsync(); - if (vt.IsCompleted) { + if (vt.IsCompleted) + { DataIsAvailable(); return; } @@ -72,28 +80,33 @@ private void AwaitNewData () { awaiter.UnsafeOnCompleted(_DataIsAvailable); } - private void DataIsAvailable () { + private unsafe void DataIsAvailable() + { // While we COULD pump here, we don't want to. We want the pump to happen on the next event loop turn. // Otherwise we could get a chain where a pump generates a new work item and that makes us pump again, forever. - ScheduleBackgroundJob((void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); + MainThreadScheduleBackgroundJob((void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); } - public override void Post (SendOrPostCallback d, object? state) { + public override void Post(SendOrPostCallback d, object? state) + { var workItem = new WorkItem(d, state, null); if (!Queue.Writer.TryWrite(workItem)) throw new Exception("Internal error"); } // This path can only run when threading is enabled - #pragma warning disable CA1416 +#pragma warning disable CA1416 - public override void Send (SendOrPostCallback d, object? state) { - if (Thread.CurrentThread == MainThread) { + public override void Send(SendOrPostCallback d, object? state) + { + if (Thread.CurrentThread == MainThread) + { d(state); return; } - using (var signal = new ManualResetEventSlim(false)) { + using (var signal = new ManualResetEventSlim(false)) + { var workItem = new WorkItem(d, state, signal); if (!Queue.Writer.TryWrite(workItem)) throw new Exception("Internal error"); @@ -102,40 +115,50 @@ public override void Send (SendOrPostCallback d, object? state) { } } - internal static void Install () { - MainThreadSynchronizationContext ??= new JSSynchronizationContext(Thread.CurrentThread); - + internal static void Install() + { + MainThreadSynchronizationContext ??= new JSSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(MainThreadSynchronizationContext); MainThreadSynchronizationContext.AwaitNewData(); } [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern unsafe void ScheduleBackgroundJob(void* callback); + internal static extern unsafe void MainThreadScheduleBackgroundJob(void* callback); #pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] - private static unsafe void BackgroundJobHandler () { +#pragma warning restore CS3016 + // this callback will arrive on the bound thread, called from mono_background_exec + private static void BackgroundJobHandler() + { MainThreadSynchronizationContext!.Pump(); } - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] - private static unsafe void RequestPumpCallback () { - ScheduleBackgroundJob((void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); - } - - private void Pump () { - try { - while (Queue.Reader.TryRead(out var item)) { - try { + private void Pump() + { + try + { + while (Queue.Reader.TryRead(out var item)) + { + try + { item.Callback(item.Data); // While we would ideally have a catch block here and do something to dispatch/forward unhandled // exceptions, the standard threadpool (and thus standard synchronizationcontext) have zero // error handling, so for consistency with them we do nothing. Don't throw in SyncContext callbacks. - } finally { + } + finally + { item.Signal?.Set(); } } - } finally { + } + catch (Exception e) + { + Environment.FailFast("JSSynchronizationContext.BackgroundJobHandler failed", e); + } + finally + { // If an item throws, we want to ensure that the next pump gets scheduled appropriately regardless. AwaitNewData(); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs index 94a8fb6285aa13..54cfe877ec82e4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs @@ -16,15 +16,15 @@ internal sealed record ComInterfaceContext(ComInterfaceInfo Info, ComInterfaceCo /// /// Takes a list of ComInterfaceInfo, and creates a list of ComInterfaceContext. /// - public static ImmutableArray<(ComInterfaceContext? Context, Diagnostic? Diagnostic)> GetContexts(ImmutableArray data, CancellationToken _) + public static ImmutableArray> GetContexts(ImmutableArray data, CancellationToken _) { Dictionary nameToInterfaceInfoMap = new(); - var accumulator = ImmutableArray.CreateBuilder<(ComInterfaceContext? Context, Diagnostic? Diagnostic)>(data.Length); + var accumulator = ImmutableArray.CreateBuilder>(data.Length); foreach (var iface in data) { nameToInterfaceInfoMap.Add(iface.ThisInterfaceKey, iface); } - Dictionary nameToContextCache = new(); + Dictionary> nameToContextCache = new(); foreach (var iface in data) { @@ -32,7 +32,7 @@ internal sealed record ComInterfaceContext(ComInterfaceInfo Info, ComInterfaceCo } return accumulator.MoveToImmutable(); - (ComInterfaceContext? Context, Diagnostic? Diagnostic) AddContext(ComInterfaceInfo iface) + DiagnosticOr AddContext(ComInterfaceInfo iface) { if (nameToContextCache.TryGetValue(iface.ThisInterfaceKey, out var cachedValue)) { @@ -41,33 +41,33 @@ internal sealed record ComInterfaceContext(ComInterfaceInfo Info, ComInterfaceCo if (iface.BaseInterfaceKey is null) { - var baselessCtx = new ComInterfaceContext(iface, null); - nameToContextCache[iface.ThisInterfaceKey] = (baselessCtx, null); - return (baselessCtx, null); + var baselessCtx = DiagnosticOr.From(new ComInterfaceContext(iface, null)); + nameToContextCache[iface.ThisInterfaceKey] = baselessCtx; + return baselessCtx; } + DiagnosticOr baseReturnedValue; if ( - // Cached base info has a diagnostic - failure - (nameToContextCache.TryGetValue(iface.BaseInterfaceKey, out var basePair) && basePair.Diagnostic is not null) + // Cached base info is a diagnostic - failure + (nameToContextCache.TryGetValue(iface.BaseInterfaceKey, out var baseCachedValue) && baseCachedValue.IsDiagnostic) // Cannot find base ComInterfaceInfo - failure (failed ComInterfaceInfo creation) || !nameToInterfaceInfoMap.TryGetValue(iface.BaseInterfaceKey, out var baseInfo) - // Newly calculated base context pair has a diagnostic - failure - || (AddContext(baseInfo) is { } baseReturnPair && baseReturnPair.Diagnostic is not null)) + // Newly calculated base context pair is a diagnostic - failure + || (baseReturnedValue = AddContext(baseInfo)).IsDiagnostic) { // The base has failed generation at some point, so this interface cannot be generated - (ComInterfaceContext, Diagnostic?) diagnosticPair = (null, + var diagnostic = DiagnosticOr.From( Diagnostic.Create( GeneratorDiagnostics.BaseInterfaceIsNotGenerated, iface.DiagnosticLocation.AsLocation(), iface.ThisInterfaceKey, iface.BaseInterfaceKey)); - nameToContextCache[iface.ThisInterfaceKey] = diagnosticPair; - return diagnosticPair; + nameToContextCache[iface.ThisInterfaceKey] = diagnostic; + return diagnostic; } - var baseContext = basePair.Context ?? baseReturnPair.Context; - Debug.Assert(baseContext != null); - var ctx = new ComInterfaceContext(iface, baseContext); - (ComInterfaceContext, Diagnostic?) contextPair = (ctx, null); - nameToContextCache[iface.ThisInterfaceKey] = contextPair; - return contextPair; + DiagnosticOr baseContext = baseCachedValue ?? baseReturnedValue; + Debug.Assert(baseContext.IsValue); + var ctx = DiagnosticOr.From(new ComInterfaceContext(iface, baseContext.Value)); + nameToContextCache[iface.ThisInterfaceKey] = ctx; + return ctx; } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index 5497794cf62fed..cdfdbfab472072 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -44,43 +44,27 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Where( static modelData => modelData is not null); - var interfaceSymbolAndDiagnostics = attributedInterfaces.Select(static (data, ct) => + var interfaceSymbolOrDiagnostics = attributedInterfaces.Select(static (data, ct) => { - var (info, diagnostic) = ComInterfaceInfo.From(data.Symbol, data.Syntax); - return (InterfaceInfo: info, Diagnostic: diagnostic, Symbol: data.Symbol); + return ComInterfaceInfo.From(data.Symbol, data.Syntax, ct); }); - context.RegisterDiagnostics(interfaceSymbolAndDiagnostics.Select((data, ct) => data.Diagnostic)); + var interfaceSymbolsWithoutDiagnostics = context.FilterAndReportDiagnostics(interfaceSymbolOrDiagnostics); - var interfaceSymbolsWithoutDiagnostics = interfaceSymbolAndDiagnostics - .Where(data => data.Diagnostic is null) - .Select((data, ct) => (data.InterfaceInfo, data.Symbol)); - - var interfaceContextsAndDiagnostics = interfaceSymbolsWithoutDiagnostics + var interfaceContextsOrDiagnostics = interfaceSymbolsWithoutDiagnostics .Select((data, ct) => data.InterfaceInfo!) .Collect() .SelectMany(ComInterfaceContext.GetContexts); - context.RegisterDiagnostics(interfaceContextsAndDiagnostics.Select((data, ct) => data.Diagnostic)); - var interfaceContexts = interfaceContextsAndDiagnostics - .Where(data => data.Context is not null) - .Select((data, ct) => data.Context!); + // Filter down interface symbols to remove those with diagnostics from GetContexts - interfaceSymbolsWithoutDiagnostics = interfaceSymbolsWithoutDiagnostics - .Zip(interfaceContextsAndDiagnostics) - .Where(data => data.Right.Diagnostic is null) - .Select((data, ct) => data.Left); - - var comMethodsAndSymbolsAndDiagnostics = interfaceSymbolsWithoutDiagnostics.Select(ComMethodInfo.GetMethodsFromInterface); - context.RegisterDiagnostics(comMethodsAndSymbolsAndDiagnostics.SelectMany(static (methodList, ct) => methodList.Select(m => m.Diagnostic))); - var methodInfoAndSymbolGroupedByInterface = comMethodsAndSymbolsAndDiagnostics - .Select(static (methods, ct) => - methods - .Where(pair => pair.Diagnostic is null) - .Select(pair => (pair.Symbol, pair.ComMethod)) - .ToSequenceEqualImmutableArray()); + (var interfaceContexts, interfaceSymbolsWithoutDiagnostics) = context.FilterAndReportDiagnostics(interfaceContextsOrDiagnostics, interfaceSymbolsWithoutDiagnostics); + + var comMethodsAndSymbolsOrDiagnostics = interfaceSymbolsWithoutDiagnostics.Select(ComMethodInfo.GetMethodsFromInterface); + var methodInfoAndSymbolGroupedByInterface = context + .FilterAndReportDiagnostics<(ComMethodInfo MethodInfo, IMethodSymbol Symbol)> (comMethodsAndSymbolsOrDiagnostics); var methodInfosGroupedByInterface = methodInfoAndSymbolGroupedByInterface .Select(static (methods, ct) => - methods.Select(pair => pair.ComMethod).ToSequenceEqualImmutableArray()); + methods.Select(pair => pair.MethodInfo).ToSequenceEqualImmutableArray()); // Create list of methods (inherited and declared) and their owning interface var comMethodContextBuilders = interfaceContexts .Zip(methodInfosGroupedByInterface) @@ -98,7 +82,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var methodInfoToSymbolMap = methodInfoAndSymbolGroupedByInterface .SelectMany((data, ct) => data) .Collect() - .Select((data, ct) => data.ToDictionary(static x => x.ComMethod, static x => x.Symbol)); + .Select((data, ct) => data.ToDictionary(static x => x.MethodInfo, static x => x.Symbol)); var comMethodContexts = comMethodContextBuilders .Combine(methodInfoToSymbolMap) .Combine(context.CreateStubEnvironmentProvider()) diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs index 533eb8c54bb8eb..31db875d638d48 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -24,7 +25,7 @@ internal sealed record ComInterfaceInfo( Guid InterfaceId, LocationInfo DiagnosticLocation) { - public static (ComInterfaceInfo? Info, Diagnostic? Diagnostic) From(INamedTypeSymbol symbol, InterfaceDeclarationSyntax syntax) + public static DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)> From(INamedTypeSymbol symbol, InterfaceDeclarationSyntax syntax, CancellationToken _) { // Verify the method has no generic types or defined implementation // and is not marked static or sealed @@ -34,10 +35,11 @@ public static (ComInterfaceInfo? Info, Diagnostic? Diagnostic) From(INamedTypeSy // and is not marked static or sealed if (syntax.TypeParameterList is not null) { - return (null, Diagnostic.Create( - GeneratorDiagnostics.InvalidAttributedInterfaceGenericNotSupported, - syntax.Identifier.GetLocation(), - symbol.Name)); + return DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)>.From( + Diagnostic.Create( + GeneratorDiagnostics.InvalidAttributedInterfaceGenericNotSupported, + syntax.Identifier.GetLocation(), + symbol.Name)); } } @@ -46,25 +48,26 @@ public static (ComInterfaceInfo? Info, Diagnostic? Diagnostic) From(INamedTypeSy { if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword)) { - return (null, Diagnostic.Create( - GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, - syntax.Identifier.GetLocation(), - symbol.Name, - typeDecl.Identifier)); + return DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)>.From( + Diagnostic.Create( + GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, + syntax.Identifier.GetLocation(), + symbol.Name, + typeDecl.Identifier)); } } if (!TryGetGuid(symbol, syntax, out Guid? guid, out Diagnostic? guidDiagnostic)) - return (null, guidDiagnostic); + return DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)>.From(guidDiagnostic); if (!TryGetBaseComInterface(symbol, syntax, out INamedTypeSymbol? baseSymbol, out Diagnostic? baseDiagnostic)) - return (null, baseDiagnostic); + return DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)>.From(baseDiagnostic); if (!StringMarshallingIsValid(symbol, syntax, baseSymbol, out Diagnostic? stringMarshallingDiagnostic)) - return (null, stringMarshallingDiagnostic); + return DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)>.From(stringMarshallingDiagnostic); - return ( - new ComInterfaceInfo( + return DiagnosticOr<(ComInterfaceInfo InterfaceInfo, INamedTypeSymbol Symbol)>.From( + (new ComInterfaceInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol), symbol.ToDisplayString(), baseSymbol?.ToDisplayString(), @@ -73,7 +76,7 @@ public static (ComInterfaceInfo? Info, Diagnostic? Diagnostic) From(INamedTypeSy new ContainingSyntax(syntax.Modifiers, syntax.Kind(), syntax.Identifier, syntax.TypeParameterList), guid ?? Guid.Empty, LocationInfo.From(symbol)), - null); + symbol)); } private static bool StringMarshallingIsValid(INamedTypeSymbol symbol, InterfaceDeclarationSyntax syntax, INamedTypeSymbol? baseSymbol, [NotNullWhen(false)] out Diagnostic? stringMarshallingDiagnostic) diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs index a2595736973333..4d220d29538549 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs @@ -21,9 +21,9 @@ internal sealed record ComMethodInfo( /// /// Returns a list of tuples of ComMethodInfo, IMethodSymbol, and Diagnostic. If ComMethodInfo is null, Diagnostic will not be null, and vice versa. /// - public static SequenceEqualImmutableArray<(ComMethodInfo? ComMethod, IMethodSymbol Symbol, Diagnostic? Diagnostic)> GetMethodsFromInterface((ComInterfaceInfo ifaceContext, INamedTypeSymbol ifaceSymbol) data, CancellationToken ct) + public static SequenceEqualImmutableArray> GetMethodsFromInterface((ComInterfaceInfo ifaceContext, INamedTypeSymbol ifaceSymbol) data, CancellationToken ct) { - var methods = ImmutableArray.CreateBuilder<(ComMethodInfo, IMethodSymbol, Diagnostic?)>(); + var methods = ImmutableArray.CreateBuilder>(); foreach (var member in data.ifaceSymbol.GetMembers()) { if (IsComMethodCandidate(member)) @@ -59,7 +59,7 @@ private static bool IsComMethodCandidate(ISymbol member) return member.Kind == SymbolKind.Method && !member.IsStatic; } - private static (ComMethodInfo?, IMethodSymbol, Diagnostic?) CalculateMethodInfo(ComInterfaceInfo ifaceContext, IMethodSymbol method, CancellationToken ct) + private static DiagnosticOr<(ComMethodInfo, IMethodSymbol)> CalculateMethodInfo(ComInterfaceInfo ifaceContext, IMethodSymbol method, CancellationToken ct) { ct.ThrowIfCancellationRequested(); Debug.Assert(IsComMethodCandidate(method)); @@ -82,7 +82,7 @@ private static (ComMethodInfo?, IMethodSymbol, Diagnostic?) CalculateMethodInfo( if (methodLocationInAttributedInterfaceDeclaration is null) { - return (null, method, Diagnostic.Create(GeneratorDiagnostics.MethodNotDeclaredInAttributedInterface, method.Locations.FirstOrDefault(), method.ToDisplayString())); + return DiagnosticOr<(ComMethodInfo, IMethodSymbol)>.From(Diagnostic.Create(GeneratorDiagnostics.MethodNotDeclaredInAttributedInterface, method.Locations.FirstOrDefault(), method.ToDisplayString())); } @@ -100,16 +100,16 @@ private static (ComMethodInfo?, IMethodSymbol, Diagnostic?) CalculateMethodInfo( } if (comMethodDeclaringSyntax is null) { - return (null, method, Diagnostic.Create(GeneratorDiagnostics.CannotAnalyzeMethodPattern, method.Locations.FirstOrDefault(), method.ToDisplayString())); + return DiagnosticOr<(ComMethodInfo, IMethodSymbol)>.From(Diagnostic.Create(GeneratorDiagnostics.CannotAnalyzeMethodPattern, method.Locations.FirstOrDefault(), method.ToDisplayString())); } var diag = GetDiagnosticIfInvalidMethodForGeneration(comMethodDeclaringSyntax, method); if (diag is not null) { - return (null, method, diag); + return DiagnosticOr<(ComMethodInfo, IMethodSymbol)>.From(diag); } var comMethodInfo = new ComMethodInfo(comMethodDeclaringSyntax, method.Name); - return (comMethodInfo, method, null); + return DiagnosticOr<(ComMethodInfo, IMethodSymbol)>.From((comMethodInfo, method)); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf index 1c0d66c7781aa4..d21ecbbeb15b2a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - Zdrojem generovaná volání P/Invokes budou ignorovat všechny nepodporované konfigurace. + Model COM vygenerovaný zdrojem bude ignorovat všechny nepodporované konfigurace. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Konfiguraci {0} nepodporují zdrojem generovaná volání P/Invokes. Pokud je určená konfigurace povinná, použijte místo ní normální DllImport. + Konfigurace „{0}“ není podporována modelem COM generovaným zdrojem. Pokud je zadaná konfigurace povinná, použijte místo toho „ComImport“. The specified marshalling configuration is not supported by source-generated COM. {0}. - Určenou konfiguraci zařazování nepodporují zdrojem generovaná volání P/Invokes. {0}. + Zadaná konfigurace zařazování není podporována modelem COM generovaným zdrojem. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Určená konfigurace {0} pro parametr {1} není podporována voláním P/Invokes. Pokud je určená konfigurace povinná, použijte místo ní normální DllImport. + Zadaná konfigurace „{0}“ pro parametr „{1}“ není podporována modelem COM generovaným zdrojem. Pokud je zadaná konfigurace povinná, použijte místo toho ComImport. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Určenou konfiguraci {0} návratové hodnoty metody {1} nepodporují zdrojem generovaná volání P/Invokes. Pokud je určená konfigurace povinná, použijte místo ní normální Dllimport. + Zadaná konfigurace „{0}“ pro návratovou hodnotu metody „{1}“ není podporována modelem COM generovaným zdrojem. Pokud je zadaná konfigurace povinná, použijte místo toho ComImport. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Určená hodnota {0} pro {1} se nepodporuje u zdrojem generovaných volání P/Invokes. Pokud je určená konfigurace povinná, použijte místo ní normální Dllimport. + Zadaná hodnota „{0}“ pro „{1}“ není podporována modelem COM generovaným zdrojem. Pokud je zadaná konfigurace povinná, použijte místo toho ComImport. Specified configuration is not supported by source-generated COM. - Určenou konfiguraci nepodporují zdrojem generovaná volání P/Invokes. + Zadaná konfigurace není podporována modelem COM generovaným zdrojem. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - Metoda {0} je obsažena v typu {1}, který není označen jako „partial“. Generování zdrojů volání P/Invoke bude metodu {0} ignorovat. + Metoda „{0}“ je obsažena v typu „{1}“, který není označen jako „částečně provedený kód“. Generování zdroje modelu COM bude metodu „{0}“ ignorovat. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Metody označené atributem LibraryImportAttribute by měly být typu „static“, „partial“ a non-generic. Generování zdrojů volání P/Invoke bude ignorovat metody typu non-„static“, non-„partial“ a generic. + Metody na rozhraních označených atributem „GeneratedComInterfaceAttribute“ by neměly být obecné povahy. Generování zdroje modelu COM bude obecné metody ignorovat. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - Metoda {0} by měla mít vlastnost „static“, „partial“ a non-generic, když je označena atributem LibraryImportAttribute. Generování zdroje voláním P/Invoke bude metodu {0} ignorovat. + Metoda „{0}“ by neměla být v rozhraních označených atributem GeneratedComInterfaceAttribute obecné povahy. Generování zdroje modelu COM bude metodu „{0}“ ignorovat. @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Rozhraní s atributem GeneratedComInterfaceAttribute musí být částečná, neobecná a musí určovat identifikátor GUID s atributem System.Runtime.InteropServices.GuidAttribute. Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + Rozhraní „{0}“ má atribut GeneratedComInterfaceAttribute, ale je obecné. @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + Konfigurace StringMarshalling a StringMarshallingCustomType u metody {0} je neplatná. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - U typů, které nejsou podporovány zdrojem generovanými voláními P/Invoke, bude výsledné volání P/Invoke záviset na podkladovém modulu runtime, aby určený typ zařadil. + U typů, které nejsou podporovány modelem COM generovaným zdrojem, bude výsledný ukazatel funkce při zařazování zadaného typu spoléhat na základní modul runtime. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - Typ {0} nepodporují zdrojem generovaná volání P/Invokes. Vygenerovaný zdroj nebude zpracovávat zařazování parametru {1}. + Model COM vygenerovaný zdrojem nepodporuje typ „{0}“. Vygenerovaný zdroj nezpracuje zařazování parametru „{1}“. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - Typ {0} nepodporují zdrojem generovaná volání P/Invokes. Vygenerovaný zdroj nebude zpracovávat zařazování návratové hodnoty metody {1}. + Model COM vygenerovaný zdrojem nepodporuje typ „{0}“. Vygenerovaný zdroj nebude zpracovávat zařazování návratové hodnoty metody „{1}“. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Určený typ nepodporují zdrojem generovaná volání P/Invokes. + Zadaný typ není podporován modelem COM generovaným zdrojem. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf index 1f498be82346ae..0de3d202c7ff6e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - Quellgenerierte P/Invokes ignorieren alle Konfigurationen, die nicht unterstützt werden. + Quellgeneriertes COM ignoriert alle Konfigurationen, die nicht unterstützt werden. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Die Konfiguration \"{0}\" wird von quellgenerierten P/Invokes nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären DllImport. + Die Konfiguration "{0}" wird vom quellgenerierten COM nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären „ComImport“. The specified marshalling configuration is not supported by source-generated COM. {0}. - Die angegebene Marshallingkonfiguration wird von quellgenerierten P/Invokes nicht unterstützt. {0}. + Die angegebene Marshallingkonfiguration wird vom quellgenerierten COM nicht unterstützt. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Die angegebene Konfiguration \"{0}\" für den Parameter \"{1}\" wird von quellgenerierten P/Invokes nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären \"DllImport\". + Die angegebene Konfiguration "{0}" für den Parameter "{1}" wird vom quellgenerierten COM nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären „ComImport“. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Die angegebene Konfiguration \"{0}\" für den Rückgabewert der Methode \"{1}\" wird von quellgenerierten P/Invokes nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären DllImport. + Die angegebene Konfiguration "{0}" für den Rückgabewert der Methode "{1}" wird vom quellgenerierten COM nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären „ComImport“. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Der angegebene Wert \"{0}\" für \"{1}\" wird von quellgenerierten P/Invokes nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären \"DllImport\". + Der angegebene Wert "{0}" für "{1}" wird vom quellgenerierten COM nicht unterstützt. Wenn die angegebene Konfiguration erforderlich ist, verwenden Sie stattdessen einen regulären „ComImport“. Specified configuration is not supported by source-generated COM. - Die angegebene Konfiguration wird von quellgenerierten P/Invokes nicht unterstützt. + Die angegebene Konfiguration wird vom quellgenerierten COM nicht unterstützt. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - Die Methode \"{0}\" ist in einem Typ \"{1}\" enthalten, der nicht als \"partiell\" gekennzeichnet ist. Die P/Invoke-Quellgenerierung ignoriert die Methode \"{0}\". + Die Methode "{0}" ist in einem Typ "{1}" enthalten, der nicht als "partiell" gekennzeichnet ist. Die COM-Quellgenerierung ignoriert die Methode "{0}". @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Methoden, die mit \"LibraryImportAttribute\" gekennzeichnet sind, sollten \"statisch\", \"partiell\" und nicht generisch sein. Die P/Invoke-Quellgenerierung ignoriert Methoden, die nicht \"statisch\", nicht \"partiell\" oder generisch sind. + Methoden für Schnittstellen, die mit "GeneratedComInterfaceAttribute" gekennzeichnet sind, sollten nicht generisch sein. Die COM-Quellgenerierung ignoriert generische Methoden. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - Die Methode \"{0}\" muss \"statisch\", \"partiell\" und nicht generisch sein, wenn sie mit \"LibraryImportAttribute\" markiert ist. Die P/Invoke-Quellgenerierung ignoriert die Methode \"{0}\". + Die Methode "{0}" sollte bei Schnittstellen, die mit "GeneratedComInterfaceAttribute" gekennzeichnet sind, nicht generisch sein. Die COM-Quellgenerierung ignoriert die Methode "{0}". @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + Die Konfiguration von \"StringMarshalling\" und \"StringMarshallingCustomType\" für die Methode \"{0}\" ist ungültig. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Bei Typen, die von dquellgenerierten P/Invokes nicht unterstützt werden, basiert der resultierende P/Invoke auf der zugrunde liegenden Laufzeit, um den angegebenen Typ zu marshallen. + Bei Typen, die vom quellgenerierten COM nicht unterstützt werden, basiert der resultierende Funktionszeiger auf der zugrunde liegenden Laufzeit, um den angegebenen Typ zu marshallen. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - Der Typ \"{0}\" wird von vom Quellcode generierten P/Invokes nicht unterstützt. Die generierte Quelle verarbeitet das Marshalling des Parameters \"{1}\" nicht. + Der Typ "{0}" wird vom quellgenerierten COM nicht unterstützt. Die generierte Quelle verarbeitet das Marshalling des Parameters "{1}" nicht. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - Der Typ \"{0}\" wird von vom Quellcode generierten P/Invokes nicht unterstützt. Die generierte Quelle verarbeitet das Marshalling des Rückgabewerts der Methode \"{1}\" nicht. + Der Typ "{0}" wird vom quellgenerierten COM nicht unterstützt. Die generierte Quelle verarbeitet das Marshalling des Rückgabewerts der Methode "{1}" nicht. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Der angegebene Typ wird von quellgenerierten P/Invokes nicht unterstützt. + Der angegebene Typ wird vom quellgenerierten COM nicht unterstützt. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf index 47607c08be7c58..93b0eb93da5043 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - Los P/Invoke de un generador de código fuente omitirán cualquier configuración que no esté admitida. + COM generado por el origen omitirá cualquier configuración que no se admita. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configuración de “{0}” no está admitida por P/Invokes de un generador de código fuente. Si se requiere la configuración, use un “DllImport” normal en su lugar. + La configuración "{0}" no es compatible con COM generado por origen. Si se requiere la configuración especificada, usa "ComImport" en su lugar. The specified marshalling configuration is not supported by source-generated COM. {0}. - La configuración de serialización especificada no está admitida por P/Invokes de un generador de código fuente. {0}. + COM generado por origen no admite la configuración de serialización especificada. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configuración de “{0}” especificada para el parámetro “{1}” no es compatible con P/Invokes de un generador de código fuente. Si se requiere la configuración especificada, use un “DllImport” normal en su lugar. + La configuración "{0}" especificada para el parámetro "{1}" no es compatible con COM generado por el origen. Si se requiere la configuración especificada, use "ComImport" en su lugar. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configuración de “{0}” especificada para el valor devuelto del método “{1}” no es compatible con P/Invokes generados por origen. Si se requiere la configuración especificada, use un “DllImport” normal en su lugar. + La configuración "{0}" especificada para el valor devuelto del método "{1}" no es compatible con COM generado por origen. Si se requiere la configuración especificada, usa "ComImport" en su lugar. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - El valor especificado de “{0}” para “{1}” no es compatible con P/Invokes de un generador de código fuente. Si se requiere la configuración especificada, use un “DllImport” normal en su lugar. + El valor especificado "{0}" para "{1}" no es compatible con COM generado por el origen. Si se requiere la configuración especificada, usa "ComImport" en su lugar. Specified configuration is not supported by source-generated COM. - La configuración especificada no está admitida por P/Invokes de un generador de código fuente. + COM generado por origen no admite la configuración especificada. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - El método “{0}” está contenido en un tipo “{1}” que no está marcado como “partial”. La generación de código fuente P/Invoke omitirá el método “{0}”. + El método "{0}" está contenido en un tipo "{1}" que no está marcado como "parcial". La generación de código fuente COM omitirá el método "{0}". @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Los métodos marcados con “LibraryImportAttribute” deben ser “static”, “partial” y no genéricos. La generación de código fuente P/Invoke omitirá los métodos que no sean “static”, “partial” ni genéricos. + Los métodos de las interfaces marcadas con "GeneratedComInterfaceAttribute" no deben ser genéricos. La generación de orígenes COM omitirá los métodos que son genéricos. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - El método “{0}” debe ser “static”, “partial”, y no genérico cuando está marcado con “LibraryImportAttribute”. La generación del código fuente P/Invoke omitirá el método “{0}”. + El método "{0}" debe ser no genérico cuando se encuentra en interfaces marcadas con 'GeneratedComInterfaceAttribute'. La generación de código fuente COM omitirá el método "{0}". @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Las interfaces con el atributo “GeneratedComInterfaceAttribute” deben ser parciales, no genéricas y deben especificar un GUID con “System.Runtime.InteropServices.GuidAttribute”. Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + La interfaz “{0}” tiene el atributo ”GeneratedComInterfaceAttribute”, pero es genérica. @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + La configuración de “StringMarshalling” y “StringMarshallingCustomType” en el método “{0}” no es válida. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Para los tipos que no son compatibles con P/Invokes de un generador de código fuente, el P/Invoke resultante se basará en el entorno de ejecución subyacente para serializar las referencias del tipo especificado. + Para los tipos que no son compatibles con COM generado por el origen, el puntero de función resultante se basará en el tiempo de ejecución subyacente para serializar el tipo especificado. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - El tipo “{0}” no es compatible con P/Invokes de un generador de código fuente. El código fuente generado no controlará la serialización del parámetro “{1}”. + El tipo "{0}" no es compatible con COM generado por origen. El origen generado no controlará la serialización del parámetro "{1}". @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - El tipo “{0}” no es compatible con P/Invokes de un generador de código fuente. El código fuente generado no controlará la serialización del valor devuelto del método “{1}”. + El tipo "{0}" no es compatible con COM generado por origen. El origen generado no controlará la serialización del valor devuelto del método "{1}". @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - El tipo especificado no está admitido por P/Invokes de un generador de código fuente + El tipo especificado no es compatible con COM generado por el origen diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf index 6d23021f515854..c1b397464a7b4f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - Les P/Invokes générés par la source ignorent toute configuration qui n’est pas prise en charge. + COM généré par la source ignore toute configuration qui n’est pas prise en charge. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configuration de « {0} » n’est pas prise en charge par les P/Invok générés par la source. Si la configuration spécifiée est requise, utilisez plutôt un « DllImport » normal. + La configuration «{0}» n’est pas prise en charge par com généré par la source. Si la configuration spécifiée est requise, utilisez « ComImport » à la place. The specified marshalling configuration is not supported by source-generated COM. {0}. - La configuration de marshaling spécifiée n’est pas prise en charge par les P/Invokes générés par la source. {0}. + La configuration de marshaling spécifiée n’est pas prise en charge par COM généré par la source. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configuration de « {0} » spécifiée pour le paramètre « {1} » n’est pas prise en charge par les P/Invok générés par la source. Si la configuration spécifiée est requise, utilisez plutôt un « DllImport » normal. + La configuration «{0}» spécifiée pour le paramètre «{1}» n’est pas prise en charge par com généré par la source. Si la configuration spécifiée est requise, utilisez « ComImport » à la place. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configuration de « {0} » spécifiée pour la valeur renvoyée de la méthode « {1} » n’est pas prise en charge par les P/Invok générés par la source. Si la configuration spécifiée est requise, utilisez plutôt un « DllImport » normal. + La configuration «{0}» spécifiée pour la valeur de retour de la méthode «{1}» n’est pas prise en charge par com généré par la source. Si la configuration spécifiée est requise, utilisez « ComImport » à la place. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La valeur de « {0} » spécifiée pour « {1} » n’est pas prise en charge par les P/Invok générés par la source. Si la configuration spécifiée est requise, utilisez plutôt un « DllImport » normal. + La valeur spécifiée «{0}» pour «{1}» n’est pas prise en charge par com généré par la source. Si la configuration spécifiée est requise, utilisez « ComImport » à la place. Specified configuration is not supported by source-generated COM. - La configuration spécifiée n’est pas prise en charge par les P/Invokes générés par la source. + La configuration spécifiée n’est pas prise en charge par com généré par la source. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - La méthode « {0} » est contenue dans un type « {1} » qui n’est pas marqué comme étant « partial ». La génération source P/Invoke ignore la méthode « {0} ». + La méthode '{0}' est contenue dans un type '{1}' qui n’est pas marqué 'partial'. La génération de source COM ignore la méthode '{0}'. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Les méthodes marquées avec « LibraryImportAttribute » doivent être « static », « partial » et non génériques. La génération de source P/Invoke ignore les méthodes qui ne sont pas statiques, non partielles ou génériques. + Les méthodes sur les interfaces marquées avec « GeneratedComInterfaceAttribute » doivent être non génériques. La génération de source COM ignore les méthodes génériques. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - La méthode « {0} » doit être « static », « partial » et non générique quand elle est marquée avec « LibraryImportAttribute ». La génération source P/Invoke ignore la méthode « {0} ». + La méthode '{0}' doit être non générique sur les interfaces marquées avec l’attribut GeneratedComInterfaceAttribute. La génération de source COM ignore la méthode '{0}'. @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Les interfaces sont attribuées à « GeneratedComInterfaceAttribute » doivent être partielles, non génériques et doivent spécifier un GUID avec « System.Runtime.InteropServices.GuidAttribute ». Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + L’interface '{0}' est attribuée à 'GeneratedComInterfaceAttribute', mais est générique. @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + La configuration de « StringMarshalling » et de « StringMarshallingCustomType » n’est sur la méthode « {0} » pas valide. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Pour les types qui ne sont pas pris en charge par les P/Invok générés par la source, le P/Invoke résultant se base sur le runtime sous-jacent pour marshaler le type spécifié. + Pour les types qui ne sont pas pris en charge par com généré par la source, le pointeur de fonction résultant s’appuie sur le runtime sous-jacent pour marshaler le type spécifié. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - Le type « {0} » n’est pas pris en charge par les P/Invokes générés par la source. La source générée ne gère pas le marshaling du paramètre « {1} ». + Le type '{0}' n’est pas pris en charge par COM généré par la source. La source générée ne gère pas le marshaling du paramètre «{1}». @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - Le type « {0} » n’est pas pris en charge par les P/Invokes générés par la source. La source générée ne gère pas le marshaling de la valeur de retour de la méthode « {1} ». + Le type '{0}' n’est pas pris en charge par COM généré par la source. La source générée ne gère pas le marshaling de la valeur de retour de la méthode '{1}'. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Le type spécifié n’est pas prise en charge par les P/Invokes générés par la source. + Le type spécifié n’est pas pris en charge par com généré par la source diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf index 67fb4de171841c..455053eb3a00a3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - I P/Invoke generati dall'origine ignoreranno qualsiasi configurazione non supportata. + I COM generati dall'origine ignoreranno qualsiasi configurazione non supportata. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configurazione '{0}' non è supportata dai P/Invoke generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `DllImport` normale. + La configurazione '{0}' non è supportata dai COM generati dall'origine. Se la configurazione specificata è obbligatoria, usa un attributo `ComImport`. The specified marshalling configuration is not supported by source-generated COM. {0}. - La configurazione di marshalling specificata non è supportata dai P/Invoke generati dall'origine. {0}. + La configurazione di marshalling specificata non è supportata dai COM generati dall'origine. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configurazione '{0}' specificata per il parametro '{1}' non è supportata dai P/Invoke generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `DllImport` normale. + La configurazione '{0}' specificata per il parametro '{1}' non è supportata dai COM generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `ComImport`. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - La configurazione '{0}' specificata per il valore restituito del metodo '{1}' non è supportata dai P/Invoke generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `DllImport` normale. + La configurazione '{0}' specificata per il valore restituito del metodo '{1}' non è supportata dai COM generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `ComImport` normale. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Il valore '{0}' specificato per '{1}' non è supportato dai P/Invoke generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `DllImport` normale. + Il valore '{0}' specificato per '{1}' non è supportato dai COM generati dall'origine. Se la configurazione specificata è obbligatoria, usare un attributo `ComImport`. Specified configuration is not supported by source-generated COM. - La configurazione specificata non è supportata dai P/Invoke generati dall'origine. + La configurazione specificata non è supportata dai COM generati dall'origine. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - Il metodo '{0}' è contenuto in un tipo '{1}' non contrassegnato come 'partial'. Durante la generazione dell'origine P/Invoke il metodo '{0}' verrà ignorato. + Il metodo '{0}' è contenuto in un tipo '{1}' non contrassegnato come 'partial'. La generazione dell'origine COM ignorerà il metodo '{0}'. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - I metodi contrassegnati con 'LibraryImportAttribute' devono essere 'static', 'partial' e non generici. Durante la generazione dell'origine P/Invoke i metodi non 'static', non 'partial' o generici verranno ignorati. + I metodi nelle interfacce contrassegnate con l’attributo 'GeneratedComInterfaceAttribute' devono essere non generici. La generazione di origini COM ignorerà i metodi generici. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - Il metodo '{0}' deve essere 'static', 'partial' e non generico quando è contrassegnato con 'LibraryImportAttribute'. Durante la generazione dell'origine P/Invoke il metodo '{0}' verrà ignorato. + Il metodo '{0}' deve essere non generico nelle interfacce contrassegnate con l'attributo 'GeneratedComInterfaceAttribute'. La generazione di origini COM ignorerà il metodo '{0}'. @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + La configurazione di 'StringMarshalling' e 'StringMarshallingCustomType' nel metodo '{0}' non è valida. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Per i tipi non supportati da P/Invoke generati dall'origine, il P/Invoke risultante si baserà sul runtime sottostante per effettuare il marshalling del tipo specificato. + Per i tipi non supportati dai COM generati dall'origine, il puntatore funzione risultante si baserà sul runtime sottostante per effettuare il marshalling del tipo specificato. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - Il tipo '{0}' non è supportato dai P/Invoke generati dall'origine. L'origine generata non gestirà il marshalling del parametro '{1}'. + Il tipo '{0}' non è supportato dai COM generati dall'origine. L'origine generata non gestirà il marshalling del parametro '{1}'. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - Il tipo '{0}' non è supportato dai P/Invoke generati dall'origine. L'origine generata non gestirà il marshalling del valore restituito del metodo '{1}'. + Il tipo '{0}' non è supportato dai CONM generati dall'origine. L'origine generata non gestirà il marshalling del valore restituito del metodo '{1}'. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Il tipo specificato non è supportato dai P/Invoke generati dall'origine + Il tipo specificato non è supportato da COM generati dall'origine diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf index 49f864b2d70f31..057745c7cb5cdd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - ソース生成済みの P/Invoke は、サポートされていない構成を無視します。 + ソース生成済みの COM は、サポートされていない構成を無視します。 The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{0}' 構成は、ソース生成済みの P/Invoke ではサポートされていません。指定された構成が必要な場合は、代わりに通常の 'DllImport' を使用します。 + '{0}' 構成は、ソース生成済みの COM ではサポートされていません。指定した構成が必要な場合は、代わりに 'ComImport' を使用してください。 The specified marshalling configuration is not supported by source-generated COM. {0}. - 指定されたマーシャリング構成は、ソースで生成された P/Invoke ではサポートされていません。{0}。 + 指定されたマーシャリング構成は、ソース生成済みの COM ではサポートされていません。{0}。 The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - パラメーター '{1}' 向けに指定された '{0}' 構成は、ソース生成済みの P/Invoke ではサポートされていません。指定された構成が必要な場合は、代わりに通常の 'DllImport' を使用します。 + パラメーター '{1}' に指定された '{0}' 構成は、ソース生成済みの COM ではサポートされていません。指定した構成が必要な場合は、代わりに 'ComImport' を使用してください。 The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - メソッド '{1}' の戻り値向けに指定された '{0}' 構成は、ソース生成済みの P/Invoke ではサポートされていません。指定された構成が必要な場合は、代わりに通常の 'DllImport' を使用します。 + メソッド '{0}' の戻り値に指定された '{1}' 構成は、ソース生成済みの COM ではサポートされていません。指定した構成が必要な場合は、代わりに 'ComImport' を使用してください。 The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{1}' 向けに指定された値 '{0}' は、ソース生成済みの P/Invoke ではサポートされていません。指定された構成が必要な場合は、代わりに通常の 'DllImport' を使用します。 + '{1}' に指定された値 '{0}' は、ソース生成済みの COM ではサポートされていません。指定した構成が必要な場合は、代わりに 'ComImport' を使用してください。 Specified configuration is not supported by source-generated COM. - 指定された構成は、ソースで生成された P/Invoke ではサポートされていません。 + 指定された構成は、ソース生成済みの COM ではサポートされていません。 @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - メソッド '{0}' は、'partial' とマークされていない型 '{1}' に含まれています。P/Invoke ソース生成はメソッド '{0}' を無視します。 + メソッド '{0}' は、'partial' とマークされていない型 '{1}' に含まれています。COM ソース生成はメソッド '{0}' を無視します。 @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - 'LibraryImportAttribute' でマークされたメソッドは、'static'、'partial'、非ジェネリックである必要があります。P/Invoke ソース生成では、非 'static'、非 'partial'、ジェネリックであるメソッドは無視されます。 + 'GeneratedComInterfaceAttribute' でマークされたインターフェイス上のメソッドは非ジェネリックである必要があります。COM ソース生成は、ジェネリック メソッドを無視します。 Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - 'LibraryImportAttribute' でマークされている場合、メソッド '{0}'は 'static'、'partial'、非ジェネリックである必要があります。P/Invoke ソース生成では、メソッド '{0}' は無視されます。 + 'GeneratedComInterfaceAttribute' でマークされたインターフェイスでは、メソッド '{0}' は非ジェネリックである必要があります。COM ソース生成はメソッド '{0}' を無視します。 @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + メソッド '{0}' の 'StringMarshalling' と 'StringMarshallingCustomType' の構成が無効です。{1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - ソース生成済みの P/Invoke でサポートされていない型である場合、生成された P/Invoke は、基礎となるなるランタイムに依存して、指定された型をマーシャリングします。 + ソース生成済みの COM でサポートされていない型である場合、生成された関数ポインターは、基礎となるなるランタイムに依存して、指定された型をマーシャリングします。 The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - 型 '{0}' は、ソース生成済みの P/Invoke ではサポートされていません。生成されたソースは、パラメーター '{1}' のマーシャリングを処理しません。 + 型 '{0}' は、ソース生成済みの COM ではサポートされていません。生成されたソースは、パラメーター '{1}' のマーシャリングを処理しません。 @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - 型 '{0}' は、ソース生成済みの P/Invoke ではサポートされていません。生成されたソースは、メソッド '{1}' の戻り値のマーシャリングを処理しません。 + 型 '{0}' は、ソース生成済みの COM ではサポートされていません。生成されたソースは、メソッド '{1}' の戻り値のマーシャリングを処理しません。 @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - 指定された型は、ソースで生成された P/Invoke ではサポートされていません + 指定された型は、ソース生成済みの COM ではサポートされていません diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf index cc35991644b7df..a13812c5cdc31d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf @@ -24,57 +24,57 @@ COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. - COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. + COM 인터페이스 원본을 생성하려면 모든 COM 기본 인터페이스가 유효해야 합니다. 기본 인터페이스의 문제를 수정하여 이 진단을 해결하세요. COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. - COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. + COM 인터페이스 {0}이(가) 오류가 있는 {1}에서 상속됩니다. ComInterfaceGenerator는 {0}에 대한 원본을 생성하지 않습니다. The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. - The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. + 기본 COM 인터페이스에서 원본을 생성하지 못했습니다. ComInterfaceGenerator는 이 인터페이스에 대한 원본을 생성하지 않습니다. Source-generated COM will ignore any configuration that is not supported. - 소스 생성 P/Invoke는 지원되지 않는 구성을 무시합니다. + 소스 생성 COM은 지원되지 않는 구성을 무시합니다. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{0}' 구성은 소스 생성 P/Invoke에서 지원되지 않습니다. 지정된 구성이 필요한 경우 일반 'DllImport'를 대신 사용하세요. + '{0}' 구성은 원본 생성 COM에서 지원되지 않습니다. 지정된 구성이 필요한 경우 대신 'ComImport'를 사용합니다. The specified marshalling configuration is not supported by source-generated COM. {0}. - 지정된 마샬링 구성은 소스 생성 P/Invoke에서 지원되지 않습니다. {0}. + 지정된 마샬링 구성은 원본 생성 COM에서 지원되지 않습니다. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 매개 변수 '{1}'에 대해 지정된 '{0}' 구성이 소스 생성 P/Invoke에서 지원되지 않습니다. 지정된 구성이 필요한 경우 일반 'DllImport'를 대신 사용하세요. + 매개 변수 '{1}'에 대해 지정된 '{0}' 구성이 원본 생성 COM에서 지원되지 않습니다. 지정된 구성이 필요한 경우 'ComImport’를 대신 사용합니다. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 메서드 '{1}'의 반환 값에 대해 지정된 '{0}' 구성은 소스 생성 P/Invoke에서 지원되지 않습니다. 지정된 구성이 필요한 경우 일반 'DllImport'를 대신 사용하세요. + 메서드 '{1}'의 반환 값에 대해 지정된 '{0}' 구성은 소스 생성 COM에서 지원되지 않습니다. 지정된 구성이 필요한 경우 대신 'ComImport'를 사용합니다. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{1}'에 대해 지정된 값 '{0}'은(는) 소스 생성 P/Invoke에서 지원되지 않습니다. 지정된 구성이 필요한 경우 일반 'DllImport'를 대신 사용하세요. + '{1}'에 대해 지정된 값 '{0}'은(는) 원본 생성 COM에서 지원되지 않습니다. 지정된 구성이 필요한 경우 대신 'ComImport'를 사용합니다. Specified configuration is not supported by source-generated COM. - 지정된 구성은 소스 생성 P/Invoke에서 지원되지 않습니다. + 지정된 구성은 원본 생성 COM에서 지원되지 않습니다. The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. + 'StringMarshalling' 및 'StringMarshallingCustomType'의 구성은 기본 COM 인터페이스와 일치해야 합니다. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - 메서드 '{0}'은(는) 'partial'로 표시되지 않은 '{1}' 형식에 포함되어 있습니다. P/Invoke 소스 생성은 '{0}' 메서드를 무시합니다. + 메서드 '{0}'은(는) 'partial'로 표시되지 않은 '{1}' 형식에 포함되어 있습니다. COM 원본 생성은 '{0}' 메서드를 무시합니다. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - 'LibraryImportAttribute'로 표시된 메소드는 'static', 'partial' 및 비제네릭이어야 합니다. P/Invoke 소스 생성은 'static'이 아니거나 'partial'이 아니거나 제네릭인 메서드를 무시합니다. + 'GeneratedComInterfaceAttribute'로 표시된 인터페이스의 메서드는 제네릭이 아니어야 합니다. COM 원본 생성은 제네릭 메서드를 무시합니다. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - '{0}' 메서드는 'LibraryImportAttribute'로 표시된 경우 '정적', '부분적'이어야 하며 일반이 아니어야 합니다. P/Invoke 소스 생성은 '{0}' 메서드를 무시합니다. + 'GeneratedComInterfaceAttribute'로 표시된 인터페이스에서 '{0}' 메서드는 제네릭이 아니어야 합니다. COM 원본 생성은 '{0}' 메서드를 무시합니다. @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + 'GeneratedComInterfaceAttribute'로 특성이 지정된 인터페이스는 부분적이고 제네릭이 아니어야 하며 'System.Runtime.InteropServices.GuidAttribute'를 사용하여 GUID를 지정해야 합니다. Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + '{0}' 인터페이스는 'GeneratedComInterfaceAttribute'로 특성이 지정되지만 제네릭입니다. @@ -169,12 +169,12 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} + '{0}' 인터페이스의 'StringMarshalling' 및 'StringMarshallingCustomType' 구성이 잘못되었습니다. {1} The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + '{0}' 메서드의 'StringMarshalling' 및 'StringMarshallingCustomType' 구성이 잘못되었습니다. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - 소스 생성 P/Invoke에서 지원하지 않는 형식의 경우 결과 P/Invoke는 기본 런타임에 의존하여 지정된 형식을 마샬링합니다. + 원본 생성 COM에서 지원하지 않는 형식의 경우 결과 함수 포인터는 기본 런타임을 사용하여 지정된 형식을 마샬링합니다. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - 형식 '{0}'은(는) 소스 생성 P/Invoke에서 지원되지 않습니다. 생성된 소스는 '{1}' 매개 변수의 마샬링을 처리하지 않습니다. + 형식 '{0}'은(는) 원본 생성 COM에서 지원되지 않습니다. 생성된 원본은 '{1}' 매개 변수의 마샬링을 처리하지 않습니다. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - 형식 '{0}'은(는) 소스 생성 P/Invoke에서 지원되지 않습니다. 생성된 소스는 '{1}' 메서드의 반환 값 마샬링을 처리하지 않습니다. + '{0}' 형식은 원본 생성 COM에서 지원되지 않습니다. 생성된 원본은 '{1}' 메서드의 반환 값 마샬링을 처리하지 않습니다. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - 지정된 형식은 소스 생성 P/Invoke에서 지원되지 않습니다. + 지정된 형식은 원본 생성 COM에서 지원되지 않습니다. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf index c9b2075eaa77ab..81da9ad3765ab8 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - Funkcja P/Invokes generowana przez źródło zignoruje każdą nieobsługiwaną konfigurację. + COM wygenerowany przez źródło zignoruje każdą konfigurację, która nie jest obsługiwana. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Konfiguracja {0} nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast tego zwykłego elementu „DllImport”. + Konfiguracja „{0}” nie jest obsługiwana przez COM generowany przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast niej `ComImport`. The specified marshalling configuration is not supported by source-generated COM. {0}. - Określona konfiguracja skierowania nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło. {0}. + Określona konfiguracja skierowania nie jest obsługiwana przez COM generowany źródłowo. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Określona konfiguracja „{0}” dla parametru „{1}” nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast tego zwykłego elementu „DllImport”. + Określona konfiguracja „{0}” dla parametru „{1}” nie jest obsługiwana przez COM generowany przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast niej `ComImport`. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Określona konfiguracja „{0}” dla wartości zwracanej przez metodę „{1}” nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast tego zwykłego elementu „DllImport”. + Określona konfiguracja „{0}” dla wartości zwracanej metody „{1}” nie jest obsługiwana przez COM generowany przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast niej `ComImport`. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Określona wartość „{0}” dla parametru „{1}” nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast tego zwykłego elementu „DllImport”. + Określona wartość „{0}” dla „{1}” nie jest obsługiwana przez COM generowany przez źródło. Jeśli określona konfiguracja jest wymagana, użyj zamiast niej `ComImport`. Specified configuration is not supported by source-generated COM. - Określona konfiguracja nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło. + Określona konfiguracja nie jest obsługiwana przez COM wygenerowany przez źródło. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - Metoda „{0}” jest zawarta w typie „{1}”, który nie jest oznaczony jako „częściowy”. Generowanie źródła funkcji P/Invoke zignoruje metodę „{0}”. + Metoda „{0}” jest zawarta w typie „{1}”, który nie jest oznaczony jako „częściowy”. Generowanie źródła COM zignoruje metodę „{0}”. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Metody oznaczone jako atrybut „LibraryImportAttribute” powinny być „statyczne”, „częściowe” i nieogólne. Generowanie źródła funkcji P/Invoke zignoruje metody, które nie są „statyczne”, nie są „częściowe” lub ogólne. + Metody w interfejsach oznaczone atrybutem „GeneratedComInterfaceAttribute” powinny być nie-ogólne. Generowanie źródła COM zignoruje metody, które są ogólne. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - Metoda „{0}” powinna być „statyczna”, „częściowa” i nieogólna, gdy jest oznaczona za pomocą atrybutu „LibraryImportAttribute”. Generowanie źródła funkcji P/Invoke zignoruje metodę „{0}”. + Metoda „{0}” powinna być nie-ogólna w interfejsach oznaczonych atrybutem „GeneratedComInterfaceAttribute”. Generowanie źródła COM zignoruje metodę „{0}”. @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfejsy z atrybutem „GeneratedComInterfaceAttribute” muszą być częściowe, nie być ogóle, i muszą określać identyfikator GUID z atrybutem „System.Runtime.InteropServices.GuidAttribute”. Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + Interfejs „{0}” ma atrybut „GeneratedComInterfaceAttribute”, ale jest ogólny. @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + Konfiguracja elementów „StringMarshalling” i „StringMarshallingCustomType” w metodzie „{0}” jest nieprawidłowa. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - W przypadku typów, które nie są obsługiwane przez funkcję P/Invokes generowaną przez źródło, wynikowa funkcja P/Invoke będzie polegać na bazowym środowisku uruchomieniowym, aby skierować określony typ. + W przypadku typów, które nie są obsługiwane przez COM wygenerowany przez źródło, wynikowy wskaźnik funkcji będzie polegał na bazowym środowisku uruchomieniowym w celu skierowania określonego typu. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - Typ „{0}” nie jest obsługiwany przez funkcję P/Invokes generowaną przez źródło. Wygenerowane źródło nie obsługuje skierowania parametru „{1}”. + Typ „{0}” nie jest obsługiwany przez COM generowany przez źródło. Wygenerowane źródło nie będzie obsługiwać skierowania parametru „{1}”. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - Typ „{0}” nie jest obsługiwany przez funkcję P/Invokes generowaną przez źródło. Wygenerowane źródło nie obsługuje skierowania wartości zwracanej przez metodę „{1}”. + Typ „{0}” nie jest obsługiwany przez COM generowany przez źródło. Wygenerowane źródło nie będzie obsługiwać skierowania wartości zwracanej przez metodę „{1}”. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Określony typ nie jest obsługiwany przez funkcję P/Invokes generowaną przez źródło + Określony typ nie jest obsługiwany przez źródłowy COM diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf index 94df2644a56619..9ce3756f147c4c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf @@ -24,57 +24,57 @@ COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. - COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. + A geração de fonte de interface COM requer que todas as interfaces COM base sejam interfaces válidas. Corrija quaisquer problemas na interface base para resolver este diagnóstico. COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. - COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. + A interface COM {0} herda de {1}, que contém erros. ComInterfaceGenerator não irá gerar fonte para{0}. The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. - The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. + A interface COM base falhou ao gerar a fonte. ComInterfaceGenerator não irá gerar fonte para esta interface. Source-generated COM will ignore any configuration that is not supported. - P/Invokes gerados pela origem ignorarão qualquer configuração sem suporte. + Um COM gerado pela origem ignorará qualquer configuração sem suporte. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - A configuração '{0}' não é suportada por P/Invokes gerados pela origem. Se a configuração especificada for necessária, use um 'DllImport' regular. + Um COM gerado pela origem não dá suporte à configuração '{0}'. Se a configuração especificada for necessária, use um 'ComImport' em vez disso. The specified marshalling configuration is not supported by source-generated COM. {0}. - Não há suporte para a configuração de marshaling especificada por P/Invokes gerados pela origem. {0}. + Um COM gerado pela origem não dá suporte à configuração de realização de marshaling especificada. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - A configuração '{0}' especificada para o parâmetro '{1}' não é suportada por P/Invokes gerados pela origem. Se a configuração especificada for necessária, use um 'DllImport' regular. + Um COM gerado pela origem não dá suporte à configuração '{0}' especificada para o parâmetro '{1}'. Se a configuração especificada for necessária, use 'ComImport' em vez disso. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - A configuração '{0}' especificada para o valor retornado do método '{1}' não é suportada por P/Invokes gerados pela origem. Se a configuração especificada for necessária, use um 'DllImport' regular. + Um Com gerado pela origem não dá suporte à configuração “{0}” especificada para o valor retornado do método “{1}”. Se a configuração especificada for necessária, use `ComImport` em vez disso. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - O valor '{0}' especificado para '{1}' não é suportada por P/Invokes gerados pela origem. Se a configuração especificada for necessária, use um 'DllImport' regular. + Um COM gerado pela origem não dá suporte ao valor '{0}' especificado para '{1}'. Se a configuração especificada for necessária, use `ComImport' em vez disso. Specified configuration is not supported by source-generated COM. - A configuração especificada não tem suporte de P/Invokes gerados pela origem. + Um COM gerado pela origem não dá suporte a uma configuração especificada. The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. + A configuração de 'StringMarshalling' e 'StringMarshallingCustomType' deve corresponder à interface COM base. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - O '{0}' está contido em um tipo '{1}' que não está marcado como 'partial'. A geração de origem P/Invoke ignorará o método '{0}'. + O método '{0}' está contido em um tipo '{1}' que não está marcado como 'partial'. A geração de origem do COM ignorará o método '{0}'. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Os métodos marcados com 'LibraryImportAttribute' devem ser 'static', 'partial' e não genéricos. A geração de origem P/Invoke ignorará os métodos que não são 'static', não-'partial' ou genéricos. + Métodos em interfaces marcados com 'GeneratedComInterfaceAttribute' não devem ser genéricos. A geração de origem do COM ignorará os métodos genéricos. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - O método '{0}' deve ser 'static', 'partial' e não genérico quando marcado com 'LibraryImportAttribute'. A geração de origem P/Invoke ignorará o método '{0}'. + O método '{0}' não deve ser genérico em interfaces marcadas com 'GeneratedComInterfaceAttribute'. A geração de origem do COM ignorará o método '{0}'. @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + As interfaces atribuídas com "GeneratedComInterfaceAttribute" devem ser parciais, não-genéricas e especificar um GUID com "System.Runtime.InteropServices.GuidAttribute". Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + A interface "{0}" é atribuída com "GeneratedComInterfaceAttribute", mas é genérica. @@ -169,12 +169,12 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} + A configuração de 'StringMarshalling' e 'StringMarshallingCustomType' na interface '{0}' é inválida. {1} The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + A configuração de 'StringMarshalling' e 'StringMarshallingCustomType' no método '{0}' é inválida. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Para tipos sem suporte por P/Invokes gerados pela origem, o P/Invoke resultante dependerá do tempo de execução subjacente para realizar marshaling no tipo especificado. + Para tipos sem suporte para um COM gerado pela origem, o ponteiro de função resultante dependerá do tempo de execução subjacente para realizar marshaling no tipo especificado. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - O tipo '{0}' não é suportado por P/Invokes gerados pela origem. A origem gerada não manipulará o marshalling do parâmetro '{1}'. + Um COM gerado pela origem não dá suporte ao tipo '{0}'. A origem gerada não realizará marshalling do parâmetro '{1}'. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - O tipo '{0}' não é suportado por P/Invokes gerados pela origem. A origem gerada não tratará marshaling do valor de retorno do método '{1}'. + Um COM gerado pela origem não dá suporte ao tipo '{0}'. A origem gerada não realizará marshaling do valor de retorno do método '{1}'. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - O tipo especificado não tem suporte de P/Invokes gerados pela origem. + Um COM gerado pela origem não dá suporte ao tipo especificado. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf index f6cb2cb3fb0499..01ab6e83a0c67f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf @@ -24,57 +24,57 @@ COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. - COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. + Для создания источника COM-интерфейса все базовые COM-интерфейсы должны быть допустимыми. Устраните все проблемы в базовом интерфейсе, чтобы разрешить это диагностическое уведомление. COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. - COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. + COM-интерфейс {0} наследует от {1}, где есть ошибки. ComInterfaceGenerator не будет создавать источник для {0}. The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. - The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. + Базовому COM-интерфейсу не удалось создать источник. ComInterfaceGenerator не будет создавать источник для этого интерфейса. Source-generated COM will ignore any configuration that is not supported. - P/Invoke с созданием источника будут игнорировать все неподдерживаемые конфигурации. + Модель COM генератора исходного кода будет игнорировать любую неподдерживаемую конфигурацию. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Конфигурация \"{0}\" не поддерживается в P/Invoke с созданием источника. Если указанная конфигурация обязательна, используйте обычный метод \"DllImport\". + Конфигурация "{0}" не поддерживается моделью COM генератора исходного кода. Если указанная конфигурация обязательна, используйте "ComImport". The specified marshalling configuration is not supported by source-generated COM. {0}. - Указанная конфигурация маршализации не поддерживается в P/Invoke с созданием источника. {0}. + Указанная конфигурация маршалинга не поддерживается моделью COM генератора исходного кода. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Указанная конфигурация \"{0}\" для параметра \"{1}\" не поддерживается в P/Invoke с созданием источника. Если указанная конфигурация обязательна, используйте обычный метод \"DllImport\". + Указанная конфигурация "{0}" для параметра "{1}" не поддерживается моделью COM генератора исходного кода. Если указанная конфигурация обязательна, используйте "ComImport". The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Указанная конфигурация \"{0}\" для возвращаемого значения метода \"{1}\" не поддерживается в P/Invoke с созданием источника. Если указанная конфигурация обязательна, используйте обычный метод \"DllImport\". + Указанная конфигурация "{0}" для возвращаемого значения метода "{1}" не поддерживается моделью COM генератора исходного кода. Если указанная конфигурация обязательна, используйте "ComImport". The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - Указанное значение “{0}” для “{1}” не поддерживается в P/Invoke с созданием источника. Если указанная конфигурация обязательна, используйте обычный метод “DllImport”. + Указанное значение "{0}" для "{1}" не поддерживается моделью COM генератора исходного кода. Если указанная конфигурация обязательна, используйте "ComImport". Specified configuration is not supported by source-generated COM. - Указанная конфигурация не поддерживается в P/Invoke с созданием источника. + Указанная конфигурация не поддерживается моделью COM генератора исходного кода. The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. + Конфигурация "StringMarshalling" и "StringMarshallingCustomType" должна соответствовать базовому COM-интерфейсу. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - Метод \"{0}\" содержится в типе \"{1}\", который не помечен как \"partial\". Метод \"{0}\" будет игнорироваться при создании источника в P/Invoke. + Метод "{0}" содержится в типе "{1}", который не помечен как "partial". Генерация исходного кода модели COM будет игнорировать метод "{0}". @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - Методы, помеченные атрибутом \"LibraryImportAttribute\", должны быть \"static\", \"partial\" и неуниверсальными. При создании источника в P/Invoke будут игнорироваться методы, отличные от \"static\", \"partial\" или универсальные. + Методы для интерфейсов с атрибутом "GeneratedComInterfaceAttribute" должны быть неуниверсальными. Генерация исходного кода модели COM будет игнорировать универсальные методы. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - Метод \"{0}\" должен быть \"static\", \"partial\" и неуниверсальным, если он имеет атрибут \"LibraryImportAttribute\". При создании источника в P/Invoke метод \"{0}\" игнорируется. + Метод "{0}" должен быть неуниверсальным для интерфейсов с атрибутом "GeneratedComInterfaceAttribute". Генерация исходного кода модели COM будет игнорировать метод "{0}". @@ -169,12 +169,12 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} + Конфигурация "StringMarshalling" и "StringMarshallingCustomType" в интерфейсе "{0}" недопустима. {1} The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + Конфигурация \"StringMarshalling\" и \"StringMarshallingCustomType\" в методе \"{0}\" недопустима. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Для типов, которые не поддерживаются в P/Invoke с созданием источника, в полученном P/Invoke для маршализации указанного типа будет использоваться среда выполнения. + Для типов, которые не поддерживаются моделью COM генератора исходного кода, результирующий указатель на функцию будет выполнять маршалинг указанного типа на базе используемой среды выполнения. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - Тип \"{0}\" не поддерживается в P/Invoke с созданием источника. Созданный источник не будет обрабатывать маршализацию параметра \"{1}\". + Тип "{0}" не поддерживается моделью COM генератора исходного кода. Созданный исходный код не будет обрабатывать маршалинг параметра "{1}". @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - Тип \"{0}\" не поддерживается в P/Invoke с созданием источника. Созданный источник не будет обрабатывать маршализацию возвращаемого значения метода \"{1}\". + Тип "{0}" не поддерживается моделью COM генератора исходного кода. Созданный исходный код не будет обрабатывать маршалинг возвращаемого значения метода "{1}". @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Указанный тип не поддерживается в P/Invoke с созданием источника. + Указанный тип не поддерживается моделью COM генератора исходного кода. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf index e9bf892c3a6aa0..a14f019f5ffdf4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf @@ -24,57 +24,57 @@ COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. - COM interface source generation requires all base COM interfaces to be valid interfaces. Fix any issues on the base interface to resolve this diagnostic. + COM arabiriminin kaynak oluşturabilmesi için tüm temel COM arabirimlerinin geçerli arabirimler olması gerekiyor. Bu tanılamayı çözmek için temel arabirimdeki sorunları düzeltin. COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. - COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}. + {0} COM arabirimi hata içeren {1} konumundan devralıyor. ComInterfaceGenerator, {0} için kaynak oluşturamaz. The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. - The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface. + Temel COM arabirimi kaynak oluşturamadı. ComInterfaceGenerator bu arabirim için kaynak oluşturamaz. Source-generated COM will ignore any configuration that is not supported. - Kaynak tarafından oluşturulan P/Invokes desteklenmeyen yapılandırmaları yok sayar. + Kaynak tarafından oluşturulan COM, desteklenmeyen herhangi bir yapılandırmayı yoksayar. The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{0}' yapılandırması, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. Belirtilen yapılandırma gerekli ise, bunun yerine normal bir 'DllImport' kullanın. + '{0}' yapılandırması, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. Belirtilen yapılandırma gerekiyorsa, bunun yerine "ComImport" kullanın. The specified marshalling configuration is not supported by source-generated COM. {0}. - Belirtilen sıralama yapılandırması, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. {0}. + Belirtilen sıralama yapılandırması, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. {0}. The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{1}' parametresi için belirtilen '{0}' yapılandırması, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. Belirtilen yapılandırma gerekli ise, bunun yerine normal bir 'DllImport' kullanın. + '{1}' parametresi için belirtilen '{0}' yapılandırması, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. Belirtilen yapılandırma gerekiyorsa, bunun yerine "ComImport" kullanın. The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{1}' metodunun dönüş değeri için belirtilen '{0}' yapılandırması, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. Belirtilen yapılandırma gerekli ise, bunun yerine normal bir 'DllImport' kullanın. + '{1}' yönteminin dönüş değeri için belirtilen '{0}' yapılandırması, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. Belirtilen yapılandırma gerekiyorsa, bunun yerine "ComImport" kullanın. The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - '{1}' için belirtilen '{0}' değeri, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. Belirtilen yapılandırma gerekli ise, bunun yerine normal bir 'DllImport' kullanın. + '{1}' için belirtilen '{0}' değeri, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. Belirtilen yapılandırma gerekiyorsa, bunun yerine "ComImport" kullanın. Specified configuration is not supported by source-generated COM. - Belirtilen yapılandırma, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. + Belirtilen yapılandırma, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface. + 'StringMarshalling' ve 'StringMarshallingCustomType' yapılandırması temel COM arabirimiyle eşleşmelidir. @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - '{0}'metodu, 'partial' olarak işaretlenmemiş olan bir '{1}' türünün içinde yer alıyor. P/Invoke kaynak oluşturma işlemi, '{0}' metodunu yok sayacak. + '{0}' yöntemi, 'kısmi' olarak işaretlenmemiş bir '{1}' türünde yer alıyor. COM kaynak üretimi, '{0}' yöntemini yok sayacaktır. @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - 'LibraryImportAttribute' ile işaretlenmiş metotlar 'static', 'partial' ve genel olmayan özellikte olmalıdır. P/Invoke kaynak oluşturma 'static', 'partial' ve genel olmayan özellikteki metotlar dışında kalan metotları yok sayar. + 'GeneratedComInterfaceAttribute' ile işaretlenmiş ara birimlerdeki yöntemler genel olmamalıdır. COM kaynak oluşturma, genel yöntemleri yok sayacaktır. Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - 'LibraryImportAttribute' ile işaretlendiğinde '{0}' metodu 'static', 'partial' ve genel olmayan özellikte olmalıdır. P/Invoke kaynak oluşturma '{0}' metodunu yok sayar. + 'GeneratedComInterfaceAttribute' ile işaretlenmiş arabirimlerde '{0}' yöntemi genel olmamalıdır. COM kaynak üretimi, '{0}' yöntemini yok sayacaktır. @@ -134,12 +134,12 @@ Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. - Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + 'GeneratedComInterfaceAttribute' özniteliğine sahip arabirimler kısmi olmalı, genel olmamalı ve 'System.Runtime.InteropServices.GuidAttribute' ile bir GUID belirtmelidir. Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. - Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic. + '{0}' 'GeneratedComInterfaceAttribute' özniteliğine sahip ancak genel. @@ -169,12 +169,12 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1} + '{0}' arabirimindeki 'StringMarshalling' ve 'StringMarshallingCustomType' yapılandırması geçersiz. {1} The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + '{0}' metodundaki 'StringMarshalling' ve 'StringMarshallingCustomType' yapılandırması geçersiz. {1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - Kaynak tarafından oluşturulan P/Invokes tarafından desteklenmeyen türler için, elde edilen P/Invoke, belirtilen türü sıralamak için temel alınan çalışma zamanını kullanır. + Kaynak tarafından oluşturulan COM tarafından desteklenmeyen türler için, ortaya çıkan işlev işaretçisi, belirtilen türü sıralamak için temeldeki çalışma zamanına güvenir. The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - '{0}' türü, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. Oluşturulan kaynak, '{1}' parametresinin sıralamasını işlemez. + '{0}' türü, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. Üretilen kaynak, '{1}' parametresinin sıralamasını işlemeyecektir. @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - '{0}' türü, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor. Oluşturulan kaynak, '{1}' metodunun dönüş değerinin sıralamasını işlemez. + '{0}' türü, kaynak tarafından oluşturulan COM tarafından desteklenmiyor. Oluşturulan kaynak, '{1}' yönteminin dönüş değerinin sıralamasını işlemez. @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - Belirtilen tür, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor + Belirtilen tür, kaynak tarafından oluşturulan COM tarafından desteklenmiyor diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf index 8fae13efb4960f..38706465712e52 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - 源生成的 P/Invoke 将忽略任何不受支持的配置。 + 源生成的 COM 将忽略任何不受支持的配置。 The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 源生成的 P/Invoke 不支持“{0}”配置。如果需要指定的配置,请改用常规的 “DllImport”。 + 源生成的 COM 不支持 "{0}" 配置。如果需要指定的配置,请改用 `ComImport`。 The specified marshalling configuration is not supported by source-generated COM. {0}. - 源生成的 P/Invoke 不支持指定的封送配置。{0}。 + 源生成的 COM 不支持指定的封送配置。{0}。 The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 源生成的 P/Invoke 不支持为参数“{1}”指定的“{0}”配置。如果需要指定的配置,请改用常规的 `DllImport`。 + 源生成的 COM 不支持方法“{0}”参数“{1}”配置。如果需要指定配置,请改用 `ComImport`。 The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 源生成的 P/Invoke 不支持为方法“{1}”的返回值指定的“{0}”配置。如果需要指定的配置,请改用常规的 “DllImport”。 + 源生成的 COM 不支持方法“{0}”返回值的指定“{1}”配置。如果需要指定配置,请改用 `ComImport`。 The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 源生成的 P/Invoke 不支持为“{1}”指定的值“{0}”。如果需要指定的配置,请改用常规的 “DllImport”。 + 源生成的 COM 不支持为 "{1}" 指定的值 "{0}"。如果需要指定的配置,请改用 `ComImport`。 Specified configuration is not supported by source-generated COM. - 源生成的 P/Invoke 不支持指定的配置。 + 源生成的 COM 不支持指定的配置。 @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - 方法“{0}”包含在未标记为 “partial” 的类型“{1}”中。P/Invoke 源生成将忽略方法“{0}”。 + 方法 "{0}" 包含在未标记为 "partial" 的类型 "{1}" 中。COM 源生成将忽略方法 "{0}"。 @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - 标记为 “LibraryImportAttribute” 的方法应为 “static”、“partial” 和非泛型。P/Invoke 源生成将忽略非“static”、“non--partial” 或泛型的方法。 + 标记为 "GeneratedComInterfaceAttribute" 的接口上的方法应为非泛型。COM 源生成将忽略泛型方法。 Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - 在标记为 “LibraryImportAttribute” 时,方法“{0}”应为 “static”、“partial” 和非泛型。P/Invoke 源生成将忽略方法“{0}”。 + 在标记为 "GeneratedComInterfaceAttribute" 的接口上时,方法 "{0}" 应为非泛型。COM 源生成将忽略方法 "{0}"。 @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + 方法“{0}”上的 “StringMarshalling” 和 “StringMarshallingCustomType” 的配置无效。{1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - 对于源生成的 P/Invoke 不支持的类型,生成的 P/Invoke 将依赖基础运行时来封送指定的类型。 + 对于源生成的 COM 不支持的类型,生成的函数指针将依赖基础运行时来封送指定的类型。 The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - 源生成的 P/Invoke 不支持“{0}”类型。生成的源将不处理参数“{1}”的封送。 + 源生成的 COM 不支持“{0}”类型。生成的源将不处理参数“{1}”的封送。 @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - 源生成的 P/Invoke 不支持“{0}”类型。生成的源将不处理方法“{1}”的返回值的封送。 + 源生成的 COM 不支持 "{0}" 类型。生成的源将不处理方法 "{1}" 的返回值的封送。 @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - 源生成的 P/Invoke 不支持指定的类型 + 源生成的 COM 不支持指定的类型 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf index 1bc4bef29f222f..164a570823367c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf @@ -39,37 +39,37 @@ Source-generated COM will ignore any configuration that is not supported. - 来源產生的 P/Invokes 將會忽略任何不支援的設定。 + 来源產生的 COM 將會略過任何不支援的設定。 The '{0}' configuration is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 来源產生的 P/Invokes 不支援為'{0}'設定。如果需要指定的設定,請改用一般 'DllImport'。 + 來源產生的 COM 不支援 '{0}' 設定。如果需要指定的設定,請改用 `ComImport`。 The specified marshalling configuration is not supported by source-generated COM. {0}. - 来源產生的 P/Invokes 不支援指定的排列設定。{0}。 + 来源產生的 COM 不支援指定的封送處理設定。{0}。 The specified '{0}' configuration for parameter '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 来源產生的 P/Invokes 不支援为参數 '{0}' 指定的'{1}'設定。如果需要指定的設定,請改用一般 'DllImport'。 + 來源產生的 COM 不支援為參數 '{1}' 指定的 '{0}' 設定。如果需要指定的設定,請改用 `ComImport`。 The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 来源產生的 P/Invokes 不支援為方法 '{0}' 的傳回值指定的 '{1}' 設定。如果需要指定的設定,請改用一般 'DllImport'。 + 來源產生的 COM 不支援為方法 '{1}' 的傳回值指定 '{0}' 設定。如果需要指定的設定,請改用 `ComImport`。 The specified value '{0}' for '{1}' is not supported by source-generated COM. If the specified configuration is required, use `ComImport` instead. - 来源產生的 P/Invokes 不支援為'{0}'指定的值 '{1}'。如果需要指定的設定,請改用一般 'DllImport'。 + 來源產生的 COM 不支援為 '{1}' 指定的值 '{0}'。如果需要指定的設定,請改用 `ComImport`。 Specified configuration is not supported by source-generated COM. - 来源產生的 P/Invokes 不支援指定的設定。 + 来源產生的 COM 不支援指定的設定。 @@ -89,7 +89,7 @@ Method '{0}' is contained in a type '{1}' that is not marked 'partial'. COM source generation will ignore method '{0}'. - 方法 '{0}' 包含在未標示為 'partial' 的類型'{1}'中。產生 P/Invoke 来源會忽略方法'{0}'。 + 方法 '{0}' 包含在未標示為 'partial' 的類型 '{1}' 中。COM 来源產生將略過方法 '{0}'。 @@ -99,12 +99,12 @@ Methods on interfaces marked with 'GeneratedComInterfaceAttribute' should be non-generic. COM source generation will ignore methods that are generic. - 以 'LibraryImportAttribute' 標示的方法應該是 'static'、'partial' 和非泛型。產生 P/Invoke 来源會忽略非'static'、non-'partial' 或一般的方法。 + 標示為 'GeneratedComInterfaceAttribute' 之介面上的方法應該是非泛型的。COM 來源產生將略過泛型方法。 Method '{0}' should be non-generic when on interfaces marked with the 'GeneratedComInterfaceAttribute'. COM source generation will ignore method '{0}'. - 以 'LibraryImportAttribute' 標示時,方法'{0}'應為 'static'、'partial' 和非泛型。產生 P/Invoke 来源會忽略方法'{0}'。 + 在用 'GeneratedComInterfaceAttribute' 標示的介面上,方法 '{0}' 應該是非泛型的。COM 來源產生將略過方法 '{0}'。 @@ -174,7 +174,7 @@ The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + 方法 '{0}' 上的 'StringMarshalling' 和 'StringMarshallingCustomType' 設定無效。{1} {1} is a message containing additional details about what is not valid @@ -214,12 +214,12 @@ For types that are not supported by source-generated COM, the resulting function pointer will rely on the underlying runtime to marshal the specified type. - 對於來源產生的 P/Invokes 不支援的類型,產生的 P/Invoke 將依賴基礎運行時間來封送指定的類型。 + 對於來源產生的 COM 不支援的類型,產生的函式指標將依賴基礎執行階段來封送處理指定的類型。 The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of parameter '{1}'. - 来源產生的 P/Invokes 不支援類型 '{0}'。產生的来源將不會處理參數 '{1}' 的排列。 + 来源產生的 COM 不支援類型 '{0}'。產生的来源將不會處理參數 '{1}' 的封送處理。 @@ -230,7 +230,7 @@ The type '{0}' is not supported by source-generated COM. The generated source will not handle marshalling of the return value of method '{1}'. - 来源產生的 P/Invokes 不支援類型 '{0}'。產生的來源將不會處理方法 '{1}' 的傳回值排列。 + 来源產生的 COM 不支援類型 '{0}'。產生的來源將不會處理方法 '{1}' 的傳回值之封送處理。 @@ -241,7 +241,7 @@ Specified type is not supported by source-generated COM - 来源產生的 P/Invokes 不支援指定的類型。 + 来源產生的 COM 不支援指定的類型。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/DiagnosticOr.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/DiagnosticOr.cs new file mode 100644 index 00000000000000..0be31fa6277675 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/DiagnosticOr.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.Interop +{ + /// + /// A union of Diagnostic and T, representing either a value of type T or a Diagnostic. + /// Useful helper functions exist in and . + /// + public abstract record DiagnosticOr + { + public abstract bool IsValue { get; } + + public bool IsDiagnostic => !IsValue; + + /// + /// Throws if IsValue is false + /// + public abstract T Value { get; } + + /// + /// Throws if IsDiagnostic is false + /// + public abstract Diagnostic Diagnostic { get; } + + private sealed record Diag : DiagnosticOr + { + private readonly Diagnostic _diagnostic; + internal Diag(Diagnostic diagnostic) => _diagnostic = diagnostic; + public override bool IsValue => false; + public override T Value => throw new InvalidOperationException(); + public override Diagnostic Diagnostic => _diagnostic; + } + + private sealed record Val : DiagnosticOr + { + private readonly T _value; + internal Val(T value) => _value = value; + public override bool IsValue => true; + public override T Value => _value; + public override Diagnostic Diagnostic => throw new InvalidOperationException(); + } + + /// + /// Create a Diagnostic variant + /// + public static DiagnosticOr From(Diagnostic diagnostic) + { + Debug.Assert(diagnostic is not null); + return new Diag(diagnostic); + } + + /// + /// Create a Value variant + /// + public static DiagnosticOr From(T value) + { + Debug.Assert(value is not null); + return new Val(value); + } + } + + public static class DiagnosticOrTHelperExtensions + { + /// + /// Splits the elements of into a "values" provider and a "diagnositics" provider. + /// + public static (IncrementalValuesProvider, IncrementalValuesProvider) Split(this IncrementalValuesProvider> provider) + { + var values = provider.Where(x => x.IsValue).Select(static (x, ct) => x.Value); + var diagnostics = provider.Where(x => x.IsDiagnostic).Select(static (x, ct) => x.Diagnostic); + return (values, diagnostics); + } + + /// + /// Splits the inner arrays of into values and diagnostics. + /// + public static (IncrementalValuesProvider>, IncrementalValuesProvider) SplitArrays(this IncrementalValuesProvider>> provider) + { + var values = provider.Select((arr, ct) => arr.Where(x => x.IsValue).Select((x, ct) => x.Value).ToSequenceEqualImmutableArray()); + var diagnostics = provider.SelectMany((arr, ct) => arr.Where(x => x.IsDiagnostic).Select((x, ct) => x.Diagnostic)); + return (values, diagnostics); + } + + /// + /// Splits the elements of into groups depending on whether Item1 is a value or a Diagnostic. + /// + public static (IncrementalValuesProvider<(T, T2)>, IncrementalValuesProvider) Split(this IncrementalValuesProvider<(DiagnosticOr, T2)> provider) + { + var values = provider.Where(x => x.Item1.IsValue).Select(static (x, ct) => (x.Item1.Value, x.Item2)); + var diagnostics = provider.Where(x => !x.Item1.IsValue).Select(static (x, ct) => x.Item1.Diagnostic); + return (values, diagnostics); + } + + /// + /// Filters the by whether or not the is a , reports the diagnostics, and returns the values. + /// + public static IncrementalValuesProvider FilterAndReportDiagnostics(this IncrementalGeneratorInitializationContext ctx, IncrementalValuesProvider> diagnosticOrValues) + { + var (values, diagnostics) = diagnosticOrValues.Split(); + ctx.RegisterDiagnostics(diagnostics); + return values; + } + + /// + /// Filters both by whether or not the value in is a , reports the diagnostics, and returns the values. + /// + public static (IncrementalValuesProvider, IncrementalValuesProvider) FilterAndReportDiagnostics( + this IncrementalGeneratorInitializationContext ctx, + IncrementalValuesProvider> diagnosticOrValues, + IncrementalValuesProvider associatedValues) + { + var (values, diagnostics) = diagnosticOrValues.Zip(associatedValues).Split(); + ctx.RegisterDiagnostics(diagnostics); + return values.Split(); + } + + /// + /// Filters each inner of by whether the elements are s, reports the diagnostics, and returns the values. + /// + public static IncrementalValuesProvider> FilterAndReportDiagnostics( + this IncrementalGeneratorInitializationContext ctx, + IncrementalValuesProvider>> diagnosticOrValues) + { + var (values, diagnostics) = diagnosticOrValues.SplitArrays(); + ctx.RegisterDiagnostics(diagnostics); + return values; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IncrementalValuesProviderExtensions.cs similarity index 86% rename from src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs rename to src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IncrementalValuesProviderExtensions.cs index 826aa4e01112e5..123d649c913cd5 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IncrementalValuesProviderExtensions.cs @@ -7,7 +7,7 @@ namespace Microsoft.Interop { - internal static class IncrementalValuesProviderExtensions + public static class IncrementalValuesProviderExtensions { public static IncrementalValuesProvider<(T Left, U Right)> Zip(this IncrementalValuesProvider left, IncrementalValuesProvider right) { @@ -46,5 +46,10 @@ public static IncrementalValuesProvider SelectNormalized(this Incr { return provider.Select((node, ct) => node.NormalizeWhitespace()); } + + public static (IncrementalValuesProvider, IncrementalValuesProvider) Split(this IncrementalValuesProvider<(T, T2)> provider) + { + return (provider.Select(static (data, ct) => data.Item1), provider.Select(static (data, ct) => data.Item2)); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs index b592d8b1815cb8..5607c79bdf5ede 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop; using Microsoft.Interop.UnitTests; +using SourceGenerators.Tests; using Xunit; using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; @@ -39,7 +40,7 @@ partial interface INativeAPI } """; - await VerifySourceGeneratorAsync(source, "INativeAPI"); + await VerifyGeneratedTypeShapes(source, "INativeAPI"); } [Fact] @@ -65,7 +66,7 @@ partial interface J } """; - await VerifySourceGeneratorAsync(source, "I", "J"); + await VerifyGeneratedTypeShapes(source, "I", "J"); } [Fact] @@ -96,7 +97,7 @@ partial interface J } """; - await VerifySourceGeneratorAsync(source, "I", "Empty", "J"); + await VerifyGeneratedTypeShapes(source, "I", "Empty", "J"); } [Fact] @@ -122,7 +123,111 @@ partial interface J : I } """; - await VerifySourceGeneratorAsync(source, "I", "J"); + await VerifyGeneratedTypeShapes(source, "I", "J"); + } + + [Fact] + public async Task InheritingComInterfacesGenerateShadowingMethodsWithDefaultImplementations() + { + string source = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + [Guid("9D3FD745-3C90-4C10-B140-FAFB01E3541D")] + partial interface I + { + void Method(); + void Method2(); + } + [GeneratedComInterface] + [Guid("734AFCEC-8862-43CB-AB29-5A7954929E23")] + partial interface J : I + { + void MethodA(); + void MethodB(); + } + """; + + var test = new VerifyCompilationTest(false) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck, + CompilationVerifier = VerifyCompilation + }; + await test.RunAsync(); + + static void VerifyCompilation(Compilation comp) + { + var j = comp.GetTypeByMetadataName("J"); + Assert.NotNull(j); + + var shadowingMethod = Assert.Single(j.GetMembers("Method")); + VerifyShadowingMethodShape(shadowingMethod); + + shadowingMethod = Assert.Single(j.GetMembers("Method2")); + VerifyShadowingMethodShape(shadowingMethod); + } + } + + [Fact] + public async Task InheritingComInterfacesGenerateShadowingMethodsWithDefaultImplementations_LongInheritanceChain() + { + string source = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + [Guid("9D3FD745-3C90-4C10-B140-FAFB01E3541D")] + partial interface I + { + void Method(); + } + [GeneratedComInterface] + [Guid("734AFCEC-8862-43CB-AB29-5A7954929E23")] + partial interface J : I + { + void Method2(); + } + [GeneratedComInterface] + [Guid("0DB41042-0255-4CDD-B73A-9C5D5F31303D")] + partial interface K : J + { + void MethodA(); + void MethodB(); + } + """; + + var test = new VerifyCompilationTest(false) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck, + CompilationVerifier = VerifyCompilation + }; + await test.RunAsync(); + + static void VerifyCompilation(Compilation comp) + { + var k = comp.GetTypeByMetadataName("K"); + Assert.NotNull(k); + + var shadowingMethod = Assert.Single(k.GetMembers("Method")); + VerifyShadowingMethodShape(shadowingMethod); + + shadowingMethod = Assert.Single(k.GetMembers("Method2")); + Assert.False(shadowingMethod.IsAbstract); + Assert.True(shadowingMethod.IsVirtual); + VerifyShadowingMethodShape(shadowingMethod); + } + } + + private static void VerifyShadowingMethodShape(ISymbol method) + { + Assert.False(method.IsAbstract); + Assert.True(method.IsVirtual); + + var syntax = Assert.IsType(Assert.Single(method.DeclaringSyntaxReferences).GetSyntax()); + Assert.Contains(syntax.Modifiers, token => token.IsKind(SyntaxKind.NewKeyword)); } [Fact] @@ -170,7 +275,7 @@ static void VerifyCompilation(Compilation comp) } } - private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames) + private static async Task VerifyGeneratedTypeShapes(string source, params string[] typeNames) { GeneratedShapeTest test = new(typeNames) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs index ee1c28017c555f..7c00170541d6f4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs @@ -19,6 +19,7 @@ using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; using StringMarshalling = System.Runtime.InteropServices.StringMarshalling; using System.Runtime.InteropServices.Marshalling; +using Microsoft.CodeAnalysis.CSharp; namespace ComInterfaceGenerator.Unit.Tests { @@ -418,5 +419,75 @@ partial interface {|#0:IFoo2|} await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic); } + + [Fact] + public async Task VerifyComInterfaceInheritingFromComInterfaceInOtherAssemblyReportsDiagnostic() + { + string additionalSource = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + [Guid("9D3FD745-3C90-4C10-B140-FAFB01E3541D")] + public partial interface I + { + void Method(); + } + """; + + string source = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + [Guid("0DB41042-0255-4CDD-B73A-9C5D5F31303D")] + partial interface {|#0:J|} : I + { + void MethodA(); + } + """; + + var test = new VerifyComInterfaceGenerator.Test(referenceAncillaryInterop: false) + { + TestState = + { + Sources = + { + ("Source.cs", source) + }, + AdditionalProjects = + { + ["Other"] = + { + Sources = + { + ("Other.cs", additionalSource) + }, + }, + }, + AdditionalProjectReferences = + { + "Other" + } + }, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck, + }; + test.TestState.AdditionalProjects["Other"].AdditionalReferences.AddRange(test.TestState.AdditionalReferences); + + test.ExpectedDiagnostics.Add( + VerifyComInterfaceGenerator + .Diagnostic(GeneratorDiagnostics.BaseInterfaceIsNotGenerated) + .WithLocation(0) + .WithArguments("J", "I")); + + // The Roslyn SDK doesn't apply the compilation options from CreateCompilationOptions to AdditionalProjects-based projects. + test.SolutionTransforms.Add((sln, _) => + { + var additionalProject = sln.Projects.First(proj => proj.Name == "Other"); + return additionalProject.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true)).Solution; + }); + + await test.RunAsync(); + } } } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index e4e8e5acfc8d9c..e329d014f075c3 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -22,7 +22,6 @@ private sealed partial class Emitter private const string CreateValueInfoMethodName = "CreateValueInfo"; private const string CtorParamInitMethodNameSuffix = "CtorParamInit"; private const string DefaultOptionsStaticVarName = "s_defaultOptions"; - private const string DefaultContextBackingStaticVarName = "s_defaultContext"; private const string OriginatingResolverPropertyName = "OriginatingResolver"; private const string InfoVarName = "info"; private const string NumberHandlingPropName = "NumberHandling"; @@ -1026,12 +1025,10 @@ private static SourceText GetRootJsonContextImplementation(ContextGenerationSpec writer.WriteLine(); writer.WriteLine($$""" - private static {{contextTypeRef}}? {{DefaultContextBackingStaticVarName}}; - /// /// The default associated with a default instance. /// - public static {{contextTypeRef}} Default => {{DefaultContextBackingStaticVarName}} ??= new {{contextTypeRef}}(new {{JsonSerializerOptionsTypeRef}}({{DefaultOptionsStaticVarName}})); + public static {{contextTypeRef}} Default { get; } = new {{contextTypeRef}}(new {{JsonSerializerOptionsTypeRef}}({{DefaultOptionsStaticVarName}})); /// /// The source-generated options associated with this context. diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs index 2af24e5067538e..a61ba2bc6a877f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs @@ -171,6 +171,22 @@ static void AssertFieldNull(string fieldName) }).Dispose(); } + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void JsonSerializerContext_GeneratedDefault_IsSingleton() + { + RemoteExecutor.Invoke( + static () => + { + const int Count = 30; + var contexts = new MetadataContext[Count]; + Parallel.For(0, Count, i => contexts[i] = MetadataContext.Default); + + Assert.All(contexts, ctx => Assert.Same(MetadataContext.Default, ctx)); + + }).Dispose(); + } + [Fact] public static void SupportsReservedLanguageKeywordsAsProperties() { diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index a5e54288a29208..96ca4d4c939f3f 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -74,12 +74,12 @@ public static void GetAvailableThreads(out int workerThreads, out int completion public static long CompletedWorkItemCount => 0; - internal static void RequestWorkerThread() + internal static unsafe void RequestWorkerThread() { if (_callbackQueued) return; _callbackQueued = true; - QueueCallback(); + MainThreadScheduleBackgroundJob((void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); } internal static void NotifyWorkItemProgress() @@ -110,14 +110,24 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( throw new PlatformNotSupportedException(); } - [DynamicDependency("Callback")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void QueueCallback(); + internal static extern unsafe void MainThreadScheduleBackgroundJob(void* callback); - private static void Callback() +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] +#pragma warning restore CS3016 + // this callback will arrive on the bound thread, called from mono_background_exec + private static void BackgroundJobHandler () { - _callbackQueued = false; - ThreadPoolWorkQueue.Dispatch(); + try + { + _callbackQueued = false; + ThreadPoolWorkQueue.Dispatch(); + } + catch (Exception e) + { + Environment.FailFast("ThreadPool.BackgroundJobHandler failed", e); + } } private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs index ba5e678e874277..0682f3ef246c98 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; namespace System.Threading { @@ -27,19 +28,28 @@ private TimerQueue(int _) { } - [DynamicDependency("TimeoutCallback")] // This replaces the current pending setTimeout with shorter one [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void SetTimeout(int timeout); + private static extern unsafe void MainThreadScheduleTimer(void* callback, int shortestDueTimeMs); - // Called by mini-wasm.c:mono_set_timeout_exec - private static void TimeoutCallback() +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] +#pragma warning restore CS3016 + // this callback will arrive on the main thread, called from mono_wasm_execute_timer + private static void TimerHandler () { - // always only have one scheduled at a time - s_shortestDueTimeMs = long.MaxValue; + try + { + // always only have one scheduled at a time + s_shortestDueTimeMs = long.MaxValue; - long currentTimeMs = TickCount64; - ReplaceNextSetTimeout(PumpTimerQueue(currentTimeMs), currentTimeMs); + long currentTimeMs = TickCount64; + ReplaceNextTimer(PumpTimerQueue(currentTimeMs), currentTimeMs); + } + catch (Exception e) + { + Environment.FailFast("TimerQueue.TimerHandler failed", e); + } } // this is called with shortest of timers scheduled on the particular TimerQueue @@ -57,13 +67,13 @@ private bool SetTimer(uint actualDuration) _scheduledDueTimeMs = currentTimeMs + (int)actualDuration; - ReplaceNextSetTimeout(ShortestDueTime(), currentTimeMs); + ReplaceNextTimer(ShortestDueTime(), currentTimeMs); return true; } // shortest time of all TimerQueues - private static void ReplaceNextSetTimeout(long shortestDueTimeMs, long currentTimeMs) + private static unsafe void ReplaceNextTimer(long shortestDueTimeMs, long currentTimeMs) { if (shortestDueTimeMs == long.MaxValue) { @@ -75,9 +85,8 @@ private static void ReplaceNextSetTimeout(long shortestDueTimeMs, long currentTi { s_shortestDueTimeMs = shortestDueTimeMs; int shortestWait = Math.Max((int)(shortestDueTimeMs - currentTimeMs), 0); - // this would cancel the previous schedule and create shorter one - // it is expensive call - SetTimeout(shortestWait); + // this would cancel the previous schedule and create shorter one, it is expensive callback + MainThreadScheduleTimer((void*)(delegate* unmanaged[Cdecl])&TimerHandler, shortestWait); } } diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index 66404acf7137c8..f1c6ac70ce7b9c 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -4451,11 +4451,9 @@ user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data) { UserBreakCbData *data = (UserBreakCbData*)user_data; - if (frame->type == FRAME_TYPE_INTERP_TO_MANAGED || frame->type == FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX) { - data->found = TRUE; - return TRUE; - } - if (frame->managed) { + if (frame->managed || + frame->type == FRAME_TYPE_INTERP_TO_MANAGED || + frame->type == FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX) { data->found = TRUE; *data->ctx = *ctx; diff --git a/src/mono/mono/metadata/gc.c b/src/mono/mono/metadata/gc.c index f5f74cfd5073c9..9f8f4918214daa 100644 --- a/src/mono/mono/metadata/gc.c +++ b/src/mono/mono/metadata/gc.c @@ -695,7 +695,7 @@ mono_wasm_gc_finalize_notify (void) /* use this if we are going to start the finalizer thread on wasm. */ mono_coop_sem_post (&finalizer_sem); #else - mono_threads_schedule_background_job (mono_runtime_do_background_work); + mono_main_thread_schedule_background_job (mono_runtime_do_background_work); #endif } diff --git a/src/mono/mono/metadata/sgen-mono.c b/src/mono/mono/metadata/sgen-mono.c index d23f4f57ab08cc..cb1e1ba8a21585 100644 --- a/src/mono/mono/metadata/sgen-mono.c +++ b/src/mono/mono/metadata/sgen-mono.c @@ -2897,7 +2897,7 @@ sgen_client_binary_protocol_collection_end (int minor_gc_count, int generation, void sgen_client_schedule_background_job (void (*cb)(void)) { - mono_threads_schedule_background_job (cb); + mono_main_thread_schedule_background_job (cb); } #endif diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 980b4887d69f97..d85d306142e99b 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -2651,7 +2651,8 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame if (count == mono_opt_jiterpreter_jit_call_trampoline_hit_count) { mono_interp_jit_wasm_jit_call_trampoline ( rmethod->method, rmethod, cinfo, - rmethod->arg_offsets, mono_aot_mode == MONO_AOT_MODE_LLVMONLY_INTERP + initialize_arg_offsets(rmethod, mono_method_signature_internal (rmethod->method)), + mono_aot_mode == MONO_AOT_MODE_LLVMONLY_INTERP ); } else { int excess = count - mono_opt_jiterpreter_jit_call_queue_flush_threshold; diff --git a/src/mono/mono/mini/mini-arm64.c b/src/mono/mono/mini/mini-arm64.c index 69714fcbc62d4f..de3a390f143d88 100644 --- a/src/mono/mono/mini/mini-arm64.c +++ b/src/mono/mono/mini/mini-arm64.c @@ -1530,15 +1530,26 @@ is_hfa (MonoType *t, int *out_nfields, int *out_esize, int *field_offsets) } static void -add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) +add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t, gboolean is_return) { int i, size, align_size, nregs, nfields, esize; int field_offsets [16]; guint32 align; + MonoClass *klass; + klass = mono_class_from_mono_type_internal (t); size = mini_type_stack_size_full (t, &align, cinfo->pinvoke); align_size = ALIGN_TO (size, 8); + if (m_class_is_simd_type (klass) && size <= 16 && !cinfo->pinvoke && !is_return && cinfo->fr < FP_PARAM_REGS) { + ainfo->storage = ArgInSIMDReg; + ainfo->reg = cinfo->fr; + ainfo->nregs = 1; + ainfo->size = size; + cinfo->fr ++; + return; + } + nregs = align_size / 8; if (is_hfa (t, &nfields, &esize, field_offsets)) { /* @@ -1594,7 +1605,7 @@ add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) } static void -add_param (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) +add_param (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t, gboolean is_return) { MonoType *ptype; @@ -1646,7 +1657,7 @@ add_param (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) break; case MONO_TYPE_VALUETYPE: case MONO_TYPE_TYPEDBYREF: - add_valuetype (cinfo, ainfo, ptype); + add_valuetype (cinfo, ainfo, ptype, is_return); break; case MONO_TYPE_VOID: ainfo->storage = ArgNone; @@ -1661,7 +1672,7 @@ add_param (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) ainfo->storage = ArgVtypeByRef; ainfo->gsharedvt = TRUE; } else { - add_valuetype (cinfo, ainfo, ptype); + add_valuetype (cinfo, ainfo, ptype, is_return); } break; case MONO_TYPE_VAR: @@ -1703,7 +1714,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) #endif /* Return value */ - add_param (cinfo, &cinfo->ret, sig->ret); + add_param (cinfo, &cinfo->ret, sig->ret, TRUE); if (cinfo->ret.storage == ArgVtypeByRef) cinfo->ret.reg = ARMREG_R8; /* Reset state */ @@ -1724,10 +1735,10 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) cinfo->gr = PARAM_REGS; cinfo->fr = FP_PARAM_REGS; /* Emit the signature cookie just before the implicit arguments */ - add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type ()); + add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type (), FALSE); } - add_param (cinfo, ainfo, sig->params [pindex]); + add_param (cinfo, ainfo, sig->params [pindex], FALSE); if (ainfo->storage == ArgVtypeByRef) { /* Pass the argument address in the next register */ if (cinfo->gr >= PARAM_REGS) { @@ -1749,7 +1760,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) cinfo->gr = PARAM_REGS; cinfo->fr = FP_PARAM_REGS; /* Emit the signature cookie just before the implicit arguments */ - add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type ()); + add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type (), FALSE); } cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT); @@ -2601,6 +2612,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) break; case ArgVtypeInIRegs: case ArgHFA: + case ArgInSIMDReg: ins->opcode = OP_REGOFFSET; ins->inst_basereg = cfg->frame_reg; /* These arguments are saved to the stack in the prolog */ @@ -2806,21 +2818,6 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) break; } case ArgVtypeInIRegs: -#if 0 - /* FIXME: the non-LLVM codegen should also pass arguments in registers or - * else there could a mismatch when LLVM code calls non-LLVM code - * - * See https://github.com/dotnet/runtime/issues/73454 - */ - if ((t->type == MONO_TYPE_GENERICINST) && !cfg->full_aot && !sig->pinvoke) { - MonoClass *klass = mono_class_from_mono_type_internal (t); - if (mini_class_is_simd (cfg, klass)) { - lainfo->storage = LLVMArgVtypeInSIMDReg; - break; - } - } -#endif - lainfo->storage = LLVMArgAsIArgs; lainfo->nslots = ainfo->nregs; break; @@ -2839,6 +2836,9 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) lainfo->nslots = ainfo->size / 8; } break; + case ArgInSIMDReg: + lainfo->storage = LLVMArgVtypeInSIMDReg; + break; default: g_assert_not_reached (); break; @@ -3001,6 +3001,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) case ArgVtypeByRef: case ArgVtypeByRefOnStack: case ArgVtypeOnStack: + case ArgInSIMDReg: case ArgHFA: { MonoInst *ins; guint32 align; @@ -3110,6 +3111,15 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset + (i * 8), load->dreg); } break; + case ArgInSIMDReg: + MONO_INST_NEW (cfg, load, OP_LOADX_MEMBASE); + load->dreg = mono_alloc_ireg (cfg); + load->inst_basereg = src->dreg; + load->inst_offset = 0; + load->klass = src->klass; + MONO_ADD_INS (cfg->cbb, load); + add_outarg_reg (cfg, call, ArgInFReg, ainfo->reg, load); + break; default: g_assert_not_reached (); break; @@ -5584,6 +5594,9 @@ emit_move_args (MonoCompile *cfg, guint8 *code) code = emit_strfpx (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + ainfo->foffsets [part]); } break; + case ArgInSIMDReg: + code = emit_strfpq (code, ainfo->reg, ins->inst_basereg, ins->inst_offset); + break; default: g_assert_not_reached (); break; diff --git a/src/mono/mono/mini/mini-arm64.h b/src/mono/mono/mini/mini-arm64.h index 17195886b76725..f0bba652184fba 100644 --- a/src/mono/mono/mini/mini-arm64.h +++ b/src/mono/mono/mini/mini-arm64.h @@ -222,11 +222,13 @@ typedef enum { ArgOnStackR4, /* * Vtype passed in consecutive int registers. - * ainfo->reg is the firs register, + * ainfo->reg is the first register, * ainfo->nregs is the number of registers, * ainfo->size is the size of the structure. */ ArgVtypeInIRegs, + /* SIMD arg in NEON register */ + ArgInSIMDReg, ArgVtypeByRef, ArgVtypeByRefOnStack, ArgVtypeOnStack, diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index c0a64db6804033..8aa7934570b5c3 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -436,11 +436,10 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe //functions exported to be used by JS G_BEGIN_DECLS -EMSCRIPTEN_KEEPALIVE void mono_set_timeout_exec (void); +EMSCRIPTEN_KEEPALIVE void mono_wasm_execute_timer (void); //JS functions imported that we use -extern void mono_set_timeout (int t); -extern void mono_wasm_queue_tp_cb (void); +extern void mono_wasm_schedule_timer (int shortestDueTimeMs); G_END_DECLS void mono_background_exec (void); @@ -558,6 +557,9 @@ mono_init_native_crash_info (void) #endif +// this points to System.Threading.TimerQueue.TimerHandler C# method +static void *timer_handler; + #ifdef HOST_BROWSER void @@ -582,102 +584,42 @@ mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoThreadInfo } EMSCRIPTEN_KEEPALIVE void -mono_set_timeout_exec (void) +mono_wasm_execute_timer (void) { - MONO_ENTER_GC_UNSAFE; - ERROR_DECL (error); - - static MonoMethod *method = NULL; - if (method == NULL) { - MonoClass *klass = mono_class_load_from_name (mono_defaults.corlib, "System.Threading", "TimerQueue"); - g_assert (klass); - - method = mono_class_get_method_from_name_checked (klass, "TimeoutCallback", -1, 0, error); - mono_error_assert_ok (error); - g_assert (method); - } - - MonoObject *exc = NULL; - - mono_runtime_try_invoke (method, NULL, NULL, &exc, error); - - //YES we swallow exceptions cuz there's nothing much we can do from here. - //FIXME Maybe call the unhandled exception function? - if (!is_ok (error)) { - g_printerr ("timeout callback failed due to %s\n", mono_error_get_message (error)); - mono_error_cleanup (error); - } - - if (exc) { - char *type_name = mono_type_get_full_name (mono_object_class (exc)); - g_printerr ("timeout callback threw a %s\n", type_name); - g_free (type_name); - } - MONO_EXIT_GC_UNSAFE; + g_assert (timer_handler); + background_job_cb cb = timer_handler; + cb (); } + #endif void -mono_wasm_set_timeout (int timeout) +mono_wasm_main_thread_schedule_timer (void *timerHandler, int shortestDueTimeMs) { + g_assert (timerHandler); + timer_handler = timerHandler; #ifdef HOST_BROWSER #ifndef DISABLE_THREADS if (!mono_threads_wasm_is_browser_thread ()) { - mono_threads_wasm_async_run_in_main_thread_vi ((void (*)(gpointer))mono_wasm_set_timeout, GINT_TO_POINTER(timeout)); + mono_threads_wasm_async_run_in_main_thread_vi ((void (*)(gpointer))mono_wasm_schedule_timer, GINT_TO_POINTER(shortestDueTimeMs)); return; } #endif - mono_set_timeout (timeout); + mono_wasm_schedule_timer (shortestDueTimeMs); #endif } -static void -tp_cb (void) -{ - ERROR_DECL (error); - - static MonoMethod *method = NULL; - if (method == NULL) { - MonoClass *klass = mono_class_load_from_name (mono_defaults.corlib, "System.Threading", "ThreadPool"); - g_assert (klass); - - method = mono_class_get_method_from_name_checked (klass, "Callback", -1, 0, error); - mono_error_assert_ok (error); - g_assert (method); - } - - MonoObject *exc = NULL; - - mono_runtime_try_invoke (method, NULL, NULL, &exc, error); - - if (!is_ok (error)) { - g_printerr ("ThreadPool Callback failed due to error: %s\n", mono_error_get_message (error)); - mono_error_cleanup (error); - } - - if (exc) { - char *type_name = mono_type_get_full_name (mono_object_class (exc)); - g_printerr ("ThreadPool Callback threw an unhandled exception of type %s\n", type_name); - g_free (type_name); - } -} - -#ifdef HOST_BROWSER -void -mono_wasm_queue_tp_cb (void) -{ - mono_threads_schedule_background_job (tp_cb); -} -#endif - void mono_arch_register_icall (void) { #ifdef HOST_BROWSER - mono_add_internal_call_internal ("System.Threading.TimerQueue::SetTimeout", mono_wasm_set_timeout); - mono_add_internal_call_internal ("System.Threading.ThreadPool::QueueCallback", mono_wasm_queue_tp_cb); -#endif + mono_add_internal_call_internal ("System.Threading.TimerQueue::MainThreadScheduleTimer", mono_wasm_main_thread_schedule_timer); + mono_add_internal_call_internal ("System.Threading.ThreadPool::MainThreadScheduleBackgroundJob", mono_main_thread_schedule_background_job); +#ifndef DISABLE_THREADS + mono_add_internal_call_internal ("System.Runtime.InteropServices.JavaScript.JSSynchronizationContext::MainThreadScheduleBackgroundJob", mono_main_thread_schedule_background_job); +#endif /* DISABLE_THREADS */ +#endif /* HOST_BROWSER */ } void diff --git a/src/mono/mono/mini/mini-wasm.h b/src/mono/mono/mini/mini-wasm.h index 61e81e8a81fbdb..9de72fdd840e83 100644 --- a/src/mono/mono/mini/mini-wasm.h +++ b/src/mono/mono/mini/mini-wasm.h @@ -96,7 +96,7 @@ typedef struct { // sdks/wasm/driver.c is C and uses this G_EXTERN_C void mono_wasm_enable_debugging (int log_level); -void mono_wasm_set_timeout (int timeout); +void mono_wasm_main_thread_schedule_timer (void *timerHandler, int shortestDueTimeMs); int mono_wasm_assembly_already_added (const char *assembly_name); const unsigned char *mono_wasm_get_assembly_bytes (const char *name, unsigned int *size); diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index 6a33dfa0d5bc2f..dd49bf4205e3a3 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -345,27 +345,61 @@ mono_memory_barrier_process_wide (void) G_EXTERN_C extern void schedule_background_exec (void); -/* jobs is not protected by a mutex, only access from a single thread! */ -static GSList *jobs; - +// when this is called from ThreadPool, the cb would be System.Threading.ThreadPool.BackgroundJobHandler +// when this is called from JSSynchronizationContext, the cb would be System.Runtime.InteropServices.JavaScript.JSSynchronizationContext.BackgroundJobHandler +// when this is called from sgen it would be wrapper of sgen_perform_collection_inner +// when this is called from gc, it would be mono_runtime_do_background_work void -mono_threads_schedule_background_job (background_job_cb cb) +mono_main_thread_schedule_background_job (background_job_cb cb) { + g_assert (cb); #ifndef DISABLE_THREADS if (!mono_threads_wasm_is_browser_thread ()) { - THREADS_DEBUG ("worker %p queued job %p\n", (gpointer)pthread_self(), (gpointer) cb); - mono_threads_wasm_async_run_in_main_thread_vi ((void (*)(gpointer))mono_threads_schedule_background_job, cb); + THREADS_DEBUG ("mono_main_thread_schedule_background_job1: thread %p queued job %p to main thread\n", (gpointer)pthread_self(), (gpointer) cb); + mono_threads_wasm_async_run_in_main_thread_vi ((void (*)(gpointer))mono_current_thread_schedule_background_job, cb); return; } -#endif +#endif /*DISABLE_THREADS*/ + THREADS_DEBUG ("mono_main_thread_schedule_background_job2: thread %p queued job %p to current thread\n", (gpointer)pthread_self(), (gpointer) cb); + mono_current_thread_schedule_background_job (cb); +} + +#ifndef DISABLE_THREADS +MonoNativeTlsKey jobs_key; +#else /* DISABLE_THREADS */ +GSList *jobs; +#endif /* DISABLE_THREADS */ - THREADS_DEBUG ("main thread queued job %p\n", (gpointer) cb); +void +mono_current_thread_schedule_background_job (background_job_cb cb) +{ + g_assert (cb); +#ifdef DISABLE_THREADS if (!jobs) schedule_background_exec (); if (!g_slist_find (jobs, (gconstpointer)cb)) jobs = g_slist_prepend (jobs, (gpointer)cb); + +#else /*DISABLE_THREADS*/ + + GSList *jobs = mono_native_tls_get_value (jobs_key); + THREADS_DEBUG ("mono_current_thread_schedule_background_job1: thread %p queuing job %p into %p\n", (gpointer)pthread_self(), (gpointer) cb, (gpointer) jobs); + if (!jobs) + { + THREADS_DEBUG ("mono_current_thread_schedule_background_job2: thread %p calling schedule_background_exec before job %p\n", (gpointer)pthread_self(), (gpointer) cb); + schedule_background_exec (); + } + + if (!g_slist_find (jobs, (gconstpointer)cb)) + { + jobs = g_slist_prepend (jobs, (gpointer)cb); + mono_native_tls_set_value (jobs_key, jobs); + THREADS_DEBUG ("mono_current_thread_schedule_background_job3: thread %p queued job %p\n", (gpointer)pthread_self(), (gpointer) cb); + } + +#endif /*DISABLE_THREADS*/ } G_EXTERN_C @@ -377,15 +411,22 @@ EMSCRIPTEN_KEEPALIVE void mono_background_exec (void) { MONO_ENTER_GC_UNSAFE; -#ifndef DISABLE_THREADS - g_assert (mono_threads_wasm_is_browser_thread ()); -#endif +#ifdef DISABLE_THREADS GSList *j = jobs, *cur; jobs = NULL; +#else /* DISABLE_THREADS */ + THREADS_DEBUG ("mono_background_exec on thread %p started\n", (gpointer)pthread_self()); + GSList *jobs = mono_native_tls_get_value (jobs_key); + GSList *j = jobs, *cur; + mono_native_tls_set_value (jobs_key, NULL); +#endif /* DISABLE_THREADS */ for (cur = j; cur; cur = cur->next) { background_job_cb cb = (background_job_cb)cur->data; + g_assert (cb); + THREADS_DEBUG ("mono_background_exec on thread %p running job %p \n", (gpointer)pthread_self(), (gpointer)cb); cb (); + THREADS_DEBUG ("mono_background_exec on thread %p done job %p \n", (gpointer)pthread_self(), (gpointer)cb); } g_slist_free (j); MONO_EXIT_GC_UNSAFE; @@ -463,6 +504,17 @@ mono_threads_wasm_async_run_in_main_thread_vii (void (*func) (gpointer, gpointer emscripten_async_run_in_main_runtime_thread (EM_FUNC_SIG_VII, func, user_data1, user_data2); } +void +mono_threads_wasm_async_run_in_target_thread (pthread_t target_thread, void (*func) (void)) +{ + emscripten_dispatch_to_thread_async (target_thread, EM_FUNC_SIG_V, func, NULL); +} + +void +mono_threads_wasm_async_run_in_target_thread_vi (pthread_t target_thread, void (*func) (gpointer), gpointer user_data) +{ + emscripten_dispatch_to_thread_async (target_thread, EM_FUNC_SIG_VI, func, NULL, user_data); +} #endif /* DISABLE_THREADS */ diff --git a/src/mono/mono/utils/mono-threads-wasm.h b/src/mono/mono/utils/mono-threads-wasm.h index c06d8501e1ec3e..95f8e6392906b8 100644 --- a/src/mono/mono/utils/mono-threads-wasm.h +++ b/src/mono/mono/utils/mono-threads-wasm.h @@ -43,6 +43,12 @@ mono_threads_wasm_async_run_in_main_thread_vi (void (*func)(gpointer), gpointer void mono_threads_wasm_async_run_in_main_thread_vii (void (*func)(gpointer, gpointer), gpointer user_data1, gpointer user_data2); +void +mono_threads_wasm_async_run_in_target_thread (pthread_t target_thread, void (*func) (void)); + +void +mono_threads_wasm_async_run_in_target_thread_vi (pthread_t target_thread, void (*func) (gpointer), gpointer user_data); + static inline int32_t mono_wasm_atomic_wait_i32 (volatile int32_t *addr, int32_t expected, int32_t timeout_ns) @@ -59,6 +65,10 @@ mono_wasm_atomic_wait_i32 (volatile int32_t *addr, int32_t expected, int32_t tim // 2 == "timed-out", timeout expired before thread was woken up return __builtin_wasm_memory_atomic_wait32((int32_t*)addr, expected, timeout_ns); } + +extern MonoNativeTlsKey jobs_key; +#else /* DISABLE_THREADS */ +extern GSList *jobs; #endif /* DISABLE_THREADS */ // Called from register_thread when a pthread attaches to the runtime diff --git a/src/mono/mono/utils/mono-threads.c b/src/mono/mono/utils/mono-threads.c index 026c290fb3224d..b364d9467b1d52 100644 --- a/src/mono/mono/utils/mono-threads.c +++ b/src/mono/mono/utils/mono-threads.c @@ -515,7 +515,14 @@ register_thread (MonoThreadInfo *info) /* for wasm, the stack can be placed at the start of the linear memory */ #ifndef TARGET_WASM g_assert (staddr); -#endif +#endif /* TARGET_WASM */ + +#ifdef HOST_WASM +#ifndef DISABLE_THREADS + mono_native_tls_set_value (jobs_key, NULL); +#endif /* DISABLE_THREADS */ +#endif /* HOST_WASM */ + g_assert (stsize); info->stack_start_limit = staddr; info->stack_end = staddr + stsize; @@ -962,6 +969,12 @@ mono_thread_info_init (size_t info_size) mono_threads_suspend_policy_init (); +#ifdef HOST_WASM +#ifndef DISABLE_THREADS + res = mono_native_tls_alloc (&jobs_key, NULL); +#endif /* DISABLE_THREADS */ +#endif /* HOST_BROWSER */ + #ifdef HOST_WIN32 res = mono_native_tls_alloc (&thread_info_key, NULL); res = mono_native_tls_alloc (&thread_exited_key, NULL); diff --git a/src/mono/mono/utils/mono-threads.h b/src/mono/mono/utils/mono-threads.h index cdfc9f61336713..80d6f198a16b97 100644 --- a/src/mono/mono/utils/mono-threads.h +++ b/src/mono/mono/utils/mono-threads.h @@ -846,7 +846,8 @@ void mono_threads_join_unlock (void); #ifdef HOST_WASM typedef void (*background_job_cb)(void); -void mono_threads_schedule_background_job (background_job_cb cb); +void mono_main_thread_schedule_background_job (background_job_cb cb); +void mono_current_thread_schedule_background_job (background_job_cb cb); #endif #ifdef USE_WINDOWS_BACKEND diff --git a/src/mono/sample/wasm/browser-threads-minimal/Program.cs b/src/mono/sample/wasm/browser-threads-minimal/Program.cs index 4379c9092bf618..0e9e5584c7c9d9 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/Program.cs +++ b/src/mono/sample/wasm/browser-threads-minimal/Program.cs @@ -21,15 +21,21 @@ public static int Main(string[] args) [JSExport] public static async Task TestCanStartThread() { + Console.WriteLine($"smoke: TestCanStartThread 1 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var tcs = new TaskCompletionSource(); var t = new Thread(() => { + Console.WriteLine($"smoke: TestCanStartThread 2 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var childTid = Thread.CurrentThread.ManagedThreadId; tcs.SetResult(childTid); + Console.WriteLine($"smoke: TestCanStartThread 3 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); }); t.Start(); + Console.WriteLine($"smoke: TestCanStartThread 4 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var childTid = await tcs.Task; + Console.WriteLine($"smoke: TestCanStartThread 5 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); t.Join(); + Console.WriteLine($"smoke: TestCanStartThread 6 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); if (childTid == Thread.CurrentThread.ManagedThreadId) throw new Exception("Child thread ran on same thread as parent"); } @@ -51,24 +57,35 @@ public static async Task TestCallSetTimeoutOnWorker() const string fetchhelper = "./fetchelper.js"; [JSImport("responseText", fetchhelper)] - private static partial Task FetchHelperResponseText(JSObject response); + private static partial Task FetchHelperResponseText(JSObject response, int delayMs); [JSExport] public static async Task FetchBackground(string url) { + Console.WriteLine($"smoke: FetchBackground 1 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var t = Task.Run(async () => { - using var import = await JSHost.ImportAsync(fetchhelper, "./fetchhelper.js"); + Console.WriteLine($"smoke: FetchBackground 2 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); + var x=JSHost.ImportAsync(fetchhelper, "./fetchhelper.js"); + Console.WriteLine($"smoke: FetchBackground 3A ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); + using var import = await x; + Console.WriteLine($"smoke: FetchBackground 3B ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var r = await GlobalThisFetch(url); + Console.WriteLine($"smoke: FetchBackground 4 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var ok = (bool)r.GetPropertyAsBoolean("ok"); - Console.WriteLine($"XYZ: FetchBackground fetch returned to thread:{Thread.CurrentThread.ManagedThreadId}, ok: {ok}"); + Console.WriteLine($"smoke: FetchBackground 5 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); if (ok) { - var text = await FetchHelperResponseText(r); - Console.WriteLine($"XYZ: FetchBackground fetch returned to thread:{Thread.CurrentThread.ManagedThreadId}, text: {text}"); + #if DEBUG + var text = await FetchHelperResponseText(r, 5000); + #else + var text = await FetchHelperResponseText(r, 25000); + #endif + Console.WriteLine($"smoke: FetchBackground 6 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); return text; } + Console.WriteLine($"smoke: FetchBackground 7 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); return "not-ok"; }); var r = await t; diff --git a/src/mono/sample/wasm/browser-threads-minimal/fetchhelper.js b/src/mono/sample/wasm/browser-threads-minimal/fetchhelper.js index 928492378fc6c4..74b168a8a8bd72 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/fetchhelper.js +++ b/src/mono/sample/wasm/browser-threads-minimal/fetchhelper.js @@ -1,11 +1,11 @@ -function delay(timeoutMs) { - return new Promise(resolve => setTimeout(resolve, timeoutMs)); +export function delay(timeoutMs) { + return new Promise(resolve => setTimeout(resolve, timeoutMs)); } -export async function responseText(response) /* Promise */ { - console.log("artificially waiting for response for 25 seconds"); - await delay(25000); +export async function responseText(response, timeoutMs) /* Promise */ { + console.log(`artificially waiting for response for ${timeoutMs} ms`); + await delay(timeoutMs); console.log("artificial waiting done"); return await response.text(); } diff --git a/src/mono/sample/wasm/browser-threads-minimal/main.js b/src/mono/sample/wasm/browser-threads-minimal/main.js index 68b72278769097..b90abdbdf51044 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/main.js +++ b/src/mono/sample/wasm/browser-threads-minimal/main.js @@ -19,36 +19,36 @@ try { await exports.Sample.Test.TestCanStartThread(); console.log("smoke: TestCanStartThread done"); - console.log ("smoke: running TestCallSetTimeoutOnWorker"); + console.log("smoke: running TestCallSetTimeoutOnWorker"); await exports.Sample.Test.TestCallSetTimeoutOnWorker(); - console.log ("smoke: TestCallSetTimeoutOnWorker done"); + console.log("smoke: TestCallSetTimeoutOnWorker done"); - console.log ("smoke: running FetchBackground(blurst.txt)"); + console.log("smoke: running FetchBackground(blurst.txt)"); let s = await exports.Sample.Test.FetchBackground("./blurst.txt"); - console.log ("smoke: FetchBackground(blurst.txt) done"); + console.log("smoke: FetchBackground(blurst.txt) done"); if (!s.startsWith("It was the best of times, it was the blurst of times.")) { const msg = `Unexpected FetchBackground result ${s}`; document.getElementById("out").innerHTML = msg; - throw new Error (msg); + throw new Error(msg); } - console.log ("smoke: running FetchBackground(missing)"); + console.log("smoke: running FetchBackground(missing)"); s = await exports.Sample.Test.FetchBackground("./missing.txt"); - console.log ("smoke: FetchBackground(missing) done"); + console.log("smoke: FetchBackground(missing) done"); if (s !== "not-ok") { const msg = `Unexpected FetchBackground(missing) result ${s}`; document.getElementById("out").innerHTML = msg; - throw new Error (msg); + throw new Error(msg); } - console.log ("smoke: running TaskRunCompute"); + console.log("smoke: running TaskRunCompute"); const r1 = await exports.Sample.Test.RunBackgroundTaskRunCompute(); if (r1 !== 524) { const msg = `Unexpected result ${r1} from RunBackgorundTaskRunCompute()`; document.getElementById("out").innerHTML = msg; throw new Error(msg); } - console.log ("smoke: TaskRunCompute done"); + console.log("smoke: TaskRunCompute done"); let exit_code = await runMain(assemblyName, []); diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index d5c184ab6656b7..0968e482f52f85 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -104,6 +104,9 @@ run-build-tests: run-browser-tests-%: PATH="$(GECKODRIVER):$(CHROMEDRIVER):$(PATH)" XHARNESS_COMMAND="test-browser --browser=$(XHARNESS_BROWSER)" $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) +build-runtime-tests: + $(TOP)/src/tests/build.sh -mono os browser wasm $(CONFIG) + build-debugger-tests-helix: $(DOTNET) build -restore -bl:$(TOP)/artifacts/log/$(CONFIG)/Wasm.Debugger.Tests.binlog \ /p:ContinuousIntegrationBuild=true /p:ArchiveTests=true \ diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c index 1991b4c930c1d0..1a6494a101cf31 100644 --- a/src/mono/wasm/runtime/corebindings.c +++ b/src/mono/wasm/runtime/corebindings.c @@ -25,7 +25,8 @@ extern void mono_wasm_bind_cs_function(MonoString **fully_qualified_name, int si extern void mono_wasm_marshal_promise(void *data); typedef void (*background_job_cb)(void); -void mono_threads_schedule_background_job (background_job_cb cb); +void mono_main_thread_schedule_background_job (background_job_cb cb); +void mono_current_thread_schedule_background_job (background_job_cb cb); #ifndef DISABLE_LEGACY_JS_INTEROP extern void mono_wasm_invoke_js_with_args_ref (int js_handle, MonoString **method, MonoArray **args, int *is_exception, MonoObject **result); @@ -54,8 +55,6 @@ extern int mono_wasm_normalize_string(int32_t normalizationForm, MonoString **sr void bindings_initialize_internals (void) { - mono_add_internal_call ("System.Runtime.InteropServices.JavaScript.JSSynchronizationContext::ScheduleBackgroundJob", mono_threads_schedule_background_job); - mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object); mono_add_internal_call ("Interop/Runtime::BindJSFunction", mono_wasm_bind_js_function); mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_bound_function); diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index 715a3936cec07e..f18a313504155c 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -42,7 +42,7 @@ const fn_signatures: SigLine[] = [ [true, "mono_wasm_parse_runtime_options", null, ["number", "number"]], [true, "mono_wasm_strdup", "number", ["string"]], [true, "mono_background_exec", null, []], - [true, "mono_set_timeout_exec", null, []], + [true, "mono_wasm_execute_timer", null, []], [true, "mono_wasm_load_icu_data", "number", ["number"]], [false, "mono_wasm_add_assembly", "number", ["string", "number", "number"]], [true, "mono_wasm_add_satellite_assembly", "void", ["string", "string", "number", "number"]], @@ -162,7 +162,7 @@ export interface t_Cwraps { mono_wasm_strdup(value: string): number; mono_wasm_parse_runtime_options(length: number, argv: VoidPtr): void; mono_background_exec(): void; - mono_set_timeout_exec(): void; + mono_wasm_execute_timer(): void; mono_wasm_load_icu_data(offset: VoidPtr): number; mono_wasm_add_assembly(name: string, data: VoidPtr, size: number): number; mono_wasm_add_satellite_assembly(name: string, culture: string, data: VoidPtr, size: number): void; diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index 23f27df9485603..9a525a57e24507 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -70,7 +70,7 @@ const DotnetSupportLib = { // --- keep in sync with exports.ts --- let linked_functions = [ // mini-wasm.c - "mono_set_timeout", + "mono_wasm_schedule_timer", // mini-wasm-debugger.c "mono_wasm_asm_loaded", diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts index 0aa96cc9cdecdd..63d1e98a8d359a 100644 --- a/src/mono/wasm/runtime/exports-linker.ts +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -13,7 +13,7 @@ import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_c import { mono_wasm_marshal_promise } from "./marshal-to-js"; import { mono_wasm_eventloop_has_unsettled_interop_promises } from "./pthreads/shared/eventloop"; import { mono_wasm_pthread_on_pthread_attached } from "./pthreads/worker"; -import { mono_set_timeout, schedule_background_exec } from "./scheduling"; +import { mono_wasm_schedule_timer, schedule_background_exec } from "./scheduling"; import { mono_wasm_asm_loaded } from "./startup"; import { mono_wasm_diagnostic_server_on_server_thread_created } from "./diagnostics/server_pthread"; import { mono_wasm_diagnostic_server_on_runtime_server_init, mono_wasm_event_pipe_early_startup_callback } from "./diagnostics"; @@ -64,7 +64,7 @@ const mono_wasm_legacy_interop_exports = !WasmEnableLegacyJsInterop ? undefined export function export_linker(): any { return { // mini-wasm.c - mono_set_timeout, + mono_wasm_schedule_timer, // mini-wasm-debugger.c mono_wasm_asm_loaded, diff --git a/src/mono/wasm/runtime/invoke-cs.ts b/src/mono/wasm/runtime/invoke-cs.ts index bae3872e35b456..fa9099d73bf4b9 100644 --- a/src/mono/wasm/runtime/invoke-cs.ts +++ b/src/mono/wasm/runtime/invoke-cs.ts @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import BuildConfiguration from "consts:configuration"; + import MonoWasmThreads from "consts:monoWasmThreads"; import { Module, runtimeHelpers } from "./globals"; import { bind_arg_marshal_to_cs } from "./marshal-to-cs"; @@ -10,7 +12,7 @@ import { bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type, } from "./marshal"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots"; -import { conv_string, conv_string_root } from "./strings"; +import { conv_string_root, string_decoder } from "./strings"; import { MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod, JSMarshalerArguments, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, VoidPtrNull, MonoObjectRefNull, MonoObjectNull } from "./types/internal"; import { Int32Ptr } from "./types/emscripten"; import cwraps from "./cwraps"; @@ -18,8 +20,10 @@ import { assembly_load } from "./class-loader"; import { wrap_error_root, wrap_no_error_root } from "./invoke-js"; import { startMeasure, MeasuredBlock, endMeasure } from "./profiler"; import { mono_log_debug } from "./logging"; +import { assert_synchronization_context } from "./pthreads/shared"; export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, signature_hash: number, signature: JSFunctionSignature, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_synchronization_context(); const fqn_root = mono_wasm_new_external_root(fully_qualified_name), resultRoot = mono_wasm_new_external_root(result_address); const mark = startMeasure(); try { @@ -84,6 +88,13 @@ export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, bound_fn = bind_fn(closure); } + // this is just to make debugging easier. + // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds + // in Release configuration, it would be a trimmed by rollup + if (BuildConfiguration === "Debug") { + bound_fn = new Function("fn", "return (function JSExport_" + methodname + "(){ return fn.apply(this, arguments)});")(bound_fn); + } + (bound_fn)[bound_cs_function_symbol] = true; _walk_exports_to_set_function(assembly, namespace, classname, methodname, signature_hash, bound_fn); @@ -233,8 +244,9 @@ type BindingClosure = { } export function invoke_method_and_handle_exception(method: MonoMethod, args: JSMarshalerArguments): void { + assert_synchronization_context(); const fail = cwraps.mono_wasm_invoke_method_bound(method, args); - if (fail) throw new Error("ERR24: Unexpected error: " + conv_string(fail)); + if (fail) throw new Error("ERR24: Unexpected error: " + string_decoder.copy(fail)); if (is_args_exception(args)) { const exc = get_arg(args, 0); throw marshal_exception_to_js(exc); diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts index ee1220b8d9ab35..a1fc9ae6e42a90 100644 --- a/src/mono/wasm/runtime/invoke-js.ts +++ b/src/mono/wasm/runtime/invoke-js.ts @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import BuildConfiguration from "consts:configuration"; + import { marshal_exception_to_cs, bind_arg_marshal_to_cs } from "./marshal-to-cs"; import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol } from "./marshal"; import { setI32_unchecked } from "./memory"; @@ -14,10 +16,12 @@ import { mono_log_debug, mono_wasm_symbolicate_string } from "./logging"; import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles"; import { endMeasure, MeasuredBlock, startMeasure } from "./profiler"; import { wrap_as_cancelable_promise } from "./cancelable-promise"; +import { assert_synchronization_context } from "./pthreads/shared"; const fn_wrapper_by_fn_handle: Function[] = [null];// 0th slot is dummy, we never free bound functions export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_name: MonoStringRef, signature: JSFunctionSignature, function_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_synchronization_context(); const function_name_root = mono_wasm_new_external_root(function_name), module_name_root = mono_wasm_new_external_root(module_name), resultRoot = mono_wasm_new_external_root(result_address); @@ -28,7 +32,7 @@ export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_ const js_function_name = conv_string_root(function_name_root)!; const mark = startMeasure(); const js_module_name = conv_string_root(module_name_root)!; - mono_log_debug(`Binding [JSImport] ${js_function_name} from ${js_module_name}`); + mono_log_debug(`Binding [JSImport] ${js_function_name} from ${js_module_name} module`); const fn = mono_wasm_lookup_function(js_function_name, js_module_name); const args_count = get_signature_argument_count(signature); @@ -81,6 +85,13 @@ export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_ bound_fn = bind_fn(closure); } + // this is just to make debugging easier. + // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds + // in Release configuration, it would be a trimmed by rollup + if (BuildConfiguration === "Debug") { + bound_fn = new Function("fn", "return (function JSImport_" + js_function_name.replaceAll(".", "_") + "(){ return fn.apply(this, arguments)});")(bound_fn); + } + (bound_fn)[imported_js_function_symbol] = true; const fn_handle = fn_wrapper_by_fn_handle.length; fn_wrapper_by_fn_handle.push(bound_fn); @@ -315,6 +326,7 @@ export const importedModules: Map> = new Map(); export function dynamic_import(module_name: string, module_url: string): Promise { mono_assert(module_name, "Invalid module_name"); mono_assert(module_url, "Invalid module_name"); + assert_synchronization_context(); let promise = importedModulesPromises.get(module_name); const newPromise = !promise; if (newPromise) { diff --git a/src/mono/wasm/runtime/jiterpreter-jit-call.ts b/src/mono/wasm/runtime/jiterpreter-jit-call.ts index b5a487ebf8c581..e3e2f56d8e6478 100644 --- a/src/mono/wasm/runtime/jiterpreter-jit-call.ts +++ b/src/mono/wasm/runtime/jiterpreter-jit-call.ts @@ -98,6 +98,8 @@ class TrampolineInfo { method: MonoMethod, rmethod: VoidPtr, cinfo: VoidPtr, arg_offsets: VoidPtr, catch_exceptions: boolean ) { + mono_assert(arg_offsets, "Expected nonzero arg_offsets pointer"); + this.method = method; this.rmethod = rmethod; this.catchExceptions = catch_exceptions; diff --git a/src/mono/wasm/runtime/loader/worker.ts b/src/mono/wasm/runtime/loader/worker.ts index a46e7075caea5f..2f2024140d8f49 100644 --- a/src/mono/wasm/runtime/loader/worker.ts +++ b/src/mono/wasm/runtime/loader/worker.ts @@ -1,6 +1,6 @@ import { MonoConfig } from "../types"; import { MonoConfigInternal } from "../types/internal"; -import { deep_merge_config } from "./config"; +import { deep_merge_config, normalizeConfig } from "./config"; import { ENVIRONMENT_IS_WEB, loaderHelpers } from "./globals"; import { mono_log_debug } from "./logging"; @@ -30,6 +30,7 @@ function onMonoConfigReceived(config: MonoConfigInternal): void { } deep_merge_config(loaderHelpers.config, config); + normalizeConfig(); mono_log_debug("mono config received"); workerMonoConfigReceived = true; loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config); diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts index bc54e51b85bddc..0dbe70b4106084 100644 --- a/src/mono/wasm/runtime/managed-exports.ts +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -1,9 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import MonoWasmThreads from "consts:monoWasmThreads"; + import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; import cwraps from "./cwraps"; -import { runtimeHelpers, ENVIRONMENT_IS_PTHREAD, Module } from "./globals"; +import { runtimeHelpers, Module } from "./globals"; import { alloc_stack_frame, get_arg, get_arg_gc_handle, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_method_and_handle_exception } from "./invoke-cs"; import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; @@ -21,8 +23,8 @@ export function init_managed_exports(): void { if (!runtimeHelpers.runtime_interop_exports_class) throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class"; - const install_sync_context = cwraps.mono_wasm_assembly_find_method(runtimeHelpers.runtime_interop_exports_class, "InstallSynchronizationContext", -1); - // mono_assert(install_sync_context, "Can't find InstallSynchronizationContext method"); + const install_sync_context = MonoWasmThreads ? get_method("InstallSynchronizationContext") : undefined; + mono_assert(!MonoWasmThreads || install_sync_context, "Can't find InstallSynchronizationContext method"); const call_entry_point = get_method("CallEntrypoint"); mono_assert(call_entry_point, "Can't find CallEntrypoint method"); const release_js_owned_object_by_gc_handle_method = get_method("ReleaseJSOwnedObjectByGCHandle"); @@ -153,7 +155,7 @@ export function init_managed_exports(): void { } }; - if (install_sync_context) { + if (MonoWasmThreads && install_sync_context) { runtimeHelpers.javaScriptExports.install_synchronization_context = () => { const sp = Module.stackSave(); try { @@ -163,10 +165,6 @@ export function init_managed_exports(): void { Module.stackRestore(sp); } }; - - if (!ENVIRONMENT_IS_PTHREAD) - // Install our sync context so that async continuations will migrate back to this thread (the main thread) automatically - runtimeHelpers.javaScriptExports.install_synchronization_context(); } } diff --git a/src/mono/wasm/runtime/net6-legacy/cs-to-js.ts b/src/mono/wasm/runtime/net6-legacy/cs-to-js.ts index 34730933952aa7..28a1294df9b7e7 100644 --- a/src/mono/wasm/runtime/net6-legacy/cs-to-js.ts +++ b/src/mono/wasm/runtime/net6-legacy/cs-to-js.ts @@ -10,17 +10,20 @@ import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; import { ManagedObject } from "../marshal"; import { getU32, getI32, getF32, getF64, setI32_unchecked } from "../memory"; import { mono_wasm_new_root, mono_wasm_new_external_root } from "../roots"; -import { conv_string_root } from "../strings"; +import { conv_string_root, string_decoder } from "../strings"; import { legacyManagedExports } from "./corebindings"; import { legacyHelpers } from "./globals"; import { js_to_mono_obj_root } from "./js-to-cs"; import { mono_bind_method, mono_method_get_call_signature_ref } from "./method-binding"; import { createPromiseController } from "../globals"; +import { assert_legacy_interop } from "../pthreads/shared"; const delegate_invoke_symbol = Symbol.for("wasm delegate_invoke"); // this is only used from Blazor export function unbox_mono_obj(mono_obj: MonoObject): any { + assert_legacy_interop(); + if (mono_obj === MonoObjectNull) return undefined; @@ -129,6 +132,7 @@ export function unbox_mono_obj_root(root: WasmRoot): any { } export function mono_array_to_js_array(mono_array: MonoArray): any[] | null { + assert_legacy_interop(); if (mono_array === MonoArrayNull) return null; @@ -341,3 +345,11 @@ export function get_js_owned_object_by_gc_handle_ref(gc_handle: GCHandle, result // this is always strong gc_handle legacyManagedExports._get_js_owned_object_by_gc_handle_ref(gc_handle, result); } + +/** + * @deprecated Not GC or thread safe + */ +export function conv_string(mono_obj: MonoString): string | null { + assert_legacy_interop(); + return string_decoder.copy(mono_obj); +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts b/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts index d73cc4e060bdc5..9be5f370399c19 100644 --- a/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts +++ b/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts @@ -8,8 +8,8 @@ import { mono_wasm_load_bytes_into_heap, setB32, setI8, setI16, setI32, setI52, import { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_new_external_root, mono_wasm_release_roots } from "../roots"; import { mono_run_main, mono_run_main_and_exit } from "../run"; import { mono_wasm_setenv } from "../startup"; -import { js_string_to_mono_string, conv_string, js_string_to_mono_string_root, conv_string_root } from "../strings"; -import { mono_array_to_js_array, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array } from "./cs-to-js"; +import { js_string_to_mono_string, js_string_to_mono_string_root, conv_string_root } from "../strings"; +import { mono_array_to_js_array, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array, conv_string } from "./cs-to-js"; import { js_typed_array_to_array, js_to_mono_obj, js_typed_array_to_array_root, js_to_mono_obj_root } from "./js-to-cs"; import { mono_bind_static_method, mono_call_assembly_entry_point } from "./method-calls"; import { mono_wasm_load_runtime } from "../startup"; diff --git a/src/mono/wasm/runtime/net6-legacy/js-to-cs.ts b/src/mono/wasm/runtime/net6-legacy/js-to-cs.ts index 945640afba8a71..290dd4f68b08ce 100644 --- a/src/mono/wasm/runtime/net6-legacy/js-to-cs.ts +++ b/src/mono/wasm/runtime/net6-legacy/js-to-cs.ts @@ -15,6 +15,7 @@ import { has_backing_array_buffer } from "./buffers"; import { legacyManagedExports } from "./corebindings"; import { get_js_owned_object_by_gc_handle_ref } from "./cs-to-js"; import { legacyHelpers, wasm_type_symbol } from "./globals"; +import { assert_legacy_interop } from "../pthreads/shared"; export function _js_to_mono_uri_root(should_add_in_flight: boolean, js_obj: any, result: WasmRoot): void { switch (true) { @@ -37,6 +38,7 @@ export function _js_to_mono_uri_root(should_add_in_flight: boolean, js_obj: any, * @deprecated Not GC or thread safe. For blazor use only */ export function js_to_mono_obj(js_obj: any): MonoObject { + assert_legacy_interop(); const temp = mono_wasm_new_root(); try { js_to_mono_obj_root(js_obj, temp, false); @@ -60,6 +62,8 @@ export function _js_to_mono_obj_unsafe(should_add_in_flight: boolean, js_obj: an } export function js_to_mono_obj_root(js_obj: any, result: WasmRoot, should_add_in_flight: boolean): void { + assert_legacy_interop(); + if (is_nullish(result)) throw new Error("Expected (value, WasmRoot, boolean)"); diff --git a/src/mono/wasm/runtime/net6-legacy/method-binding.ts b/src/mono/wasm/runtime/net6-legacy/method-binding.ts index 8434bcfe6dc9bc..d75ca3ce5feb77 100644 --- a/src/mono/wasm/runtime/net6-legacy/method-binding.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-binding.ts @@ -1,14 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import MonoWasmThreads from "consts:monoWasmThreads"; import { legacy_c_functions as cwraps } from "../cwraps"; -import { ENVIRONMENT_IS_PTHREAD, Module } from "../globals"; +import { Module } from "../globals"; import { parseFQN } from "../invoke-cs"; import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; import { js_string_to_mono_string_root, js_string_to_mono_string_interned_root, conv_string_root } from "../strings"; -import { MonoMethod, MonoObject, MonoType, MonoClass, VoidPtrNull, MarshalType, MonoString, MonoObjectNull, WasmRootBuffer, WasmRoot } from "../types/internal"; +import { MonoMethod, MonoObject, VoidPtrNull, MarshalType, MonoString, MonoObjectNull, WasmRootBuffer, WasmRoot } from "../types/internal"; import { VoidPtr } from "../types/emscripten"; import { legacyManagedExports } from "./corebindings"; import { get_js_owned_object_by_gc_handle_ref, _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js"; @@ -16,6 +15,7 @@ import { legacyHelpers } from "./globals"; import { js_to_mono_obj_root, _js_to_mono_uri_root, js_to_mono_enum } from "./js-to-cs"; import { _teardown_after_call } from "./method-calls"; import { mono_log_warn } from "../logging"; +import { assert_legacy_interop } from "../pthreads/shared"; const escapeRE = /[^A-Za-z0-9_$]/g; @@ -23,25 +23,7 @@ const primitiveConverters = new Map(); const _signature_converters = new Map(); const boundMethodsByMethod: Map = new Map(); -export function _get_type_name(typePtr: MonoType): string { - if (!typePtr) - return ""; - return cwraps.mono_wasm_get_type_name(typePtr); -} - -export function _get_type_aqn(typePtr: MonoType): string { - if (!typePtr) - return ""; - return cwraps.mono_wasm_get_type_aqn(typePtr); -} - -export function _get_class_name(classPtr: MonoClass): string { - if (!classPtr) - return ""; - return cwraps.mono_wasm_get_type_name(cwraps.mono_wasm_class_get_type(classPtr)); -} - -export function _create_named_function(name: string, argumentNames: string[], body: string, closure: any): Function { +function _create_named_function(name: string, argumentNames: string[], body: string, closure: any): Function { let result = null; let closureArgumentList: any[] | null = null; let closureArgumentNames = null; @@ -60,7 +42,7 @@ export function _create_named_function(name: string, argumentNames: string[], bo return result; } -export function _create_rebindable_named_function(name: string, argumentNames: string[], body: string, closureArgNames: string[] | null): Function { +function _create_rebindable_named_function(name: string, argumentNames: string[], body: string, closureArgNames: string[] | null): Function { const strictPrefix = "\"use strict\";\r\n"; let uriPrefix = "", escapedFunctionIdentifier = ""; @@ -176,7 +158,7 @@ function _get_converter_for_marshal_string(args_marshal: string/*ArgsMarshalStri return converter; } -export function _compile_converter_for_marshal_string(args_marshal: string/*ArgsMarshalString*/): Converter { +function _compile_converter_for_marshal_string(args_marshal: string/*ArgsMarshalString*/): Converter { const converter = _get_converter_for_marshal_string(args_marshal); if (typeof (converter.args_marshal) !== "string") throw new Error("Corrupt converter for '" + args_marshal + "'"); @@ -383,9 +365,7 @@ export function _decide_if_result_is_marshaled(converter: Converter, argc: numbe export function mono_bind_method(method: MonoMethod, args_marshal: string/*ArgsMarshalString*/, has_this_arg: boolean, friendly_name?: string): Function { - if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) { - throw new Error("Legacy interop is not supported with WebAssembly threads."); - } + assert_legacy_interop(); if (typeof (args_marshal) !== "string") throw new Error("args_marshal argument invalid, expected string"); diff --git a/src/mono/wasm/runtime/net6-legacy/method-calls.ts b/src/mono/wasm/runtime/net6-legacy/method-calls.ts index 235e45c8641cd2..61388647a837ba 100644 --- a/src/mono/wasm/runtime/net6-legacy/method-calls.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-calls.ts @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import MonoWasmThreads from "consts:monoWasmThreads"; import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; -import { Module, runtimeHelpers, INTERNAL, ENVIRONMENT_IS_PTHREAD } from "../globals"; +import { Module, runtimeHelpers, INTERNAL } from "../globals"; import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; import { _release_temp_frame } from "../memory"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; @@ -14,6 +13,7 @@ import { Int32Ptr, VoidPtr } from "../types/emscripten"; import { mono_array_root_to_js_array, unbox_mono_obj_root } from "./cs-to-js"; import { js_array_to_mono_array, js_to_mono_obj_root } from "./js-to-cs"; import { Converter, BoundMethodToken, mono_method_resolve, mono_method_get_call_signature_ref, mono_bind_method } from "./method-binding"; +import { assert_legacy_interop } from "../pthreads/shared"; const boundMethodsByFqn: Map = new Map(); @@ -53,6 +53,7 @@ export function _teardown_after_call( export function mono_bind_static_method(fqn: string, signature?: string/*ArgsMarshalString*/): Function { mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "The runtime must be initialized."); + assert_legacy_interop(); const key = `${fqn}-${signature}`; let js_method = boundMethodsByFqn.get(key); @@ -69,6 +70,7 @@ export function mono_bind_static_method(fqn: string, signature?: string/*ArgsMar } export function mono_bind_assembly_entry_point(assembly: string, signature?: string/*ArgsMarshalString*/): Function { + assert_legacy_interop(); const method = find_entry_point(assembly); if (typeof (signature) !== "string") signature = mono_method_get_call_signature_ref(method, undefined); @@ -84,6 +86,7 @@ export function mono_bind_assembly_entry_point(assembly: string, signature?: str export function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: string/*ArgsMarshalString*/): number { mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "The runtime must be initialized."); + assert_legacy_interop(); if (!args) { args = [[]]; } @@ -91,9 +94,7 @@ export function mono_call_assembly_entry_point(assembly: string, args?: any[], s } export function mono_wasm_invoke_js_with_args_ref(js_handle: JSHandle, method_name: MonoStringRef, args: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): any { - if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) { - throw new Error("Legacy interop is not supported with WebAssembly threads."); - } + assert_legacy_interop(); const argsRoot = mono_wasm_new_external_root(args), nameRoot = mono_wasm_new_external_root(method_name), resultRoot = mono_wasm_new_external_root(result_address); @@ -131,6 +132,7 @@ export function mono_wasm_invoke_js_with_args_ref(js_handle: JSHandle, method_na } export function mono_wasm_get_object_property_ref(js_handle: JSHandle, property_name: MonoStringRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_legacy_interop(); const nameRoot = mono_wasm_new_external_root(property_name), resultRoot = mono_wasm_new_external_root(result_address); try { @@ -158,6 +160,7 @@ export function mono_wasm_get_object_property_ref(js_handle: JSHandle, property_ } export function mono_wasm_set_object_property_ref(js_handle: JSHandle, property_name: MonoStringRef, value: MonoObjectRef, createIfNotExist: boolean, hasOwnProperty: boolean, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_legacy_interop(); const valueRoot = mono_wasm_new_external_root(value), nameRoot = mono_wasm_new_external_root(property_name), resultRoot = mono_wasm_new_external_root(result_address); @@ -206,6 +209,7 @@ export function mono_wasm_set_object_property_ref(js_handle: JSHandle, property_ } export function mono_wasm_get_by_index_ref(js_handle: JSHandle, property_index: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_legacy_interop(); const resultRoot = mono_wasm_new_external_root(result_address); try { const obj = mono_wasm_get_jsobj_from_js_handle(js_handle); @@ -225,6 +229,7 @@ export function mono_wasm_get_by_index_ref(js_handle: JSHandle, property_index: } export function mono_wasm_set_by_index_ref(js_handle: JSHandle, property_index: number, value: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_legacy_interop(); const valueRoot = mono_wasm_new_external_root(value), resultRoot = mono_wasm_new_external_root(result_address); try { @@ -246,6 +251,7 @@ export function mono_wasm_set_by_index_ref(js_handle: JSHandle, property_index: } export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { + assert_legacy_interop(); const nameRoot = mono_wasm_new_external_root(global_name), resultRoot = mono_wasm_new_external_root(result_address); try { @@ -285,9 +291,7 @@ export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_e // Blazor specific custom routine export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo: any, arg0: any, arg1: any, arg2: any): void | number { try { - if (MonoWasmThreads) { - throw new Error("Legacy interop is not supported with WebAssembly threads."); - } + assert_legacy_interop(); const blazorExports = (globalThis).Blazor; if (!blazorExports) { throw new Error("The blazor.webassembly.js library is not loaded."); diff --git a/src/mono/wasm/runtime/pthreads/shared/index.ts b/src/mono/wasm/runtime/pthreads/shared/index.ts index 430fefff34c415..6fa9789bd68654 100644 --- a/src/mono/wasm/runtime/pthreads/shared/index.ts +++ b/src/mono/wasm/runtime/pthreads/shared/index.ts @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { Module } from "../../globals"; +import MonoWasmThreads from "consts:monoWasmThreads"; + +import { ENVIRONMENT_IS_PTHREAD, Module, runtimeHelpers } from "../../globals"; import { MonoConfig } from "../../types"; import { pthreadPtr } from "./types"; @@ -128,3 +130,24 @@ export function isMonoWorkerMessagePreload(message: MonoWorkerMessage 0) { - --pump_count; - cwraps.mono_background_exec(); - } -} - export function prevent_timer_throttling(): void { if (!isChromium) { return; @@ -38,27 +31,31 @@ export function prevent_timer_throttling(): void { for (let schedule = next_reach_time; schedule < desired_reach_time; schedule += light_throttling_frequency) { const delay = schedule - now; setTimeout(() => { - cwraps.mono_set_timeout_exec(); + cwraps.mono_wasm_execute_timer(); pump_count++; - pump_message(); + mono_background_exec_until_done(); }, delay); } spread_timers_maximum = desired_reach_time; } +function mono_background_exec_until_done() { + while (pump_count > 0) { + --pump_count; + cwraps.mono_background_exec(); + } +} + export function schedule_background_exec(): void { ++pump_count; - setTimeout(pump_message, 0); + setTimeout(mono_background_exec_until_done, 0); } let lastScheduledTimeoutId: any = undefined; -export function mono_set_timeout(timeout: number): void { - function mono_wasm_set_timeout_exec() { - cwraps.mono_set_timeout_exec(); - } +export function mono_wasm_schedule_timer(shortestDueTimeMs: number): void { if (lastScheduledTimeoutId) { clearTimeout(lastScheduledTimeoutId); lastScheduledTimeoutId = undefined; } - lastScheduledTimeoutId = setTimeout(mono_wasm_set_timeout_exec, timeout); + lastScheduledTimeoutId = setTimeout(cwraps.mono_wasm_execute_timer, shortestDueTimeMs); } diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 5a3c395afd61ca..f0bf309b32fd83 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import WasmEnableLegacyJsInterop from "consts:WasmEnableLegacyJsInterop"; import { DotnetModuleInternal, CharPtrNull } from "./types/internal"; -import { disableLegacyJsInterop, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers } from "./globals"; +import { disableLegacyJsInterop, ENVIRONMENT_IS_PTHREAD, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers } from "./globals"; import cwraps, { init_c_exports } from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; import { toBase64StringImpl } from "./base64"; @@ -30,6 +30,7 @@ import { init_legacy_exports } from "./net6-legacy/corebindings"; import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy"; import { BINDING, MONO } from "./net6-legacy/globals"; import { mono_log_debug, mono_log_warn } from "./logging"; +import { install_synchronization_context } from "./pthreads/shared"; // default size if MonoConfig.pthreadPoolSize is undefined @@ -585,9 +586,12 @@ export function bindings_init(): void { try { const mark = startMeasure(); init_managed_exports(); - if (WasmEnableLegacyJsInterop && !disableLegacyJsInterop) { + if (WasmEnableLegacyJsInterop && !disableLegacyJsInterop && !ENVIRONMENT_IS_PTHREAD) { init_legacy_exports(); } + if (MonoWasmThreads && !ENVIRONMENT_IS_PTHREAD) { + install_synchronization_context(); + } initialize_marshalers_to_js(); initialize_marshalers_to_cs(); runtimeHelpers._i52_error_scratch_buffer = Module._malloc(4); diff --git a/src/mono/wasm/runtime/strings.ts b/src/mono/wasm/runtime/strings.ts index 52f6c8dbbe3732..3fd4e012d7de9a 100644 --- a/src/mono/wasm/runtime/strings.ts +++ b/src/mono/wasm/runtime/strings.ts @@ -8,6 +8,7 @@ import cwraps from "./cwraps"; import { mono_wasm_new_root } from "./roots"; import { getI32, getU32 } from "./memory"; import { NativePointer, CharPtr } from "./types/emscripten"; +import { assert_legacy_interop } from "./pthreads/shared"; export class StringDecoder { @@ -102,13 +103,6 @@ let _interned_string_current_root_buffer_count = 0; export const string_decoder = new StringDecoder(); export const mono_wasm_empty_string = ""; -/** - * @deprecated Not GC or thread safe - */ -export function conv_string(mono_obj: MonoString): string | null { - return string_decoder.copy(mono_obj); -} - export function conv_string_root(root: WasmRoot): string | null { return string_decoder.copy_root(root); } @@ -255,6 +249,7 @@ export function js_string_to_mono_string_interned(string: string | symbol): Mono * @deprecated Not GC or thread safe */ export function js_string_to_mono_string(string: string): MonoString { + assert_legacy_interop(); const temp = mono_wasm_new_root(); try { js_string_to_mono_string_root(string, temp); @@ -268,6 +263,7 @@ export function js_string_to_mono_string(string: string): MonoString { * @deprecated Not GC or thread safe */ export function js_string_to_mono_string_new(string: string): MonoString { + assert_legacy_interop(); const temp = mono_wasm_new_root(); try { js_string_to_mono_string_new_root(string, temp); diff --git a/src/mono/wasm/threads.md b/src/mono/wasm/threads.md index ef2152865c0316..697c8c2dc4bb3d 100644 --- a/src/mono/wasm/threads.md +++ b/src/mono/wasm/threads.md @@ -79,10 +79,10 @@ Mono exposes these functions as `mono_threads_wasm_async_run_in_main_thread`, et ## Background tasks ## -The runtime has a number of tasks that are scheduled with `mono_threads_schedule_background_job` +The runtime has a number of tasks that are scheduled with `mono_main_thread_schedule_background_job` (pumping the threadpool task queue, running GC finalizers, etc). -The background tasks will run on the main thread. Calling `mono_threads_schedule_background_job` on +The background tasks will run on the main thread. Calling `mono_main_thread_schedule_background_job` on a worker thread will use `async_run_in_main_thread` to queue up work for the main thread. ## Debugger tests ## diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 921919adbf3408..5ecdc88055453b 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -256,6 +256,9 @@ + + + <_EmccExportedLibraryFunction>"[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]" <_EmccExportedRuntimeMethods>"[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" diff --git a/src/tests/Common/helixpublishwitharcade.proj b/src/tests/Common/helixpublishwitharcade.proj index bd6f60135829e1..f8245790276e1b 100644 --- a/src/tests/Common/helixpublishwitharcade.proj +++ b/src/tests/Common/helixpublishwitharcade.proj @@ -361,7 +361,8 @@ <_MergedWrapperMarker Include="$(TestBinDir)**\*.MergedTestAssembly" Exclude="$(TestBinDir)**\supportFiles\*.MergedTestAssembly" /> <_MergedWrapperMarker Update="@(_MergedWrapperMarker)"> - $([System.IO.Path]::ChangeExtension('%(Identity)', '.$(TestScriptExtension)')) + $([System.IO.Path]::ChangeExtension('%(Identity)', '.$(TestScriptExtension)')) + %(RootDir)%(Directory)AppBundle/RunTests.$(TestScriptExtension) diff --git a/src/tests/JIT/opt/Compares/conditionalIncrements.cs b/src/tests/JIT/opt/Compares/conditionalIncrements.cs index 2580fa3d5ca5cc..8cf201fa1a926d 100644 --- a/src/tests/JIT/opt/Compares/conditionalIncrements.cs +++ b/src/tests/JIT/opt/Compares/conditionalIncrements.cs @@ -10,193 +10,148 @@ public class ConditionalIncrementTest { + [Theory] + [InlineData(72, 6)] + [InlineData(32, 5)] [MethodImpl(MethodImplOptions.NoInlining)] - static int cinc_byte(byte op1) + public static void cinc_byte(byte op1, int expected) { //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #42 //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} - return op1 > 42 ? 6: 5; + int result = op1 > 42 ? 6: 5; + Assert.Equal(expected, result); } + [Theory] + [InlineData(72, byte.MinValue)] + [InlineData(32, byte.MaxValue)] [MethodImpl(MethodImplOptions.NoInlining)] - static byte cinc_byte_min_max(byte op1) + public static void cinc_byte_min_max(byte op1, byte expected) { //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #43 //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, wzr, {{w[0-9]+}}, {{ge|lt}} - return op1 >= 43 ? byte.MinValue : byte.MaxValue; + byte result = op1 >= 43 ? byte.MinValue : byte.MaxValue; + Assert.Equal(expected, result); } + [Theory] + [InlineData(72, byte.MinValue)] + [InlineData(32, byte.MaxValue)] [MethodImpl(MethodImplOptions.NoInlining)] - static int cinc_short(short op1) + public static void cinv_byte_min_max(byte op1, byte expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #43 + //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{ge|lt}} + byte result = (byte) (op1 >= 43 ? byte.MinValue : ~byte.MinValue); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(74, 5)] + [InlineData(34, 6)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinc_short(short op1, short expected) { //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #44 //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} - return op1 <= 44 ? 6 : 5; + short result = (short) (op1 <= 44 ? 6 : 5); + Assert.Equal(expected, result); } - + [Theory] + [InlineData(76, short.MinValue)] + [InlineData(-35, short.MaxValue)] [MethodImpl(MethodImplOptions.NoInlining)] - static short cinc_short_min_max(short op1) + public static void cinc_short_min_max(short op1, short expected) { //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #45 //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} - return op1 > 45 ? short.MinValue : short.MaxValue; + short result = op1 > 45 ? short.MinValue : short.MaxValue; + Assert.Equal(expected, result); } + [Theory] + [InlineData(76, 6)] + [InlineData(36, 5)] [MethodImpl(MethodImplOptions.NoInlining)] - static int cinc_int(int op1) + public static void cinc_int(int op1, int expected) { //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #46 //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} - return op1 > 46 ? 6 : 5; + int result = op1 > 46 ? 6 : 5; + Assert.Equal(expected, result); } + [Theory] + [InlineData(77, int.MinValue)] + [InlineData(37, int.MaxValue)] [MethodImpl(MethodImplOptions.NoInlining)] - static int cinc_int_min_max(int op1) + public static void cinc_int_min_max(int op1, int expected) { //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #47 //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{ge|lt}} - return op1 >= 47 ? int.MinValue : int.MaxValue; + int result = op1 >= 47 ? int.MinValue : int.MaxValue; + Assert.Equal(expected, result); } + [Theory] + [InlineData(78, 5)] + [InlineData(38, 6)] [MethodImpl(MethodImplOptions.NoInlining)] - static long cinc_long(long op1) + public static void cinc_long(long op1, long expected) { //ARM64-FULL-LINE: cmp {{x[0-9]+}}, #48 //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{ge|lt}} //ARM64-FULL-LINE-NEXT: sxtw {{x[0-9]+}}, {{w[0-9]+}} - return op1 < 48 ? 6 : 5; + long result = op1 < 48 ? 6 : 5; + Assert.Equal(expected, result); } + [Theory] + [InlineData(79, long.MaxValue)] + [InlineData(39, long.MinValue)] [MethodImpl(MethodImplOptions.NoInlining)] - static long cinc_long_min_max(long op1) + public static void cinc_long_min_max(long op1, long expected) { //ARM64-FULL-LINE: cmp {{x[0-9]+}}, #49 //ARM64-FULL-LINE-NEXT: cinc {{x[0-9]+}}, {{x[0-9]+}}, {{ge|lt}} - return op1 < 49 ? long.MinValue : long.MaxValue; + long result = op1 < 49 ? long.MinValue : long.MaxValue; + Assert.Equal(expected, result); } + [Theory] + [InlineData(79, long.MaxValue)] + [InlineData(39, long.MinValue)] [MethodImpl(MethodImplOptions.NoInlining)] - static int cinc_float(float op1) + public static void cinv_long_min_max(long op1, long expected) + { + //ARM64-FULL-LINE: cmp {{x[0-9]+}}, #49 + //ARM64-FULL-LINE-NEXT: cinc {{x[0-9]+}}, {{x[0-9]+}}, {{ge|lt}} + long result = op1 < 49 ? long.MinValue : ~long.MinValue; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(80.0f, 6)] + [InlineData(30.0f, 5)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinc_float(float op1, int expected) { //ARM64-FULL-LINE: fcmp {{s[0-9]+}}, {{s[0-9]+}} //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} - return op1 > 50.0f ? 6 : 5; + int result = op1 > 50.0f ? 6 : 5; + Assert.Equal(expected, result); } - - [Fact] - public static int TestEntryPoint() + [Theory] + [InlineData(80.0, 5)] + [InlineData(30.0, 6)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinc_double(double op1, int expected) { - if (cinc_byte(72) != 6) - { - Console.WriteLine("ConditionalIncrementTest:cinc_byte() failed"); - return 101; - } - - if (cinc_byte(32) != 5) - { - Console.WriteLine("ConditionalIncrementTest:cinc_byte() failed"); - return 101; - } - - if (cinc_byte_min_max(72) != byte.MinValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_byte_min_max() failed"); - return 101; - } - - if (cinc_byte_min_max(32) != byte.MaxValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_byte_min_max() failed"); - return 101; - } - - if (cinc_short(34) != 6) - { - Console.WriteLine("ConditionalIncrementTest:cinc_short() failed"); - return 101; - } - - if (cinc_short(74) != 5) - { - Console.WriteLine("ConditionalIncrementTest:cinc_short() failed"); - return 101; - } - - if (cinc_short_min_max(75) != short.MinValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_short_min_max() failed"); - return 101; - } - - if (cinc_short_min_max(-35) != short.MaxValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_short_min_max() failed"); - return 101; - } - - if (cinc_int(76) != 6) - { - Console.WriteLine("ConditionalIncrementTest:cinc_int() failed"); - return 101; - } - - if (cinc_int(36) != 5) - { - Console.WriteLine("ConditionalIncrementTest:cinc_int() failed"); - return 101; - } - - if (cinc_int_min_max(77) != int.MinValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_int_min_max() failed"); - return 101; - } - - if (cinc_int_min_max(37) != int.MaxValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_int_min_max() failed"); - return 101; - } - - if (cinc_long(78) != 5) - { - Console.WriteLine("ConditionalIncrementTest:cinc_long() failed"); - return 101; - } - - if (cinc_long(38) != 6) - { - Console.WriteLine("ConditionalIncrementTest:cinc_long() failed"); - return 101; - } - - if (cinc_long_min_max(79) != long.MaxValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_long_min_max() failed"); - return 101; - } - - if (cinc_long_min_max(39) != long.MinValue) - { - Console.WriteLine("ConditionalIncrementTest:cinc_long_min_max() failed"); - return 101; - } - - if (cinc_float(80.0f) != 6) - { - Console.WriteLine("ConditionalIncrementTest:cinc_float() failed"); - return 101; - } - - if (cinc_float(30.0f) != 5) - { - Console.WriteLine("ConditionalIncrementTest:cinc_float() failed"); - return 101; - } - - Console.WriteLine("PASSED"); - return 100; + //ARM64-FULL-LINE: fcmp {{d[0-9]+}}, {{d[0-9]+}} + //ARM64-FULL-LINE-NEXT: cinc {{w[0-9]+}}, {{w[0-9]+}}, {{hs|lo}} + int result = op1 < 51.0 ? 6 : 5; + Assert.Equal(expected, result); } } diff --git a/src/tests/JIT/opt/Compares/conditionalInverts.cs b/src/tests/JIT/opt/Compares/conditionalInverts.cs new file mode 100644 index 00000000000000..e99ddabefee785 --- /dev/null +++ b/src/tests/JIT/opt/Compares/conditionalInverts.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// unit test for the full range comparison optimization + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class ConditionalInvertTest +{ + + [Theory] + [InlineData(72, 13, 13)] + [InlineData(32, 13, 223)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_byte(byte op1, byte op2, byte expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #42 + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + byte result = (byte) (op1 > 42 ? op2: ~op1); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(34, 13, ~13)] + [InlineData(74, 13, 74)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_short(short op1, short op2, short expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #43 + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + short result = (short) (op1 <= 43 ? ~op2 : op1); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(75, ~short.MaxValue)] + [InlineData(-35, short.MaxValue)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_short_min_max(short op1, short expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #44 + //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + short result = (short) (op1 > 44 ? ~short.MaxValue : short.MaxValue); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(76, 17, 17)] + [InlineData(36, 17, ~36)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_int(int op1, int op2, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #45 + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 45 ? op2 : ~op1; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(77, int.MaxValue)] + [InlineData(37, ~int.MaxValue)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_int_min_max(int op1, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #46 + //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{ge|lt}} + int result = op1 >= 46 ? int.MaxValue : ~int.MaxValue; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(78, 23, 78)] + [InlineData(38, 23, ~23)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_long(long op1, long op2, long expected) + { + //ARM64-FULL-LINE: cmp {{x[0-9]+}}, #47 + //ARM64-FULL-LINE-NEXT: csinv {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, {{ge|lt}} + long result = op1 < 47 ? ~op2 : op1; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(80.0f, 29, 29)] + [InlineData(30.0f, 29, ~29)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_float(float op1, int op2, int expected) + { + //ARM64-FULL-LINE: fcmp {{s[0-9]+}}, {{s[0-9]+}} + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 48.0f ? op2 : ~op2; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(60.0, 31, ~31)] + [InlineData(30.0, 31, 31)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_double(double op1, int op2, int expected) + { + //ARM64-FULL-LINE: fcmp {{d[0-9]+}}, {{d[0-9]+}} + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 49.0 ? ~op2 : op2; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(81, 21, 21)] + [InlineData(31, 17, -8)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_int_shifted_false_opr(int op1, int op2, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #50 + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 50 ? op2 : ~(op1 >> 2); + Assert.Equal(expected, result); + } + + + [Theory] + [InlineData(81, 21, -21)] + [InlineData(31, 17, 17)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cinv_int_shifted_true_opr(int op1, int op2, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #51 + //ARM64-FULL-LINE-NEXT: csinv {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 51 ? ~(op1 >> 2) : op2; + Assert.Equal(expected, result); + } +} diff --git a/src/tests/JIT/opt/Compares/conditionalInverts.csproj b/src/tests/JIT/opt/Compares/conditionalInverts.csproj new file mode 100644 index 00000000000000..dbc3ab7f2f9596 --- /dev/null +++ b/src/tests/JIT/opt/Compares/conditionalInverts.csproj @@ -0,0 +1,18 @@ + + + + true + + + None + True + + + + true + + + + + + diff --git a/src/tests/JIT/opt/Compares/conditionalNegates.cs b/src/tests/JIT/opt/Compares/conditionalNegates.cs new file mode 100644 index 00000000000000..8366f1b9b9db6f --- /dev/null +++ b/src/tests/JIT/opt/Compares/conditionalNegates.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// unit test for the full range comparison optimization + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class ConditionalNegateTest +{ + + [Theory] + [InlineData(72, 13, 13)] + [InlineData(32, 13, 224)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_byte(byte op1, byte op2, byte expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #42 + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + byte result = (byte) (op1 > 42 ? op2: -op1); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(74, 13, 74)] + [InlineData(34, 13, -13)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_short(short op1, short op2, short expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #43 + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + short result = (short) (op1 <= 43 ? -op2 : op1); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(75, -short.MaxValue)] + [InlineData(-35, short.MaxValue)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_short_min_max(short op1, short expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #44 + //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + short result = (short) (op1 > 44 ? -short.MaxValue : short.MaxValue); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(76, 17, 17)] + [InlineData(36, 17, -36)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_int(int op1, int op2, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #45 + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 45 ? op2 : -op1; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(77, int.MaxValue)] + [InlineData(37, -int.MaxValue)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_int_min_max(int op1, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #46 + //ARM64-FULL-LINE-NEXT: csel {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{ge|lt}} + int result = op1 >= 46 ? int.MaxValue : -int.MaxValue; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(78, 23, 78)] + [InlineData(38, 23, -23)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_long(long op1, long op2, long expected) + { + //ARM64-FULL-LINE: cmp {{x[0-9]+}}, #47 + //ARM64-FULL-LINE-NEXT: csneg {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, {{ge|lt}} + long result = op1 < 47 ? -op2 : op1; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(80.0f, 29, 29)] + [InlineData(30.0f, 29, -29)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_float(float op1, int op2, int expected) + { + //ARM64-FULL-LINE: fcmp {{s[0-9]+}}, {{s[0-9]+}} + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 48.0f ? op2 : -op2; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(60.0, 31, -31)] + [InlineData(30.0, 31, 31)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_double(double op1, int op2, int expected) + { + //ARM64-FULL-LINE: fcmp {{d[0-9]+}}, {{d[0-9]+}} + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 49.0 ? -op2 : op2; + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(81, 21, 21)] + [InlineData(31, 21, -62)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_shifted_false_oper(int op1, int op2, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #50 + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{gt|le}} + int result = op1 > 50 ? op2 : -(op1 << 1); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(82, 22, 22)] + [InlineData(32, 22, -4)] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void cneg_shifted_true_oper(int op1, int op2, int expected) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #51 + //ARM64-FULL-LINE-NEXT: csneg {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, {{ge|lt}} + int result = op1 < 51 ? -(op1 >> 3) : op2; + Assert.Equal(expected, result); + } +} diff --git a/src/tests/JIT/opt/Compares/conditionalNegates.csproj b/src/tests/JIT/opt/Compares/conditionalNegates.csproj new file mode 100644 index 00000000000000..dbc3ab7f2f9596 --- /dev/null +++ b/src/tests/JIT/opt/Compares/conditionalNegates.csproj @@ -0,0 +1,18 @@ + + + + true + + + None + True + + + + true + + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index b7fc2b47ac67df..9c2871386d86e4 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1087,9 +1087,6 @@ https://github.com/dotnet/runtime/issues/83051 - - https://github.com/dotnet/runtime/issues/83051 - https://github.com/dotnet/runtime/issues/83051 @@ -3229,6 +3226,13 @@ https://github.com/dotnet/runtime/issues/41472 + + https://github.com/dotnet/runtime/issues/41472 + + + + https://github.com/dotnet/runtime/issues/86772 + needs triage diff --git a/src/tests/tracing/eventpipe/config/name_config_with_pid.cs b/src/tests/tracing/eventpipe/config/name_config_with_pid.cs index 779d945bd86a4f..edd89453074e35 100644 --- a/src/tests/tracing/eventpipe/config/name_config_with_pid.cs +++ b/src/tests/tracing/eventpipe/config/name_config_with_pid.cs @@ -9,6 +9,7 @@ class NameConfigWithPid { + private const string WaitForInput = "waitforinput"; static int Main(string[] args) { if (args.Length == 0) @@ -16,7 +17,7 @@ static int Main(string[] args) else Console.WriteLine($"args[0] = `{args[0]}`"); - if (args.Length > 0 && args[0] == "waitforinput") + if (args.Length > 0 && args[0] == WaitForInput) { Console.Error.WriteLine("WaitingForInput in ErrorStream"); Console.WriteLine("WaitingForInput"); @@ -39,17 +40,16 @@ static int Main(string[] args) } string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT"); - string corerun = Path.Combine(coreRoot, "corerun"); - if (OperatingSystem.IsWindows()) - corerun = corerun + ".exe"; - // Use dll directory as temp directory - string tempDir = Path.GetDirectoryName(typeof(NameConfigWithPid).Assembly.Location); + // Use app directory as temp directory + string tempDir = AppContext.BaseDirectory; string outputPathBaseName = $"eventPipeStream{Thread.CurrentThread.ManagedThreadId}_{(ulong)Stopwatch.GetTimestamp()}"; string outputPathPattern = Path.Combine(tempDir, outputPathBaseName + "_{pid}_{pid}.nettrace"); - process.StartInfo.FileName = corerun; - process.StartInfo.Arguments = typeof(NameConfigWithPid).Assembly.Location + " waitforinput"; + process.StartInfo.FileName = Process.GetCurrentProcess().MainModule.FileName; + process.StartInfo.Arguments = TestLibrary.Utilities.IsNativeAot + ? WaitForInput + : $"{typeof(NameConfigWithPid).Assembly.Location} {WaitForInput}"; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardError = true; @@ -63,7 +63,7 @@ static int Main(string[] args) Console.Out.Flush(); process.Start(); - string readFromTargetProcess = process.StandardError.ReadLine(); + string readFromTargetProcess = process.StandardError.ReadLine(); Console.WriteLine($"Readline '{readFromTargetProcess}'"); if (readFromTargetProcess != "WaitingForInput in ErrorStream") { @@ -101,7 +101,7 @@ static int Main(string[] args) Thread.Sleep(1000); } Console.WriteLine($"Unable to delete {expectedPath}"); - return 2; + return 2; } } } diff --git a/src/tests/tracing/eventpipe/config/name_config_with_pid.csproj b/src/tests/tracing/eventpipe/config/name_config_with_pid.csproj index 6089c06c5b0f2c..187b43060edfb8 100644 --- a/src/tests/tracing/eventpipe/config/name_config_with_pid.csproj +++ b/src/tests/tracing/eventpipe/config/name_config_with_pid.csproj @@ -5,8 +5,10 @@ true true + true +