@@ -1106,72 +1106,152 @@ jobjectArray CallbackHandlers::GetJavaStringArray(JEnv &env, int length) {
11061106 return (jobjectArray) env.NewGlobalRef (tmpArr);
11071107}
11081108
1109+ namespace {
1110+
1111+ const int kDefaultWorkerPriority = 10 ; // android.os.Process.THREAD_PRIORITY_BACKGROUND
1112+
1113+ const char *const kWorkerPriorityNames =
1114+ " 'lowest', 'background', 'lessFavorable', 'default', 'moreFavorable', "
1115+ " 'foreground', 'display', 'urgentDisplay', 'video', 'audio', 'urgentAudio' "
1116+ " or a number between -20 and 19" ;
1117+
1118+ // The android.os.Process THREAD_PRIORITY_* nice values, under their camelCase
1119+ // names; anything else is a caller error.
1120+ bool MapWorkerPriorityName (const std::string &name, int &priority) {
1121+ if (name == " lowest" ) {
1122+ priority = 19 ;
1123+ } else if (name == " background" ) {
1124+ priority = 10 ;
1125+ } else if (name == " lessFavorable" ) {
1126+ priority = 1 ;
1127+ } else if (name == " default" ) {
1128+ priority = 0 ;
1129+ } else if (name == " moreFavorable" ) {
1130+ priority = -1 ;
1131+ } else if (name == " foreground" ) {
1132+ priority = -2 ;
1133+ } else if (name == " display" ) {
1134+ priority = -4 ;
1135+ } else if (name == " urgentDisplay" ) {
1136+ priority = -8 ;
1137+ } else if (name == " video" ) {
1138+ priority = -10 ;
1139+ } else if (name == " audio" ) {
1140+ priority = -16 ;
1141+ } else if (name == " urgentAudio" ) {
1142+ priority = -19 ;
1143+ } else {
1144+ return false ;
1145+ }
1146+ return true ;
1147+ }
1148+
1149+ // Nice values outside the kernel's range are clamped rather than rejected:
1150+ // a caller asking for "as low as possible" gets it.
1151+ int ClampWorkerPriority (Local<Context> context, Local<Value> value) {
1152+ int priority = value->Int32Value (context).FromMaybe (kDefaultWorkerPriority );
1153+ if (priority < -20 ) {
1154+ return -20 ;
1155+ }
1156+ if (priority > 19 ) {
1157+ return 19 ;
1158+ }
1159+ return priority;
1160+ }
1161+
1162+ // Carries a real TypeError instance so `catch (e) { e instanceof TypeError }`
1163+ // holds in JS; NewThreadCallback's catch block rethrows it unchanged.
1164+ [[noreturn]] void ThrowWorkerOptionTypeError (Isolate *isolate, const std::string &message) {
1165+ Local<Value> error = Exception::TypeError (ArgConverter::ConvertToV8String (isolate, message));
1166+ throw NativeScriptException (isolate, error, message);
1167+ }
1168+
1169+ // Reads `key` from `object`. A false return means the getter threw: the
1170+ // exception is already pending on the isolate and construction must stop
1171+ // without running anything else on it.
1172+ bool ReadWorkerOption (Isolate *isolate, Local<Context> context, Local<Object> object,
1173+ const char *key, Local<Value> &out) {
1174+ return object->Get (context, ArgConverter::ConvertToV8String (isolate, key)).ToLocal (&out);
1175+ }
1176+
11091177/*
1110- * Resolves the `androidPriority` Worker option to an android.os.Process
1111- * thread priority (nice value). Accepts the THREAD_PRIORITY_* names in
1112- * camelCase or a raw nice value clamped to [-20, 19] .
1113- * Defaults to THREAD_PRIORITY_BACKGROUND (10), the previously hardcoded value .
1178+ * Resolves the Worker thread priority to an android.os.Process nice value from
1179+ * `android. priority`, falling back to the deprecated top-level
1180+ * `androidPriority`. Defaults to THREAD_PRIORITY_BACKGROUND (10) .
1181+ * Returns false when an option getter threw (see ReadWorkerOption) .
11141182 */
1115- static int GetWorkerThreadPriority (Isolate *isolate, Local<Context> context,
1116- const v8::FunctionCallbackInfo<v8::Value> &args) {
1117- const int defaultPriority = 10 ; // android.os.Process.THREAD_PRIORITY_BACKGROUND
1183+ bool GetWorkerThreadPriority (Isolate *isolate, Local<Context> context,
1184+ const v8::FunctionCallbackInfo<v8::Value> &args, int &priority ) {
1185+ priority = kDefaultWorkerPriority ;
11181186
11191187 if (args.Length () < 2 || !args[1 ]->IsObject ()) {
1120- return defaultPriority ;
1188+ return true ;
11211189 }
11221190
11231191 auto options = args[1 ].As <Object>();
1124- Local<Value> value ;
1125- if (!options-> Get (context, ArgConverter::ConvertToV8String (isolate, " androidPriority " ))
1126- . ToLocal (&value) ||
1127- value-> IsNullOrUndefined ( )) {
1128- return defaultPriority ;
1192+ bool resolved = false ;
1193+
1194+ Local<Value> androidVal;
1195+ if (! ReadWorkerOption (isolate, context, options, " android " , androidVal )) {
1196+ return false ;
11291197 }
1198+ if (!androidVal->IsNullOrUndefined ()) {
1199+ if (!androidVal->IsObject ()) {
1200+ ThrowWorkerOptionTypeError (isolate, " Worker option \" android\" must be an object." );
1201+ }
11301202
1131- if (value->IsNumber ()) {
1132- int priority = value->Int32Value (context).FromMaybe (defaultPriority);
1133- if (priority < -20 ) {
1134- priority = -20 ;
1135- } else if (priority > 19 ) {
1136- priority = 19 ;
1203+ Local<Value> priorityVal;
1204+ if (!ReadWorkerOption (isolate, context, androidVal.As <Object>(), " priority" , priorityVal)) {
1205+ return false ;
1206+ }
1207+ if (!priorityVal->IsUndefined ()) {
1208+ if (priorityVal->IsNumber ()) {
1209+ priority = ClampWorkerPriority (context, priorityVal);
1210+ } else if (!priorityVal->IsString () ||
1211+ !MapWorkerPriorityName (
1212+ ArgConverter::ConvertToString (priorityVal.As <String>()), priority)) {
1213+ ThrowWorkerOptionTypeError (
1214+ isolate, std::string (" Worker option \" android.priority\" must be one of " ) +
1215+ kWorkerPriorityNames + " ." );
1216+ }
1217+ resolved = true ;
11371218 }
1138- return priority;
11391219 }
11401220
1141- if (value-> IsString ()) {
1142- auto name = ArgConverter::ConvertToString (value. As <String>());
1143- if (name == " lowest " ) {
1144- return 19 ;
1145- } else if (name == " background " ) {
1146- return 10 ;
1147- } else if (name == " lessFavorable " ) {
1148- return 1 ;
1149- } else if (name == " default " ) {
1150- return 0 ;
1151- } else if (name == " moreFavorable " ) {
1152- return - 1 ;
1153- } else if (name == " foreground " ) {
1154- return - 2 ;
1155- } else if (name == " display " ) {
1156- return - 4 ;
1157- } else if (name == " urgentDisplay " ) {
1158- return - 8 ;
1159- } else if (name == " video " ) {
1160- return - 10 ;
1161- } else if (name == " audio " ) {
1162- return - 16 ;
1163- } else if (name == " urgentAudio " ) {
1164- return - 19 ;
1165- }
1221+ Local<Value> legacyVal;
1222+ if (! ReadWorkerOption (isolate, context, options, " androidPriority " , legacyVal)) {
1223+ return false ;
1224+ }
1225+ if (legacyVal-> IsNullOrUndefined () ) {
1226+ return true ;
1227+ }
1228+
1229+ static std::once_flag warnedDeprecated;
1230+ std::call_once (warnedDeprecated, []() {
1231+ DEBUG_WRITE_FORCE ( " NativeScript: the Worker option \" androidPriority \" is deprecated. "
1232+ " Use \" android \" : { \" priority \" : ... } instead. " ) ;
1233+ });
1234+
1235+ if (resolved ) {
1236+ return true ;
1237+ }
1238+
1239+ if (legacyVal-> IsNumber () ) {
1240+ priority = ClampWorkerPriority (context, legacyVal) ;
1241+ return true ;
1242+ }
1243+ if (legacyVal-> IsString () &&
1244+ MapWorkerPriorityName ( ArgConverter::ConvertToString (legacyVal. As <String>()), priority)) {
1245+ return true ;
11661246 }
11671247
11681248 throw NativeScriptException (
1169- " Invalid value for the Worker 'androidPriority' option. Expected one of: "
1170- " 'lowest', 'background', 'lessFavorable', 'default', 'moreFavorable', "
1171- " 'foreground', 'display', 'urgentDisplay', 'video', 'audio', 'urgentAudio' "
1172- " or a number between -20 and 19." );
1249+ std::string (" Invalid value for the Worker 'androidPriority' option. Expected one of: " ) +
1250+ kWorkerPriorityNames + " ." );
11731251}
11741252
1253+ } // namespace
1254+
11751255void CallbackHandlers::NewThreadCallback (const v8::FunctionCallbackInfo<v8::Value> &args) {
11761256 try {
11771257 if (!args.IsConstructCall ()) {
@@ -1226,7 +1306,10 @@ void CallbackHandlers::NewThreadCallback(const v8::FunctionCallbackInfo<v8::Valu
12261306 throw NativeScriptException (" Worker constructor expects a string URL or URL object." );
12271307 }
12281308
1229- int priority = GetWorkerThreadPriority (isolate, context, args);
1309+ int priority;
1310+ if (!GetWorkerThreadPriority (isolate, context, args, priority)) {
1311+ return ;
1312+ }
12301313
12311314 // An http(s) entry has no filesystem form to validate or to resolve
12321315 // against the caller's directory: it is already absolute, and the
0 commit comments