diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml index 3c37878f3891..0bab9b25cd33 100644 --- a/eng/pipelines/common/xplat-setup.yml +++ b/eng/pipelines/common/xplat-setup.yml @@ -121,7 +121,7 @@ jobs: # OSX Build Pool (we don't have on-prem OSX BuildPool ${{ if in(parameters.osGroup, 'OSX', 'MacCatalyst', 'iOS', 'iOSSimulator', 'tvOS', 'tvOSSimulator') }}: - vmImage: 'macOS-10.15' + vmImage: 'macOS-11' # Official Build Windows Pool ${{ if and(or(eq(parameters.osGroup, 'windows'), eq(parameters.hostedOs, 'windows')), ne(variables['System.TeamProject'], 'public')) }}: diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.wasm.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.wasm.cs index 3b1f0483d9b4..d5a7f025ff2d 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.wasm.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.wasm.cs @@ -92,7 +92,6 @@ public bool ContainsCodeOffset(uint idxTryLandingStart) } else { - tryRegionIdx = 0; bool shouldInvokeHandler = InternalCalls.RhpCallFilterFunclet(exception, ehClause._filterAddress, shadowStack); if (shouldInvokeHandler) { @@ -106,40 +105,22 @@ public bool ContainsCodeOffset(uint idxTryLandingStart) return false; } - private static void InvokeSecondPassWasm(uint idxStart, uint idxTryLandingStart, ref EHClauseIterator clauseIter, uint idxLimit, void* shadowStack) + private static void InvokeSecondPassWasm(uint idxStart, uint idxTryLandingStart /* we do dont have the PC, so use the start of the block */, ref EHClauseIterator clauseIter, uint idxLimit, void* shadowStack) { - uint lastTryStart = 0, lastTryEnd = 0; // Search the clauses for one that contains the current offset. RhEHClauseWasm ehClause = new RhEHClauseWasm(); for (uint curIdx = 0; clauseIter.Next(ref ehClause) && curIdx < idxLimit; curIdx++) { - // - // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where - // the previous dispatch left off. - // - if (idxStart != MaxTryRegionIdx) - { - if (curIdx <= idxStart) - { - lastTryStart = ehClause._tryStartOffset; - lastTryEnd = ehClause._tryEndOffset; - continue; - } - // Now, we continue skipping while the try region is identical to the one that invoked the - // previous dispatch. - if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) - continue; - - // We are done skipping. This is required to handle empty finally block markers that are used - // to separate runs of different try blocks with same native code offsets. - idxStart = MaxTryRegionIdx; + if (curIdx > idxStart) + { + break; // these blocks are after the catch } EHClauseIterator.RhEHClauseKindWasm clauseKind = ehClause._clauseKind; if ((clauseKind != EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_FAULT) - || !ehClause.TryStartsAt(idxTryLandingStart)) + || !ehClause.ContainsCodeOffset(idxTryLandingStart)) { continue; } diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs index 79e99061ea54..d5aa9c355045 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs @@ -32,6 +32,7 @@ public enum LocalVarKind private class ExceptionRegion { public ILExceptionRegion ILRegion; + public bool Marked; } DependencyNodeCore.DependencyList _dependencies = new DependencyNodeCore.DependencyList(); @@ -104,6 +105,7 @@ public enum ImportState : byte } private ExceptionRegion[] _exceptionRegions; + private ExceptionRegion[] _handlerRegionsForOffsetLookup; public ILImporter(LLVMCodegenCompilation compilation, MethodDesc method, MethodIL methodIL, string mangledName, bool isUnboxingThunk) { Module = LLVMCodegenCompilation.Module; @@ -136,6 +138,7 @@ public ILImporter(LLVMCodegenCompilation compilation, MethodDesc method, MethodI _thisType = method.OwningType; var ilExceptionRegions = methodIL.GetExceptionRegions(); _exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length]; + _handlerRegionsForOffsetLookup = new ExceptionRegion[ilExceptionRegions.Length]; if (ilExceptionRegions.Length != 0) { _exceptionFunclets = new List(_exceptionRegions.Length); @@ -147,12 +150,20 @@ public ILImporter(LLVMCodegenCompilation compilation, MethodDesc method, MethodI .ThenByDescending(region => region.TryLength) // outer regions with the same try offset as inner region first - they will have longer lengths, // WASMTODO, except maybe an inner of try {} catch {} which could still be a problem .ThenBy(region => region.HandlerOffset)) { - _exceptionRegions[curRegion++] = new ExceptionRegion + _handlerRegionsForOffsetLookup[curRegion++] = new ExceptionRegion { ILRegion = region }; } + for(curRegion = 0; curRegion < ilExceptionRegions.Length; curRegion++) + { + _exceptionRegions[curRegion] = new ExceptionRegion + { + ILRegion = ilExceptionRegions[curRegion] + }; + } + _llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature, method.RequiresInstArg()); _currentFunclet = _llvmFunction; _pointerSize = compilation.NodeFactory.Target.PointerSize; @@ -569,12 +580,12 @@ private ExceptionRegion GetCurrentTryRegion() private ExceptionRegion GetTryRegion(int offset) { // Iterate backwards to find the most nested region - for (int i = _exceptionRegions.Length - 1; i >= 0; i--) + for (int i = _handlerRegionsForOffsetLookup.Length - 1; i >= 0; i--) { - ILExceptionRegion region = _exceptionRegions[i].ILRegion; + ILExceptionRegion region = _handlerRegionsForOffsetLookup[i].ILRegion; if (IsOffsetContained(offset - 1, region.TryOffset, region.TryLength)) { - return _exceptionRegions[i]; + return _handlerRegionsForOffsetLookup[i]; } } @@ -588,13 +599,13 @@ private ExceptionRegion GetTryRegion(int offset) private ExceptionRegion GetHandlerRegion(int offset) { // Iterate backwards to find the most nested region - for (int i = _exceptionRegions.Length - 1; i >= 0; i--) + for (int i = _handlerRegionsForOffsetLookup.Length - 1; i >= 0; i--) { - ExceptionRegion exceptionRegion = _exceptionRegions[i]; + ExceptionRegion exceptionRegion = _handlerRegionsForOffsetLookup[i]; if (IsOffsetContained(offset, exceptionRegion.ILRegion.HandlerOffset, exceptionRegion.ILRegion.HandlerLength) || (exceptionRegion.ILRegion.Kind == ILExceptionRegionKind.Filter && IsOffsetContained(offset, exceptionRegion.ILRegion.FilterOffset, exceptionRegion.ILRegion.HandlerOffset - exceptionRegion.ILRegion.FilterOffset))) { - return _exceptionRegions[i]; + return _handlerRegionsForOffsetLookup[i]; } } @@ -640,7 +651,7 @@ private void StartImportingBasicBlock(BasicBlock basicBlock) if (basicBlock.HandlerStart || basicBlock.FilterStart) { _funcletAddrCacheCtx = null; - foreach (ExceptionRegion ehRegion in _exceptionRegions) + foreach (ExceptionRegion ehRegion in _handlerRegionsForOffsetLookup) { if (ehRegion.ILRegion.HandlerOffset == basicBlock.StartOffset || ehRegion.ILRegion.FilterOffset == basicBlock.StartOffset) @@ -657,7 +668,7 @@ private void StartImportingBasicBlock(BasicBlock basicBlock) if (basicBlock.TryStart) { - foreach (ExceptionRegion ehRegion in _exceptionRegions) + foreach (ExceptionRegion ehRegion in _handlerRegionsForOffsetLookup) { if(ehRegion.ILRegion.TryOffset == basicBlock.StartOffset) { @@ -2960,12 +2971,26 @@ private LLVMBasicBlockRef GetOrCreateLandingPad(ExceptionRegion tryRegion) var leaveDestination = landingPadBuilder.BuildAlloca(LLVMTypeRef.Int32, "leaveDest"); // create a variable to store the operand of the leave as we can't use the result of the call directly due to domination/branches landingPadBuilder.BuildStore(LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0, false), leaveDestination); + + // second pass - + // TODO-LLVM: note that we will call the finally blocks even if there is no catch, whereas really we should fail fast + // reinitialise the iterator + CallRuntime(_compilation.TypeSystemContext, "EHClauseIterator", "InitFromEhInfo", iteratorInitArgs, null, fromLandingPad: true, builder: landingPadBuilder); + + var secondPassArgs = new StackEntry[] { new ExpressionEntry(StackValueKind.Int32, "idxStart", landingPadBuilder.BuildLoad(tryRegionIdx)), + new ExpressionEntry(StackValueKind.Int32, "idxTryLandingStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)tryRegion.ILRegion.TryOffset, false)), + new ExpressionEntry(StackValueKind.ByRef, "refFrameIter", ehInfoIterator), + new ExpressionEntry(StackValueKind.Int32, "idxLimit", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)), + new ExpressionEntry(StackValueKind.NativeInt, "shadowStack", _currentFunclet.GetParam(0)) + }; + CallRuntime(_compilation.TypeSystemContext, "EH", "InvokeSecondPassWasm", secondPassArgs, null, true, builder: landingPadBuilder); + var foundCatchBlock = _currentFunclet.AppendBasicBlock("LPFoundCatch"); // If it didn't find a catch block, we can rethrow (resume in LLVM) the C++ exception to continue the stack walk. var noCatch = landingPadBuilder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0, false), handler.ValueAsInt32(landingPadBuilder, false), "testCatch"); - var secondPassBlock = _currentFunclet.AppendBasicBlock("SecondPass"); - landingPadBuilder.BuildCondBr(noCatch, secondPassBlock, foundCatchBlock); + var resumeBlock = GetOrCreateResumeBlock(pad, tryRegion.ILRegion.TryOffset.ToString()); + landingPadBuilder.BuildCondBr(noCatch, resumeBlock, foundCatchBlock); landingPadBuilder.PositionAtEnd(foundCatchBlock); // finished with the c++ exception @@ -2981,23 +3006,9 @@ private LLVMBasicBlockRef GetOrCreateLandingPad(ExceptionRegion tryRegion) LLVMValueRef leaveReturnValue = landingPadBuilder.BuildCall(RhpCallCatchFunclet, callCatchArgs, ""); landingPadBuilder.BuildStore(leaveReturnValue, leaveDestination); - landingPadBuilder.BuildBr(secondPassBlock); - - landingPadBuilder.PositionAtEnd(secondPassBlock); - - // reinitialise the iterator - CallRuntime(_compilation.TypeSystemContext, "EHClauseIterator", "InitFromEhInfo", iteratorInitArgs, null, fromLandingPad: true, builder: landingPadBuilder); - - var secondPassArgs = new StackEntry[] { new ExpressionEntry(StackValueKind.Int32, "idxStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)), - new ExpressionEntry(StackValueKind.Int32, "idxTryLandingStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)tryRegion.ILRegion.TryOffset, false)), - new ExpressionEntry(StackValueKind.ByRef, "refFrameIter", ehInfoIterator), - new ExpressionEntry(StackValueKind.Int32, "idxLimit", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)), - new ExpressionEntry(StackValueKind.NativeInt, "shadowStack", _currentFunclet.GetParam(0)) - }; - CallRuntime(_compilation.TypeSystemContext, "EH", "InvokeSecondPassWasm", secondPassArgs, null, true, builder: landingPadBuilder); var catchLeaveBlock = _currentFunclet.AppendBasicBlock("CatchLeave"); - landingPadBuilder.BuildCondBr(noCatch, GetOrCreateResumeBlock(pad, tryRegion.ILRegion.TryOffset.ToString()), catchLeaveBlock); + landingPadBuilder.BuildCondBr(noCatch, resumeBlock, catchLeaveBlock); landingPadBuilder.PositionAtEnd(catchLeaveBlock); // Use the else as the path for no exception handler found for this exception @@ -4435,9 +4446,9 @@ void ThrowOrRethrow(StackEntry exceptionObject) _builder.BuildInvoke(RhpThrowEx, args, GetOrCreateUnreachableBlock(), GetOrCreateLandingPad(currentExceptionRegion), ""); } - for (int i = 0; i < _exceptionRegions.Length; i++) + for (int i = 0; i < _handlerRegionsForOffsetLookup.Length; i++) { - var r = _exceptionRegions[i]; + var r = _handlerRegionsForOffsetLookup[i]; if (IsOffsetContained(_currentOffset - 1, r.ILRegion.TryOffset, r.ILRegion.TryLength)) { @@ -4943,9 +4954,9 @@ private void ImportBox(int token) private void ImportLeave(BasicBlock target) { - for (int i = _exceptionRegions.Length - 1; i >= 0; i--) + for (int i = _handlerRegionsForOffsetLookup.Length - 1; i >= 0; i--) { - var r = _exceptionRegions[i]; + var r = _handlerRegionsForOffsetLookup[i]; if (r.ILRegion.Kind == ILExceptionRegionKind.Finally && IsOffsetContained(_currentOffset - 1, r.ILRegion.TryOffset, r.ILRegion.TryLength) && @@ -5382,35 +5393,21 @@ private ObjectNode.ObjectData EncodeEHInfo() builder.RequireInitialAlignment(1); int totalClauses = _exceptionRegions.Length; - // Count the number of special markers that will be needed -// for (int i = 1; i < _exceptionRegions.Length; i++) -// { -// ExceptionRegion clause = _exceptionRegions[i]; -// ExceptionRegion previousClause = _exceptionRegions[i - 1]; - - // WASMTODO : do we need these special markers and if so how do we detect and set CORINFO_EH_CLAUSE_SAMETRY? -// if ((previousClause.ILRegion.TryOffset == clause.ILRegion.TryOffset) && -// (previousClause.ILRegion.TryLength == clause.ILRegion.TryLength) && -// ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_SAMETRY) == 0)) -// { -// totalClauses++; -// } -// } - builder.EmitCompressedUInt((uint)totalClauses); - // Iterate backwards to emit the innermost first, but within a try region go forwards to get the first matching catch type - int i = _exceptionRegions.Length - 1; - while (i >= 0) + + // Iterate forwards to emit the innermost first (as they appear in the IL), but within a try region go forwards to get the first matching catch type + for(int i = 0; i < _exceptionRegions.Length; i++) { - int tryStart = _exceptionRegions[i].ILRegion.TryOffset; - int tryLength = _exceptionRegions[i].ILRegion.TryLength; + ExceptionRegion tryRegion = _exceptionRegions[i]; + + if (tryRegion.Marked) continue; + + int tryStart = tryRegion.ILRegion.TryOffset; + int tryLength = tryRegion.ILRegion.TryLength; for (var j = 0; j < _exceptionRegions.Length; j++) { ExceptionRegion exceptionRegion = _exceptionRegions[j]; if (exceptionRegion.ILRegion.TryOffset != tryStart || exceptionRegion.ILRegion.TryLength != tryLength) continue; - // if (i > 0) - // { - // ExceptionRegion previousClause = _exceptionRegions[i - 1]; // If the previous clause has same try offset and length as the current clause, // but belongs to a different try block (CORINFO_EH_CLAUSE_SAMETRY is not set), @@ -5476,11 +5473,19 @@ private ObjectNode.ObjectData EncodeEHInfo() builder.EmitReloc(new LLVMBlockRefNode(filterFuncletName), rel); break; } - i--; + + exceptionRegion.Marked = true; } } - return builder.ToObjectData(); +#if DEBUG + for (int i = 0; i < _exceptionRegions.Length; i++) + { + Debug.Assert(_exceptionRegions[i].Marked); + } +#endif + + return builder.ToObjectData(); } private string GetFuncletName(ExceptionRegion exceptionRegion, int regionOffset, ILExceptionRegionKind ilExceptionRegionKind) diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs index 46d66aed0eff..97bc4d3e3fc0 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs @@ -1364,6 +1364,7 @@ public ILImporter(LLVMBuilderRef builder, LLVMCodegenCompilation compilation, LL _thisType = GetWellKnownType(WellKnownType.Void); _pointerSize = compilation.NodeFactory.Target.PointerSize; _exceptionRegions = new ExceptionRegion[0]; + _handlerRegionsForOffsetLookup = new ExceptionRegion[0]; } internal void OutputCodeForTriggerCctor(TypeDesc type, LLVMValueRef staticBaseValueRef) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/configure.cmake b/src/libraries/Native/Unix/System.Globalization.Native/configure.cmake index fc00d106185b..adf3f49afbd6 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/configure.cmake +++ b/src/libraries/Native/Unix/System.Globalization.Native/configure.cmake @@ -20,6 +20,11 @@ else() "unicode/ucol.h" HAVE_SET_MAX_VARIABLE) + check_symbol_exists( + ucol_clone + "unicode/ucol.h" + HAVE_UCOL_CLONE) + unset(CMAKE_REQUIRED_LIBRARIES) unset(CMAKE_REQUIRED_INCLUDES) endif() diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c index f0273afac9fa..f0cfcdacc1b3 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c @@ -321,7 +321,24 @@ static UCollator* CloneCollatorWithOptions(const UCollator* pCollator, int32_t o if (customRuleLength == 0) { +#if !defined(STATIC_ICU) + if (ucol_clone_ptr != NULL) + { + pClonedCollator = ucol_clone(pCollator, pErr); + } + else + { + pClonedCollator = ucol_safeClone_ptr(pCollator, NULL, NULL, pErr); + } +#else // !defined(STATIC_ICU) + +#if HAVE_UCOL_CLONE + pClonedCollator = ucol_clone(pCollator, pErr); +#else pClonedCollator = ucol_safeClone(pCollator, NULL, NULL, pErr); +#endif // HAVE_UCOL_CLONE + +#endif // !defined(STATIC_ICU) } else { diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c index 70e8f0944993..234517d8c1f0 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c @@ -29,7 +29,6 @@ FOR_ALL_ICU_FUNCTIONS #define SYMBOL_NAME_SIZE (128 + SYMBOL_CUSTOM_SUFFIX_SIZE) #define MaxICUVersionStringWithSuffixLength (MaxICUVersionStringLength + SYMBOL_CUSTOM_SUFFIX_SIZE) - #if defined(TARGET_WINDOWS) || defined(TARGET_OSX) || defined(TARGET_ANDROID) #define MaxICUVersionStringLength 33 @@ -39,6 +38,7 @@ FOR_ALL_ICU_FUNCTIONS static void* libicuuc = NULL; static void* libicui18n = NULL; ucol_setVariableTop_func ucol_setVariableTop_ptr = NULL; +ucol_safeClone_func ucol_safeClone_ptr = NULL; #if defined (TARGET_UNIX) @@ -381,6 +381,30 @@ static void ValidateICUDataCanLoad() } } +static void InitializeUColClonePointers(char* symbolVersion) +{ + if (ucol_clone_ptr != NULL) + { + return; + } + +#if defined(TARGET_WINDOWS) + char symbolName[SYMBOL_NAME_SIZE]; + sprintf_s(symbolName, SYMBOL_NAME_SIZE, "ucol_safeClone%s", symbolVersion); + ucol_safeClone_ptr = (ucol_safeClone_func)GetProcAddress((HMODULE)libicui18n, symbolName); +#else + char symbolName[SYMBOL_NAME_SIZE]; + sprintf(symbolName, "ucol_safeClone%s", symbolVersion); + ucol_safeClone_ptr = (ucol_safeClone_func)dlsym(libicui18n, symbolName); +#endif // defined(TARGET_WINDOWS) + + if (ucol_safeClone_ptr == NULL) + { + fprintf(stderr, "Cannot get the symbols of ICU APIs ucol_safeClone or ucol_clone.\n"); + abort(); + } +} + static void InitializeVariableMaxAndTopPointers(char* symbolVersion) { if (ucol_setMaxVariable_ptr != NULL) @@ -444,6 +468,7 @@ int32_t GlobalizationNative_LoadICU() ValidateICUDataCanLoad(); InitializeVariableMaxAndTopPointers(symbolVersion); + InitializeUColClonePointers(symbolVersion); return true; } diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h index e752c7d006d5..22f870cf5bfc 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h @@ -66,7 +66,29 @@ extern ucol_setVariableTop_func ucol_setVariableTop_ptr; U_CAPI void U_EXPORT2 ucol_setMaxVariable(UCollator* coll, UColReorderCode group, UErrorCode* pErrorCode); U_CAPI int32_t U_EXPORT2 ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* region, UChar* id, int32_t idCapacity, UErrorCode* status); U_CAPI int32_t U_EXPORT2 ucal_getWindowsTimeZoneID(const UChar* id, int32_t len, UChar* winid, int32_t winidCapacity, UErrorCode* status); -#endif + +// (U_ICU_VERSION_MAJOR_NUM < 71) +// The following API is not supported in the ICU versions less than 71. We need to define it manually. +// We have to do runtime check before using the pointers to this API. That is why these are listed in the FOR_ALL_OPTIONAL_ICU_FUNCTIONS list. +U_CAPI UCollator* U_EXPORT2 ucol_clone(const UCollator* coll, UErrorCode* status); + +// ucol_setVariableTop is a deprecated function on the newer ICU versions and ucol_setMaxVariable should be used instead. +// As can run against ICU versions which not supported ucol_setMaxVariable, we'll dynamically try to get the pointer to ucol_setVariableTop +// when we couldn't get a pointer to ucol_setMaxVariable. +typedef uint32_t (U_EXPORT2 *ucol_setVariableTop_func)(UCollator* coll, const UChar* varTop, int32_t len, UErrorCode* status); + +// ucol_safeClone is deprecated in ICU version 71. We have to handle it manually to avoid getting a build break when referencing it in the code. +typedef UCollator* (U_EXPORT2 *ucol_safeClone_func)(const UCollator* coll, void* stackBuffer, int32_t* pBufferSize, UErrorCode* status); + +#else // !defined(TARGET_ANDROID) + +typedef uint32_t (*ucol_setVariableTop_func)(UCollator* coll, const UChar* varTop, int32_t len, UErrorCode* status); +typedef UCollator* (*ucol_safeClone_func)(const UCollator* coll, void* stackBuffer, int32_t* pBufferSize, UErrorCode* status); + +#endif // !defined(TARGET_ANDROID) + +extern ucol_setVariableTop_func ucol_setVariableTop_ptr; +extern ucol_safeClone_func ucol_safeClone_ptr; // List of all functions from the ICU libraries that are used in the System.Globalization.Native.so #define FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \ @@ -105,7 +127,6 @@ U_CAPI int32_t U_EXPORT2 ucal_getWindowsTimeZoneID(const UChar* id, int32_t len, PER_FUNCTION_BLOCK(ucol_open, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_openElements, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_openRules, libicui18n, true) \ - PER_FUNCTION_BLOCK(ucol_safeClone, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_setAttribute, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_strcoll, libicui18n, true) \ PER_FUNCTION_BLOCK(udat_close, libicui18n, true) \ @@ -193,7 +214,8 @@ U_CAPI int32_t U_EXPORT2 ucal_getWindowsTimeZoneID(const UChar* id, int32_t len, #define FOR_ALL_OPTIONAL_ICU_FUNCTIONS \ PER_FUNCTION_BLOCK(ucal_getWindowsTimeZoneID, libicui18n, false) \ PER_FUNCTION_BLOCK(ucal_getTimeZoneIDForWindowsID, libicui18n, false) \ - PER_FUNCTION_BLOCK(ucol_setMaxVariable, libicui18n, false) + PER_FUNCTION_BLOCK(ucol_setMaxVariable, libicui18n, false) \ + PER_FUNCTION_BLOCK(ucol_clone, libicui18n, false) #define FOR_ALL_ICU_FUNCTIONS \ FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \ @@ -232,6 +254,7 @@ FOR_ALL_ICU_FUNCTIONS #define ucal_openTimeZoneIDEnumeration(...) ucal_openTimeZoneIDEnumeration_ptr(__VA_ARGS__) #define ucal_set(...) ucal_set_ptr(__VA_ARGS__) #define ucal_setMillis(...) ucal_setMillis_ptr(__VA_ARGS__) +#define ucol_clone(...) ucol_clone_ptr(__VA_ARGS__) #define ucol_close(...) ucol_close_ptr(__VA_ARGS__) #define ucol_closeElements(...) ucol_closeElements_ptr(__VA_ARGS__) #define ucol_getOffset(...) ucol_getOffset_ptr(__VA_ARGS__) @@ -244,7 +267,6 @@ FOR_ALL_ICU_FUNCTIONS #define ucol_open(...) ucol_open_ptr(__VA_ARGS__) #define ucol_openElements(...) ucol_openElements_ptr(__VA_ARGS__) #define ucol_openRules(...) ucol_openRules_ptr(__VA_ARGS__) -#define ucol_safeClone(...) ucol_safeClone_ptr(__VA_ARGS__) #define ucol_setAttribute(...) ucol_setAttribute_ptr(__VA_ARGS__) #define ucol_setMaxVariable(...) ucol_setMaxVariable_ptr(__VA_ARGS__) #define ucol_strcoll(...) ucol_strcoll_ptr(__VA_ARGS__) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h index 1125ce91c3f8..424f72597dc7 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h @@ -470,7 +470,7 @@ int32_t ucol_previous(UCollationElements * elems, UErrorCode * status); UCollator * ucol_open(const char * loc, UErrorCode * status); UCollationElements * ucol_openElements(const UCollator * coll, const UChar * text, int32_t textLength, UErrorCode * status); UCollator * ucol_openRules(const UChar * rules, int32_t rulesLength, UColAttributeValue normalizationMode, UCollationStrength strength, UParseError * parseError, UErrorCode * status); -UCollator * ucol_safeClone(const UCollator * coll, void * stackBuffer, int32_t * pBufferSize, UErrorCode * status); +UCollator * ucol_clone(const UCollator * coll, UErrorCode * status); void ucol_setAttribute(UCollator * coll, UColAttribute attr, UColAttributeValue value, UErrorCode * status); UCollationResult ucol_strcoll(const UCollator * coll, const UChar * source, int32_t sourceLength, const UChar * target, int32_t targetLength); int32_t ucurr_forLocale(const char * locale, UChar * buff, int32_t buffCapacity, UErrorCode * ec); diff --git a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs index 93d6248aef18..2ec74d52e0cb 100644 --- a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs +++ b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs @@ -1825,6 +1825,10 @@ private static void TestTryCatch() TestTryFinallyThrowException(); + TestTryFinallyCatchException(); + + TestInnerTryFinallyOrder(); + TestTryCatchWithCallInIf(); TestThrowInCatch(); @@ -1876,10 +1880,10 @@ private static void TestTryCatchThrowException(Exception e) EndTest(caught); } - static bool finallyCalled; + static string clauseExceution; private static void TestTryFinallyThrowException() { - finallyCalled = false; + clauseExceution = ""; StartTest("Try/Finally calls finally when exception thrown test"); try { @@ -1887,9 +1891,17 @@ private static void TestTryFinallyThrowException() } catch (Exception) { + clauseExceution += "COuter"; + } + if (clauseExceution != "CInnerFCOuter") + { + FailTest("Expected CInnerFCOuter, but was " + clauseExceution); + } + else + { + PassTest(); } - EndTest(finallyCalled); } private static void TryFinally() @@ -1898,9 +1910,95 @@ private static void TryFinally() { throw new Exception(); } + catch + { + clauseExceution += "CInner"; + throw; + } finally { - finallyCalled = true; + clauseExceution += "F"; + } + } + + + private static void TestTryFinallyCatchException() + { + clauseExceution = ""; + StartTest("Try/Finally calls finally once when exception thrown and caught test"); + + TryFinallyWithCatch(); + + if (clauseExceution != "CF") + { + FailTest("Expected CF, but was " + clauseExceution); + } + else + { + PassTest(); + } + } + + private static void TryFinallyWithCatch() + { + try + { + throw new Exception(); + } + catch + { + clauseExceution += "C"; + } + finally + { + clauseExceution += "F"; + } + } + + private static void TestInnerTryFinallyOrder() + { + clauseExceution = ""; + StartTest("Inner try finally called before outer catch"); + + try + { + try + { + try + { + throw new Exception(); + } + finally + { + clauseExceution += "F1"; + } + } + finally + { + clauseExceution += "F2"; + } + + // not reached + try + { + } + finally + { + clauseExceution += "F3"; + } + } + catch + { + clauseExceution += "C"; + } + + if (clauseExceution != "F1F2C") + { + FailTest("Expected F1F2C, but was " + clauseExceution); + } + else + { + PassTest(); } }